在构造函数中使用公共最终成员变量和可重写方法

发布于 2024-11-29 13:02:28 字数 3149 浏览 1 评论 0原文

我对设计课程时使用的一些技术有疑问。我已将其一些成员声明为公共最终成员而不是私有成员,并且构造函数调用可重写的方法。我知道这些通常被认为是不好的做法,但我认为在我的情况下它们可能是合理的,而且我想知道其他人的想法。

这是我的课:

/** 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();
避免向公众提供对统计信息和健康状况的访问需要在整个 Creature 类中编写诸如 getCurrentHealth()resetMaxHealth() 之类的方法。除了构造函数之外,CurMax 类还有 10 个方法,stats 类有 12 个与 CurMax 类型类似的成员,因此需要在 Creature 类中编写 100 多个附加函数。鉴于此,我使用公共最终成员的方式是否合适?如果不是,另一种更令人满意的技术是什么?

如果使用公共最终成员没问题,那么我在构造函数中使用可重写方法怎么样?我希望允许 Creature 的每个子类确定自己的算法来创建统计数据和攻击数组。例如,一个生物可能会从列表中选择一些随机攻击,而另一个生物则选择对另一个特定生物有效的攻击。不过,由于 statsattacks 是最终变量,因此它们必须在 Creature 的构造函数中定义。我的解决方法是让构造函数调用可重写的方法,以允许子类确定 statsattacks 的值,同时将实际赋值保留在构造函数中。

我知道与在构造函数中使用可重写方法相关的主要风险是,在子类有机会定义其自己的数据成员之前将调用重写方法。我认为在我的情况下可以避免这种情况,因为generateStats()和generateAttacks()方法只能在构造函数中使用。另外,我添加了另一个抽象方法 initMembers,该方法在 Creature 构造函数中首先调用。子类可以在调用generateStats()和generateAttacks()之前在此函数中定义任何成员变量。

Stats 和 Attack 的构造函数很大,因此我不能简单地将 Stats 对象和 Attacks 数组传递给构造函数。在子类的构造函数中对 super() 的调用将非常长。

我在 Creature 构造函数中使用可重写方法的方式是否合理?如果不是,我还应该做什么?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

故人的歌 2024-12-06 13:02:28

为什么不为 StatsHealth 等提供 getter?那么你就不需要使用公共实例变量,并且调用也没有太大不同:

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

如果你使用 IDE,那么它会为你创建 getter 和 setter,所以在我看来,没有真正的借口不保留对实例变量受到限制。这也是一个惯例问题。人们只是不习惯这种事情,其他使用你的代码的人会更容易感到困惑。

关于在构造函数中调用可重写方法:如果您传递某种 Abstract Factory ,您始终可以规避此问题对象从子类到父类。您说您的子类知道如何选择自己的统计信息和攻击 - 您不是使用(抽象的)可重写方法的实现,而是将 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());
    }
}

您可以根据需要定义参数在工厂方法中,这样可以根据需要自定义具体的生物类。

Why not provide getters for Stats, Healthetc., 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.

失去的东西太少 2024-12-06 13:02:28

为什么不能将generateStats() 和generateAttack() 方法抽象化?

Why can't you make your generateStats() and generateAttack() methods abstract?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文