I have questions about a couple techniques I'm using in designing a class. I've declared some of its members as public final instead of private, and the constructor calls overridable methods. I know that these are normally considered bad practice, but I think that they may be justified in my situation, and I want to know what others think.
This is my class:
/** A monster that fights other monsters in my program */
public abstract class Creature
{
/**
* Keeps track of the different values determining how powerful a Creature
* is.
*/
public static class Stats
{
//subclass definition here.
//This subclass contains a lot of public static members like this:
public final CurMax health;
}
public final static Random RANDOM = new Random();
//These are the public final members that I'm wondering about.
public final Stats stats;
public final Attack[] attacks;
public final Stopwatch turnStopwatch;
private String name;
//This is the constructor I'm wondering about.
public Creature()
{
initMembers();
stats = generateStats();
name = RandomValues.NAMES[RANDOM.nextInt(RandomValues.NAMES.length)];
attacks = generateAttacks();
turnStopwatch = new Stopwatch(false);
}
/**
* Define any member variables for subclasses in Creature in this method,
* not in the subclasses' constructors.
*
* Using the Creature class requires the Constructor to call overridable
* methods. This is deliberate because the constructor assigns values to
* final member variables, but I want the values of these variables to be
* determined by subclasses. This method allows subclasses to assign values
* to their own member variables while still controlling the values of the
* final variables in the Creature class.
*
* @see #generateAttacks()
* @see #generateStats()
*/
protected abstract void initMembers();
protected abstract Stats generateStats();
protected abstract Attack[] generateAttacks();
public boolean isDead()
{
return stats.health.getCurrent() <= 0;
}
public String getName()
{
return name;
}
}
I declared the member variables as public final because I plan on using them frequently and creating methods to control access to them would be tedious. For example, I plan on writing lines like this throughout the program:creature.stats.health.getCurrent();
creature.stats.health.resetMax();
Avoiding giving public access to stats and health would require writing methods like getCurrentHealth()
and resetMaxHealth()
throughout the Creature class. The CurMax class has 10 methods besides constructors, and the stats class has 12 members of types that are similar to CurMax, so that would require writing over 100 additional functions in the Creature class. In light of this, is the way I used public final members appropriate? If it isn't, what would be another technique that would be more satisfactory?
If the use of public final members is fine, what about my use of overridable methods in the constructor? I want to allow each subclass of Creature to determine its own algorithm for creating the stats and array of attacks. For example, one creature might pick some random attacks out of a list, while another chooses attacks that are effective against another specific creature. Since stats
and attacks
are final variables, though, they must be defined in Creature's constructor. My workaround is to have the constructor call overridable methods to allow subclasses to determine the values of stats
and attacks
while leaving the actual assignment in the constructor.
I understand that the main risk associated with using overridable methods in constructors is that the overriden methods will be called before the subclass has an opportunity to define its own data members. I think that this is avoided in my situation because the generateStats() and generateAttacks() methods are there only to be used in the constructor. Also, I added another abstract method, initMembers, that is called in the Creature constructor before anything else. Subclasses can define any member variables in this function before generateStats() and generateAttacks() are called.
The constructors for Stats and Attack are large, so I can't simply pass a Stats object and an array of Attacks to the constructor. The call to super() in the subclasses' constructors would be unacceptably long.
Is the way I'm using overridable methods in the Creature constructor justified? If it isn't, what else should I do?
No, a constructor can't be made final. A final method cannot be overridden by any subclasses. As mentioned previously, the final modifier prevents a method from being modified in a subclass.
A constructor can call methods, yes. A method can only call a constructor in the same way anything else can: by creating a new instance. Be aware that if a method constructs a new object of the same type, then calling that method from a constructor may result in an infinite loop...
No, you cannot have both this() and super() in a single constructor.
Why not provide getters for Stats
, Health
etc., too? Then you don't need to use public instance variables and the calls are not too different:
creature.getStats().getHealth().getCurrent();
If you work with an IDE, then it will create getters and setters for you, so in my opinion there is no real excuse for not keeping access to instance variables restricted. It is also a matter of conventions. People are just not used to this kind of thing and others working with your code will be confused much more easily.
Concerning calling overridable methods in the constructor: you can always circumvent this if you pass some kind of Abstract Factory object from the subclass to the parent. You say that your subclasses know how to choose their own Stats and Attacks - instead of using an implementation of an (abstract,) overridable method you propagate an instance of a Factory interface from the concrete implementation to the parent:
public interface CreatureFactory {
public Stats newStats();
public Attack newAttack();
}
public class Creature {
public Creature(CreatureFactory f) {
this.stats = f.newStats();
this.attack = f.newAttack();
}
}
public class Monster extends Creature {
public Monster() {
super(new MonsterFactory());
}
}
You can define parameters as needed in the Factory methods and this way customize your concrete creature classes as needed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With