Reputation: 15903
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:
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
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
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
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