具有共同属性但值不同的派生类的正确设计设置

时间:2015-05-08 18:36:32

标签: c++ polymorphism

所以我可以考虑一些方法来做到这一点,但我觉得我只是在为每个新的子类重新输入相同的东西。是否有一种设计模式,我可以通过这种方式设置我的子类的完整结构,减少实现所需的代码量(如果可能,还可以强制执行?)

这看起来很简单,但是我的想法不起作用,我发现最好的是在调用构造函数时在参数中进行硬编码或在每个子类中设置新的常量然后使用这些

我现在所拥有的是这样的:

"parent.hpp"
class Parent {
    private:
        std::string name;
        int someValue;

    protected:
        Parent(std::string name, int someValue); // NOTE: There will be 7 parameters/attributes that need initial base values
        void setName(std::string name) { this->name = name; }
        void setSomeValue(int someValue) { this->someValue = someValue; }

    public:
        std::string getName() { return this->name; }
        int getSomeValue() { return this->someValue; }
};

"parent.cpp"
Parent::Parent(std::string name, int someValue) {
    setName(name);
    setSomeValue(someValue);
}

"child.hpp"
class Child : public Parent {
    public:
        Child();
};

"child.cpp - option 1"
static const std::string DEFAULT_NAME = "Jon";
static const int DEFAULT_SOME_VALUE = 100;

Child::Child() : Parent(DEFAULT_NAME, DEFAULT_SOME_VALUE) {
    // other stuff if needed
}

"child.cpp - option 2"
Child::Child() : Parent("Jon", 100) {
    // other stuff if needed
}

将会有虚拟方法,稍后我会添加,但是现在我只想知道(可能)有许多子类的正确设计模式。还有更多参数将是所有int值的共同参数。我似乎不清楚构造函数是Child::Child("string", 1, 2, 3, 4, 5, 6),尽管实现新的子类会更容易。

另一方面,如果我只是为每个子类中的基值重新输入样板常量,那么构造函数将更具描述性,但会有大量的代码重用。

在我看来,我想要做的是在Parent类中具有虚拟保护常量,Child类需要定义它们,然后从构造函数中调用它们,但这是不允许的。两个选项中哪一个更好?对此有更好的“长期”设置吗?

我浏览了所有相似的问题,我找到的最接近的是:Proper way to make base class setup parent class。虽然我不确定这个想法是否可以解决我的问题或使任何事情变得更清楚。

我的另一个想法是从默认构造函数中调用纯虚方法,但据我所知,这也是不允许的。

2 个答案:

答案 0 :(得分:1)

也许你可以在这里结合两个想法:

  1. Avoiding a large number of args passed to a function一般(包括ctor)。

  2. Method chaining

  3. (第一个在这里更为基础,第二个在本质上更少,并且仅用于提高可读性。)

    更详细:

    具有任何功能,特别是基类的ctor,取7个参数,看起来非常冗长&脆弱。假设您意识到需要添加另一个参数。你现在必须检查所有派生类吗?这有问题。

    所以让我们从以下内容开始:

    class Parent
    {
    protected:
        explicit Parent(const ParentParams &params);
    };
    

    ParentParams看起来像这样:

    class ParentParams
    {
    public:
         // Initialize with default stuff.
         ParentParams(); 
    
         // Changing only the foo aspect (via method chaining).
         ParentParams &setFoo(Foo foo_val)
         {
             m_foo = foo_val;
             return *this;
         }
    
         // Changing only the bar aspect (via method chaining).
         ParentParams &setBar(Bar bar_val)
         {
             m_bar = bar_val;
             return *this;
         }
    
         // Many more - you mentioned at least 7.
         .... 
    };
    

    现在孩子看起来像这样:

    // A child that happens to have the property that it changes foo and bar aspects.
    class FooBarChangingChild :
        public Parent
    {
    public:
         FooBarChangingChild();
    };
    

    在其实施中:

    // Static cpp function just creating the params.
    static ParentParams makeParams() 
    {
         // Note the clarity of which options are being changed.
         return ParentParams()
              .setFoo(someFooVal)
              .setBar(someBarVal);
    }
    
    FooBarChangingChild::FooBarChangingChild() :
        Parent(makeParams())
    {
    
    }
    

答案 1 :(得分:1)

我会使用另一个对象来保持像Ami这样的状态,尽管我会因为其他原因而这样做。由于状态是一个单独的类,你可以在构造实际的父和子之前完全构造它,它可以有自己的层次。

class Parent {
protected:
    struct ParentState {
        std::string name;
        int someValue;
    };

    Parent(ParentState);

    void setName(std::string name) { data.name = name; }
    void setSomeValue(int someValue) { data.someValue = someValue; }

public:
    std::string getName() { return data.name; }
    int getSomeValue() { return data.someValue; }

private:
    ParentState data;
};

class Child : public Parent {
    struct ChildDefaults : public Parent::ParentState {
        ChildDefaults();
    };

public:
    Child();
};

实施

Parent::Parent(ParentState init) {
    // since you have setters, they should be used
    // instead of just data=init;
    setName(init.name);
    setSomeValue(init.someValue);
}

Child::ChildDefaults::ChildDefaults(){
    name = "Jon";
    someValue = 100;
}

Child::Child() : Parent(ChildDefaults()){
    // other stuff if needed
}

如果将ParentState和ChildDefault类放在单独的文件中,则可以使用该文件将所有默认值放在一个可以轻松查找或更改它们的位置。如果它们没有隐藏在类中,它们也可能更漂亮,强制使用额外的范围语法。

附录: 要将整个默认设置heirarchy放在它自己的标头中,只需将它们全部移动到一个标头即可。一定要做一个包含守卫,以避免多次定义构造函数。

#ifndef THE_DEFAULTS_H
#define THE_DEFAULTS_H

struct ParentState {
    std::string name;
    int someValue;
};

struct ChildDefaults : public Parent::ParentState {
    ChildDefaults() {
        name = "Jon";
        someValue = 100;
    }
};

// more default settings for other classes

#endif