Reputation:
I asked a question about using the Strategy pattern and LSP, but the answer were mixed, and in the long run, it isn't good idea. However a comment at the bottom of my question stated that while I'm not in violation of LSP, I'm in violation of the Interface Segregation Principle which states:
that clients should not be forced to implement interfaces they don't use. Instead of one fat interface many small interfaces are preferred based on groups of methods, each one serving one submodule.
Suppose I had a Weapon class like this:
public final Weapon{
private final String name;
private final int damage;
private final List<AttackStrategy> validactions;
private final List<Actions> standardActions;
public 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.
}
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
}
}
Use:
List<AttackStrategy> actions = new ArrayList<AttackStrategy();
actions.add(new Shoot())
List<Actions> standardActionactions = new ArrayList<Actions>();
actions.add(new Reload(10))
Weapon rifle = new Weapon("Sniper Rifle", 5, standardActionactions, actions);
List<AttackStrategy> actions = new ArrayList<AttackStrategy();
actions.add(new Swing())
List<Actions> standardActionactions = new ArrayList<Actions>();
actions.add(new Drop())
Weapon sword = new Weapon("Standard Sword", 10, standardActionactions, actions);
I have two Weapons, and both behave differently, while both can be used to attack, both have different actions specific to each type. For example, Sword shouldn't and by the logic of my collection, cannot call or implement reload in anyway.
In my text adventure game if you pass "reload" to sword.standardAction("reload")
, the internal logic will check to see if the List contains a reload object, and if does, do it, if not, the weapon isn't capable and therefore can't, so it ignore it.
Am I in violation of the ISP?
My sword is never going to implement reload
, unless a client decides to implement it, I don't force a client to accept that a Sword
is aware of the Reload
method which they will never use.
Upvotes: 0
Views: 863
Reputation: 546
It sounds like that the Weapon abstraction is a little too general. I would break that into smaller abstractions
Weapon (abstract or interface - take your pick)
GunBasedWeapon (extends Weapon) (guns, rifles, laser guns, 80s megatron)
HandheldWeapon (extends Weapon) (swords, clubs, chains, etc)
ThrowableWeapon (extends Weapon) (boomerangs)
WeaponAction - each weapon class can now have a list of valid actions, if somehow someone passes "applyLotion" to a GunBasedWeapon, only the Gun class will know that it's not valid.
Upvotes: 0