Snowcrash
Snowcrash

Reputation: 86277

Making a class more generic

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

Answers (2)

Michael Anderson
Michael Anderson

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

riccardo.cardin
riccardo.cardin

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

Related Questions