Reputation: 86277
Here's some code I've inherited for a game. The sample code creates Armor.
At the moment to make some new Armor, you need to write a new class. E.g.
// Armor.java
public class Armor extends Item {
public int tier;
public Armor( int tier ) {
this.tier = tier;
}
}
and
// ClothArmor.java
public class ClothArmor extends Armor {
{
name = "Cloth armor";
}
public ClothArmor() {
super( 1 );
}
@Override
public String desc() {
return "Some Cloth Armor.";
}
}
How would you structure the code to make it more generic? It would seem obvious to just read from a text-based config file but I can see this running into problems when you wanted to create an Armor with special abilities for example.
Are there any resources or design patterns I can use to figure out how to proceed?
Upvotes: 0
Views: 92
Reputation: 73530
It's always tempting to add more levels to a type hierachy to add functionality. But that is not always the best path. Sometimes the best path is to determine the orthogonal features and extract them instead. The key here is preferring composition rather than inheritance.
So in this case I'd think about how is a ClothArmour
different from Armour
, and how is Armour
different from Item
? I'd say ClothArmour
differs from Armour in what it is made from, and the bonuses it applies. Then I'd say Armour differentiates from Item by the location it equips to.
Then I'd consider getting rid of ClothArmour
and Armour
altogether.
Item clothArmour = new Item(
EquipLocation.Chest,
ComponentType.Cloth,
Bonuses.defenceOnEquip(1) );
Why is this an improvement? It seems much less object oriented. Well loading or saving this format is relatively trivial.
Upvotes: 0
Reputation: 8353
If your intent is to add dynamically some behaviour to an Armor
, you can use the Decorator design pattern. Try to have a look here. It is one of the most used pattern of GoF's book, Design Patterns.
So, if I do understand well your needs, you can read from a file the properties you want to add to a base Armor
and then, using a factory, add them to the armor using the decorator pattern.
interface Armor {
// Put public interface of Armor here
public String desc();
}
class BaseArmor extends Item implements Armor {
public int tier;
public BaseArmor( int tier ) {
this.tier = tier;
}
public String desc() {
return "A base armor ";
}
}
// Every new possible feature of an armor has to extend this class
abstract class ArmorDecorator implements Armor {
protected final Armor armor;
// The armor that we want to decorate
public ArmorDecorator(Armor armor) {
this.armor = armor;
}
}
// An armor that is made of cloth
class MadeOfCloth extends ArmorDecorator {
public MadeOfCloth(Armor armor) {
super(armor);
}
@Override
public String desc() {
// Decoration: we add a feature to the desc method
return armor.desc() + "made of Cloth ";
}
}
// The factory that reads the properties file and build armors
// using the information read.
enum ArmorFactory {
INSTANCE;
public Armor build() {
Armor armor = null;
// At this point you have to had already read the properties file
if (/* An armor made of cloth */) {
armor = new MadeOfCloth(new BaseArmor(1));
}
return armor;
}
}
Upvotes: 2