One approach would be to utilize the Specification pattern.
So, basically, your item would have a WearerSpecification
property which returns a specification that returns true if the current character satisfies it.
Something like this:
public abstract class Item
{
private readonly ISpecification<Character> _wearerSpecification;
protected Item(ISpecification<Character> wearerSpecification)
{
_wearerSpecification = wearerSpecification;
}
public ISpecification<Character> WearerSpecification
{
get { return _wearerSpecification; }
}
}
In the method that is called when the character tries to pick up an item, the code would look something like this:
public class Character
{
public void PickUpItem(Item item)
{
if(item.WearerSpecification.SatisfiedBy(this))
{
// item can be picked up
}
else
{
// item can't be picked up
}
}
}
To simplify the actual creation of your items and to not repeat your code, you can create concrete specification classes that represent requirements that often occur, e.g. you could create a AtLeastLevel10Specification
or a MinimumStrengthSpecification
:
public class AtLeastLevel10Specification : ISpecification<Character>
{
public bool IsSatisfiedBy(Character character)
{
return character.Level >= 10;
}
}
public class MinimumStrengthSpecification : ISpecification<Character>
{
private readonly int _minimumRequiredStrength;
public MinimumStrengthSpecification(int minimumRequiredStrength)
{
_minimumRequiredStrength = minimumRequiredStrength;
}
public bool IsSatisfiedBy(Character character)
{
return character.Strength >= _minimumRequiredStrength;
}
}
You would then possible use these classes directly inside your item declaration:
public class VeryHeavyShield : Item
{
public VeryHeavyShield()
: base(CreateSpecification())
{
}
private static ISpecification<Character> CreateSpecification()
{
return new AtLeastLevel10Specification().And(
new MinimumStrengthSpecification(50));
}
}