Per prima cosa parliamo del ruolo della eredità nella programmazione ad oggetti, per esempio se dobbiamo definire vari oggetti con de comportamenti simili allora la prima cosa che ci viene in mente è definire un oggetto padre che definisce le funzioni che tutti i figli devono avere e poi le implementiamo in ogni figlio in base al proprio comportamento specifico. Per esempio:Strategy Pattern 1

Come possiamo vedere in Animal abbiamo tre metodi:

eat() che non cambia perché tutti gli animali mangiano e il comportamento è comune

sound() è comune a tutti pero per ognuno abbiamo bisogno di una implementazione particolare quindi il metodo deve essere astratto e bisogna implementare il metodo sound() in ogni sottoclasse

fly() che non si applica a Dog e Cats perché non volano, quindi bisogna sovrascrivere il comportamento del metodo padre e assicurarci che per gli animali che non volano il metodo fly() non faccia niente

Da questo possiamo vedere che solo con la eredità in caso dovessimo definire molti animali ci troveremmo con ridefinire molti metodi particolari per ogni specie e per capire il loro funzionamento dovremmo andare a analizzare la implementazione di ogni singolo oggetto.

Per riassumere gli svantaggi di questa implementazione sono:

dobbiamo duplicare molto codice nelle sottoclassi

per capire il funzionamento specifico di ogni oggetto dobbiamo vedere la implementazione particolare quindi non è molto pratico

è difficile cambiare il comportamento durante il runtime, perché tutti i comportamenti sono specificati in compile time e non si possono modificare facilmente

Allora rispettando il primo principio del disegno software che dice:

  “Identifica gli aspetti del codice cha cambiano e separali da cosa rimane uguale.”

isolando le parti del codice che cambiano possiamo aggiungere funzionalità senza modificare il resto del codice.

Segundo questo principio otterremo un codice che è più flessibile e adattabile, e che ci garantisce che solo le parti interessate cambino.

tornando al nostro design iniziale vediamo che il metodo fly() è quello che cambia e che dobbiamo separare in una implementazione esterna in questo modo:

Strategy Pattern 2

In questo modo procediamo a includere il FlyBehavior nella nostra classe padre.

Strategy Pattern 3

I vantaggi del strategy pattern sono quelli di poter separare le parti del codice che cambiano e poterli assegnare in run time. Questo conferisce molta flessibilità alla nostra soluzione che adesso può essere estesa e modificata in modo organico