I'm sure many of you know Eric Lippert's Blog series: Wizards and Warriors. In it, he has two rules:
- A warrior can only use a sword.
- A wizard can only use a staff.
Over the series of blogs, he tries to show you how the C# type system fails to implement those rules and what to do about it instead. He suggest the creation of a Rule class, and let the Rule system determine what can happen in the game. You can agree or disagree, that's not the point of my question. I included it because it relates to my bigger question, how would you tell the Rule class or system that the object you're trying to carry is in fact a Sword without instanceof?
For my question, lets remove the Rule object and stick with two objects, a Wizard and Sword. A Wizard is a subclass of the abstract Character class and Sword is a subclass of the abstract Weapon class. The Character class will have a method called tryAdd();
public final boolean tryAdd(Weapon weapon){
// Code to add Weapon
}
At first I thought I would use an enum to determine what object the player is attempting to add to it's inventory.
public enum WeaponType {
ReloadableWeapon // -- imagine more weapon types
}
public abstract class Weapon
{
private WeaponType wt;
public Weapon(WeaponType wt)
{
this.wt = wt;
}
}
public ReloadableWeapon extends Weapon{
public ReloadableWeapon()
super(WeaponType.ReloadableWeapon);
{
}
}
As pointed out, it violates DRY and is a poor version of instanceof. Okay, fine, the comments suggested a Map or Collection of attributes, even this GameDev link suggested the same thing.
So a long the lines of this:
public enum GameObjectAttributes{
Replenishable
Destructable
Health
LongBlade
ShortBlade
Mana
Health
Potion
Indestructible
}
public abstract class Weapon
{
private List<GameObjectAttributes> gOAttributes;
// imagine a constructor :D.
public final boolean containsAttribute(GameObjectAttributes attribute)
{
// determine if weapon contains a specific attribute.
}
}
Suppose I had a Sword object, and the level of detail allows a Sword to be damaged and broken if hit hard enough, so my Sword object might contain Destructable and LongBlade as attributes.
As suggested in this link, this too is a bad idea because I'm using the attributes to determine or carry out behavior with that object.
HOWEVER!
Isn't this how OOP is supposed to work? I query an object for an answer, in this case true or false and carry out actions with that object? In this case I ask the Weapon, do you have LongBlade as an attribute? If so, the Wizard cannot add it to there inventory. The method doesn't add the weapon and returns false.
Problem
Even if we were to use a Rule class, at some point, we have to determine what object is being passed to the TryAdd() method, in other words, one object will have to communicate with another object to determine the object type. The link that suggests the attribute collection is a bad idea, says that a Player should implement a tryCarry method, but even that methods needs to decided if the Weapon parameter is a Sword.
How then do you make that distinction without instanceof or a variable to decided what to do? Or How do two objects communicate with each other so that one object is aware of the other object type without violating OOP principals? Can someone provide a clean code sample?
This problem is getting frustrating to solve because no matter what solution I come up with, someone will point out it's a bad idea, but no one can provide a solid solution to the problem. Even Eric Lippert's blog just suggests a Rule class but no example code of how to implement it.
What seems like a simple solution to a problem is ripped apart by veteran developers who are quick to point out something is a code smell or bad code. Sometimes I wonder why bother writing any code at all.