I'm following up on this question, but I'm switching my focus from code to a principle.
From my understanding of the Liskov substitution principle (LSP), whatever methods are in my base class, they must be implemented in my subclass, and according to this page, if you override a method in the base class and it does nothing or throws an exception, you're in violation of the principle.
Now, my problem can be summed up like this: I have an abstract Weapon
class
, and two classes, Sword
and Reloadable
. If Reloadable
contains a specific method
, called Reload()
, I would have to downcast to access that method
, and, ideally, you'd want to avoid that.
I then thought of using the Strategy Pattern
. This way each weapon was only aware of the actions it's capable of performing, so for example, a Reloadable
weapon, can obviously reload, but a Sword
can't, and isn't even aware of a Reload class/method
. As I stated in my Stack Overflow post, I don't have to downcast, and I can maintain a List<Weapon>
collection.
On another forum, the first answer suggested to allow Sword
to be aware of Reload
, just don't do anything. This same answer was given on the Stack Overflow page I linked to above.
I don't fully understand why. Why violate the principle and allow Sword to be aware of Reload
, and leave it blank? As I said in my Stack Overflow post, the SP, pretty much solved my problems.
Why isn't it a viable solution?
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) ;
}
}
Attack Interface and Implementation:
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
}
}
class Weapon { bool supportsReload(); void reload(); }
. Clients would test if supported before reloading.reload
is defined contractually to throw iff!supportsReload()
. That adheres to the LSP iff drived classes adhere to the protocol I just outlined.reload()
blank or whetherstandardActions
does not contain a reload action is just a different mechanism. There's no fundamental difference. You can do both. => Your solution is viable (which was your question).; Sword does not need to know about reload if Weapon contains a blank default implementation.