Sto seguendo questa domanda , ma sto spostando la mia attenzione dal codice a un principio.
Dalla mia comprensione del principio di sostituzione di Liskov (LSP), qualunque sia il metodo nella mia classe di base, devono essere implementati nella mia sottoclasse e, secondo questa pagina, se si ignora un metodo nella classe di base e non fa nulla o si genera un eccezione, stai violando il principio.
Ora, il mio problema può essere riassunto in questo modo: ho un abstract Weapon
class
e due classi Sword
e Reloadable
. Se Reloadable
contiene uno specifico method
, chiamato Reload()
, dovrei effettuare il downcast per accedervi method
e, idealmente, dovresti evitarlo.
Ho quindi pensato di usare il Strategy Pattern
. In questo modo ogni arma era a conoscenza solo delle azioni che è in grado di compiere, quindi ad esempio Reloadable
un'arma può ovviamente ricaricare, ma Sword
non può e non è nemmeno a conoscenza di a Reload class/method
. Come ho affermato nel mio post Stack Overflow, non devo effettuare il downcast e posso mantenere una List<Weapon>
raccolta.
Su un altro forum , la prima risposta ha suggerito di consentire Sword
di essere consapevoli Reload
, semplicemente non fare nulla. La stessa risposta è stata data nella pagina Stack Overflow che ho collegato sopra.
Non capisco bene perché. Perché violare il principio e consentire a Sword di essere consapevole Reload
e lasciarlo in bianco? Come ho detto nel mio post Stack Overflow, l'SP ha praticamente risolto i miei problemi.
Perché non è una soluzione praticabile?
public final Weapon{
private final String name;
private final int damage;
private final List<AttackStrategy> validactions;
private final List<Actions> standardActions;
private Weapon(String name, int damage, List<AttackStrategy> standardActions, List<Actions> attacks)
{
this.name = name;
this.damage = damage;
standardActions = new ArrayList<Actions>(standardActions);
validAttacks = new ArrayList<AttackStrategy>(validActions);
}
public void standardAction(String action){} // -- Can call reload or aim here.
public int attack(String action){} // - Call any actions that are attacks.
public static Weapon Sword(String name, damage, List<AttackStrategy> standardActions, List<Actions> attacks){
return new Weapon(name, damage,standardActions, attacks) ;
}
}
Interfaccia di attacco e implementazione:
public interface AttackStrategy{
void attack(Enemy enemy);
}
public class Shoot implements AttackStrategy {
public void attack(Enemy enemy){
//code to shoot
}
}
public class Strike implements AttackStrategy {
public void attack(Enemy enemy){
//code to strike
}
}
reload()
vuoto o che standardActions
non contenga un'azione di ricarica è solo un meccanismo diverso. Non c'è alcuna differenza fondamentale. Puoi fare entrambe le cose. => La tua soluzione è praticabile (quale era la tua domanda) .; Sword non ha bisogno di sapere di ricaricare se Arma contiene un'implementazione predefinita vuota.
class Weapon { bool supportsReload(); void reload(); }
. I clienti testerebbero se supportati prima di ricaricare.reload
è definito contrattualmente per lanciare iff!supportsReload()
. Ciò aderisce all'LSP se le classi guidate aderiscono al protocollo che ho appena delineato.