Polymorphism

Come gestire alternative basate sul tipo? Come creare componenti software inseribili?

  • Alternative basate sul tipo. Le variazioni condizionali sono un tema fondamentale nei programmi. Se un programma è progettato utilizzando la logica delle istruzioni condizionali if-then-else o switch-case, nel caso in cui si presenti una nuova variazione sarà necessario modificare la logica condizionale, spesso in diversi punti (in più metodi, o addirittura classi). Questo approccio rende difficile l'estensione di un programma con nuove variazioni.
  • Componenti software inseribili. Considerando i componenti in una relazione client-server, com'è possibile sostituire un componente server con un altro, senza ripercussioni sul client? Ovvero, come consentire a un componente client di fruire di un servizio, ma lasciando il componente client indipendente dall'implementazione del servizio?

La soluzione è il polimorfismo.

Corollario

Per eseguire alternative che variano in base al tipo, non testare il tipo di un oggetto (se un X è un TipoX allora) attraverso una logica condizionale.

Problema Monopoly: come progettare le azioni per le diverse Square
...

Nel Monopoly, quando si arriva su una casella, il comportamento è diverso per ciascun tipo di casella.
Per esempio quando un Player finisce sulla casella Go, riceve 200 dollari. L'azione è diversa se il giocatore finisce sulla casella Income Tax e così via. Si noti che ci sono delle regole differenti per i diversi tipi di caselle.

Come non fare
...
// brutto progetto
SWITCH ON (square.type){
	CASE GoSquare: player riceve $200
	CASE IncomeTaxSqaure: player paga la tassa
	...
}
Cosa consiglia il pattern
...

Polymorphism suggerisce di definire un'operazione polimorfa per ciascun tipo per cui il comportamento varia. Esso varia per i tipi (le classi) RegularSquare, GoSqaure, ecc..
Qual è l'operazione che varia? Quella che avviene quando un giocatore finisce su una casella.
Pertanto un buon nome per tale operazione potrebbe essere landedOn ("finito su").
In base a Plymorphism si creerà una classe separata per ciascun tipo di casella che ha una diversa responsabilità landedOn, implementata in ciascuna classe come un metodo landedOn differente.
Pasted image 20230521173823.png
Si noti che in UML è stato indicato l'operazione landedOn come astratta {abstract}.
Si noti anche che la superclasse è indicata anch'essa come astratta {abstract}.

Il problema interessante da risolvere è la progettazione dinamica: come devono evolvere i diagrammi di interazione? Quale oggetto deve inviare il messaggio landedOn alla casalle su cui finisce un giocatore?
Poiché un oggetto Player conosce la sua posizione attuale (su cui è arrivato) in base a Low Coupling e Information Expert, Player è un buon candidato per inviare tale messaggio.

Pasted image 20230521174257.png
Pasted image 20230521174341.png
Si noti, nella prima immagine, che Square è la superclasse.
Nella seconda immagine invece sono mostrate le operazioni per ogni sottoclasse.
Quando viene chiamata l'operazione landedOn viene passato il Player p come parametro, poiché potrebbe essere necessario accreditargli del soldi o addebitarglieli.
Ogni Square particolare avrà una diversa implementazione del metodo landedOn.

  • GoSquare: vengono accreditati dei soldi a Player
  • IncomeTaxSquare: vengono addebitati dei soldi a Player
  • RegularSquare: in questo caso non succede nulla, il corpo di questo metodo sarà vuoto (metodo NO-OP)