正确的方法使基类设置父类

Proper way to make base class setup parent class

本文关键字:基类 设置 父类 方法      更新时间:2023-10-16

我在以下情况下有点挣扎:假设我有一个类"Base",它需要一些信息才能正常工作。因此,我设置了一个带有适当参数的构造函数,例如:

class Base {
    public: Base(X x, Y y, Z z);
};

这适用于一般的"base"。但是我想有子类,代表特定的"基",经常使用,并包含设置基类所需的所有信息,例如:

class Derived {
    public: Derived() {
        X x;
        x.addSomeInformation("foo bar");
        // ...
        // now what?
    }
};

这里的问题是,我不能通过初始化列表调用父构造函数(通常是这样做的),因为要传递的参数还不存在。

因此,我觉得我的设计有严重的问题。我可以通过引入一个受保护的默认构造函数和一个模仿普通构造函数的"configure"方法来实现这一点,但我不确定这是否是个好主意(主要是因为这样我不能确保子类实际上调用了"configure"——如果不这样做,就会给我留下一个未正确初始化的基类):

class Base {
    public: Base(X x, Y y, Z z) { configure(x, y, z); }
    protected: Base() {}
    protected: void configure(X x, Y y, Z z);
};
class Derived {
    public: Derived() {
        X x;
        // ...
        configure(x, y, z);
    }
};

一个人如何以适当的方式处理这种情况?

我的第一个建议是让X有一个有价值的构造函数,而不是强迫它默认构造:

class Derived {
    public: Derived() : Base(X("foo bar"), <blah>) {
    }
};

如果这不是一个选项,将X-creation逻辑拆分为另一个函数并使用它:

class Derived {
    public: Derived() : Base(make_X(), <blah>) {
    }
    private: static X make_X() {
        X x;
        x.addSomeInformation("foo bar");
        // Anything else needed.
        return x;
    }
};

整个configure(有时称为initialize)方法概念似乎是可行的。事实上,我自己在过去的游戏开发项目中也遇到过这个问题,我只是使用了类似于你提出的解决方案,而不是让它在未来伤害我。

只要这个类不是某个公共API的一部分(即使它是,真的),您不应该担心如果有人不调用configure可能发生的问题。如果你真的想的话,你可以通过把参数左右设置为无效值来破坏很多api,但这并不意味着这些api不好。只要确保清楚地记录这个使用模式即可。

你感到不安,因为事实上,人们认为代码的正确性依赖于某个方法必须在某个时间调用另一个方法是不好的。他们是对的,只要这会影响代码的可读性(甚至可能影响性能),而在您的情况下,这是不正确的。具有initialize(或任何您想要调用它的方法)方法的对象以及构造函数都是而不是的坏做法,并且使用这样的方法实际上是编程中广泛使用的习惯用法。

另一件肯定有帮助的事情是设计基类,使在X/Y/Z出错时尽快失败。这样,任何可能因忘记调用configure而弹出的错误都可以在它们引起更大的问题之前被捕获并修复。

另一种选择是重新考虑(如果可能的话)X, Y等对象的构建方式,并为这些类的用户(在这种情况下是您)提供更多通过构造函数初始化它们的可能性。将这些对象划分为子组件(如果适用于您的情况)也会有所帮助。

这是以一种非常不同的方式给出的答案,但是您可以检查组合而不是继承。

。派生词"真的"是词根吗?

另一种选择是考虑创建一个工厂方法,它负责完成最终调用构造函数所需的所有工作,并提供所需的参数。(这基本上就是Mark B在他的第二个例子中所展示的。)

或者,有派生修改基。X和底。Y变成它们需要的值,作为这些setter被使用的结果更新了基地建设中应该发生的事情。

听起来隐藏的承包商可能是有益的,这样你就可以强制所有的建设通过一个工厂,总是可以调用product->config之前返回具体的产品创建。