Использование открытых окончательных переменных-членов и переопределяемых методов в конструкторе

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

Другие вопросы по тегам