Bailey Parker
Bailey Parker

Reputation: 15903

Design Pattern to Avoid Abstract Static Methods (or Overriding a Static Method)

I know that static methods can't be abstracted nor can they be overridden, only hidden (and in which case late bindings don't happen). With regard to this, I am struggling with a logical way to express the following relationship:

public abstract class Spell {
    protected int power;

    public Spell(int power) {
        this.power = power;
    }

    public int getPower() { return power; }

    // I know this is impossible, but please bare with me
    public abstract static int getMaxPower();
}

public class AttackSpell extends Spell {
    public AttackSpell(int power) {
        super(power);
    }

    public static int getMaxPower() { return 50; }
}

public class HealSpell extends Spell {
    public HealSpell(int power) {
        super(power);
    }

    public static int getMaxPower() { return 30; }
}

In this contrived example, max power is an attribute that I expect every spell to know (and every subclass of Spell has an individual max power that applies to all instances of itself). In other SO threads, the proposed solution to this impossible situation is to make getMaxPower() an instance method. However, I don't think this makes sense because power is an implementation detail that is useful to know before instantiation (eg. Constructing an array of spell instances from power 1 to the max power of that spell).

A solution that I see is creating a factory for each spell (or a more generic one for all spells), which has an instance method getMaxPower() that knows the max power attribute of the spell which it instantiates. However, this also doesn't seem optimal to me because:

  1. It makes more sense for each spell to know its own properties
  2. This makes the subclass of Spell oblivious to an implementation detail (namely, its constructor can't check whether a provided power exceeds its max power)

Is the factory pattern the correct approach? If so, what would be the logical justification to it? If not, is there a pattern that more appropriately fits this problem?

Upvotes: 5

Views: 1052

Answers (3)

Bob Dalgleish
Bob Dalgleish

Reputation: 8227

A simple method is to make getMaxPower() into a factory method. Behind the scenes it instantiates an instance of itself, caches it and queries the instance.

Upvotes: 0

fabian
fabian

Reputation: 82491

Since you can only override non-static functions, I see only one way doing this: You have to attach the information to the Class object somehow.

But this is no design pattern, i know...

You best annotate your class with the information and use reflection. Since annotations can be inherited, this should be exactly the behaviour you want. See main in Spell for an example how to use it.

Definition of the annotation type:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited
public @interface SpellInfo {
    int maxPower();
}

NOTE: Integer.MIN_VALUE is the value for "undefined" here.

@SpellInfo(maxPower = Integer.MIN_VALUE)
public class Spell {
    protected int power;

    public Spell(int power) {
        this.power = power;
    }

    public int getPower() { return power; }

    public static int getMaxPower(Class<? extends Spell> c) {
        return c.getAnnotation(SpellInfo.class).maxPower();
    }

    public final int getMaxPower() {
        return getMaxPower(this.getClass());
    }

    // test it
    public static void main(String[] args) {
        System.out.println("Spell.getMaxPower(Spell.class)="+
                                   Spell.getMaxPower(Spell.class));
        System.out.println("Spell.getMaxPower(AbstractSpellType.class)="+
                                   Spell.getMaxPower(AbstractSpellType.class));
        System.out.println("Spell.getMaxPower(HealingSpell.class)="+
                                   Spell.getMaxPower(HealingSpell.class));
        System.out.println("Spell.getMaxPower(AttackSpell.class)="+
                                   Spell.getMaxPower(AttackSpell.class));
        Spell spell = new AttackSpell(3);
        System.out.println("((Spell) new AttackSpell(3)).getMaxPower()=" +
                                   spell.getMaxPower());
        spell = new HealingSpell(4);
        System.out.println("((Spell) new HealingSpell(4)).getMaxPower()=" +
                                   spell.getMaxPower());

    }
}

AbstractSpellType inherits the annotation from Spell

public abstract class AbstractSpellType extends Spell {

    public AbstractSpellType(int power) {
        super(power);
    }

}



@SpellInfo(maxPower = 30)
public class HealingSpell extends Spell {

    public HealingSpell(int power) {
        super(power);
    }

}



@SpellInfo(maxPower = 50)
public class AttackSpell extends Spell {
    public AttackSpell(int power) {
        super(power);
    }
}

Upvotes: 1

Tony Hopkinson
Tony Hopkinson

Reputation: 20320

Disconnect generating spells from spell would be my first thought.

Have a class that described a spell family with a power range and then use that to create a collection of spells in the range, passing power etc through the constructor.

You could then think about some sort of data file save you a lot of hard coding.

Upvotes: 3

Related Questions