Использование открытых окончательных переменных-членов и переопределяемых методов в конструкторе
У меня есть вопросы о паре техник, которые я использую при разработке класса. Я объявил некоторые из его членов как public final вместо private, и конструктор вызывает переопределенные методы. Я знаю, что это обычно считается плохой практикой, но я думаю, что они могут быть оправданы в моей ситуации, и я хочу знать, что думают другие.
Это мой класс:
/** 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;
}
}
Я объявил переменные-члены как public final, потому что я планирую использовать их часто, и создание методов для контроля доступа к ним было бы утомительно. Например, я планирую писать такие строки в программе: creature.stats.health.getCurrent();
creature.stats.health.resetMax();
Чтобы не предоставлять публичный доступ к статистике и здоровью, потребовалось бы написать такие методы, как getCurrentHealth()
а также resetMaxHealth()
на протяжении всего класса Существа. В классе CurMax помимо конструкторов есть 10 методов, а в классе stats есть 12 членов типов, похожих на CurMax, так что для этого потребуется написать более 100 дополнительных функций в классе Creature. В свете этого, уместно ли то, как я использовал публичные финальные члены? Если это не так, что может быть более удовлетворительным?
Если использование открытых финальных членов в порядке, как насчет использования переопределенных методов в конструкторе? Я хочу позволить каждому подклассу Существа определять свой собственный алгоритм для создания статистики и массива атак. Например, одно существо может выбрать несколько случайных атак из списка, в то время как другое выбирает атаки, которые эффективны против другого конкретного существа. поскольку stats
а также attacks
являются окончательными переменными, однако, они должны быть определены в конструкторе Существа. Мой обходной путь состоит в том, чтобы конструктор вызывал переопределенные методы, чтобы подклассы могли определять значения stats
а также attacks
оставляя фактическое назначение в конструкторе.
Я понимаю, что основной риск, связанный с использованием переопределенных методов в конструкторах, заключается в том, что переопределенные методы будут вызываться до того, как подкласс получит возможность определять свои собственные члены данных. Я думаю, что в моей ситуации этого избегают, потому что методы generateStats() и generateAttacks() используются только в конструкторе. Кроме того, я добавил еще один абстрактный метод, initMembers, который вызывается в конструкторе Creature раньше всего. Подклассы могут определять любые переменные-члены в этой функции перед вызовом generateStats() и generateAttacks().
Конструкторы для Stats и Attack являются большими, поэтому я не могу просто передать объект Stats и массив Attacks в конструктор. Вызов super() в конструкторах подклассов будет недопустимо длинным.
Оправдан ли способ использования переопределяемых методов в конструкторе Creature? Если это не так, что еще мне делать?
2 ответа
Почему бы не предоставить добытчики для Stats
, Health
и тд тоже? Тогда вам не нужно использовать публичные переменные экземпляра, и вызовы не слишком отличаются:
creature.getStats().getHealth().getCurrent();
Если вы работаете с IDE, она создаст для вас геттеры и сеттеры, так что, на мой взгляд, нет никакого реального оправдания тому, чтобы не ограничивать доступ к переменным экземпляра. Это также вопрос соглашений. Люди просто не привыкли к подобным вещам, и другие, работающие с вашим кодом, будут гораздо легче запутаться.
Что касается вызова переопределенных методов в конструкторе: вы всегда можете обойти это, если передаете какой-то объект Abstract Factory из подкласса в родительский. Вы говорите, что ваши подклассы знают, как выбирать свои собственные Stats и Attacks - вместо того, чтобы использовать реализацию переопределяемого (абстрактного) метода, вы распространяете экземпляр интерфейса Factory из конкретной реализации в родительский:
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());
}
}
Вы можете определять параметры по мере необходимости в методах Фабрики и таким образом настраивать ваши конкретные классы существ по мере необходимости.
Почему вы не можете сделать ваши методы generateStats() и generateAttack() абстрактными?