Использование открытых окончательных переменных-членов и переопределяемых методов в конструкторе - PullRequest
5 голосов
/ 15 августа 2011

У меня есть вопросы о паре техник, которые я использую при разработке класса.Я объявил некоторые из его членов как 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 ]

7 голосов
/ 15 августа 2011

Почему бы не предоставить геттеры для Stats, Health и т. Д.?Тогда вам не нужно использовать общедоступные переменные экземпляра, и вызовы не слишком отличаются:

creature.getStats().getHealth().getCurrent();

Если вы работаете с IDE, то он создаст для вас геттеры и сеттеры, так что, по моему мнению,не является реальным оправданием для ограничения доступа к переменным экземпляра.Это также вопрос соглашений.Люди просто не привыкли к подобным вещам, и другие, работающие с вашим кодом, будут гораздо легче запутаться.

Относительно вызова переопределенных методов в конструкторе: вы всегда можете обойти это, если передадите какой-то Abstract Factory Объект из подкласса в родительский.Вы говорите, что ваши подклассы знают, как выбирать свои собственные Stats и Attacks - вместо того, чтобы использовать реализацию переопределяемого метода (abstract,), вы распространяете экземпляр интерфейса 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());
    }
}

Вы можете определять параметры по мере необходимости в методах Фабрики и таким образом настраивать ваши конкретные классы существ по мере необходимости.

0 голосов
/ 15 августа 2011

Почему вы не можете сделать ваши методы generateStats () и generateAttack () абстрактными?

...