Il problema è che, pur essendo semanticamente diversi, l'heater e il cooler, in quanto svolgono diverse funzioni (anzi opposte), hanno lo stesso contratto (switchOn e switchOff) in quanto entrambi sono dei device che vengo accesi e spenti.
Quindi mi chiedo è corretto avere due interfacce con medesimo contratto o meglio generalizzare in un'unica interfaccia?
Entrambi sono dei device pertanto è preferibile avere un'unica interfaccia.
Ad esempio:
public interface Device {
public void switchOn();
public void switchOff();
}
public class Heater implements Device {
/* proprie di Heater */
public void heat() { /* codice vario */ ; }
public void noHeat() { /* codice vario */ ; }
/* Implementa le funzioni di interfaccia */
public void switchOn() { this.heat(); }
public void switchOff() { this.noHeat(); }
}
public class Cooler implements Device {
/* proprie di Cooler*/
public void cool() { /* codice vario */ ; }
public void noCool() { /* codice vario */ ; }
/* Implementa le funzioni di interfaccia */
public void switchOn() { this.cool(); }
public void switchOff() { this.noCool(); }
}
public class TemperatureControllerTask{
Device cl;
Device ht;
public TemperatureControllerTask(Device cl, Device ht) {
this.cl = cl;
this.ht = ht;
}
public void action() {
this.ht.switchOff();
this.cl.switchOn();
this.cl.switchOff();
this.ht.switchOn();
}
}
a questo punto il TemperatureControllerTask può essere anche implementato come uno state pattern se le circostanze lo richiedono.
Se poi serve un NullDevice è sufficiente fornire un'implementazione vuota all'interfaccia Device.
Allargando un po' il discorso, si potrebbe anche implementare un PressureDevice (se la cosa ha senso) fornendo l'opportuna implementazione all'interfaccia Device, e poi passare l'oggetto a TemperatureControllerTask. Il succo del discorso è che la chiamata a action() non viene modificata.
Il contratto è rispettato è la logica cambia in base all'oggetto (oggetti) istanziati in TemperatureControllerTask. (Quel che fa il pattern State appunto).
Infine, potrei anche fare in modo che le interfacce heater e cooler abbiano diversi contratti, ed utilizzare la composizione https://pastebin.com/GdxW1iw, anche se alla fine le relative classi concrete hanno medesima implementazione; però se in futuro c'è la necessità di riscrivere la logica, la composizione mi permette di non apportare modifiche al task a differenza dell'uso dell'ereditarietà...o mi sbaglio?
Ereditarietà e composizione si scelgono in base al criterio
is a o
has a.
Come hai notato tu stesso sia Heater che Cooler
sono Device, non
hanno un Device, per cui qui si deve usare l'ereditarietà.
Alla luce di questo, nel secondo link che hai postato è come se tu avessi messo un termostato in un termostato, il che non ha molto senso.