用于创建派生类的模式,包含本身和基类中的许多字段

q09*_*987 6 c++ design-patterns

鉴于以下预先存在的框架,我需要找到好的设计模式来创建派生类的不同实例.

我面临的主要挑战如下:

challenge-1 >每个类有10个以上的字段,以及如何将这些字段传递给派生类,然后有效地传递给基类.

为了解决这个问题,我可以找出四个解决方案,但没有一个能吸引我.

方法1 >以简单格式传递所有参数

classA::classA(int field1, float field2, ..., double field29)
Run Code Online (Sandbox Code Playgroud)

=>缺点:创建一个超过6~7个传入参数的函数不是一个好主意

方法2 >将所有参数作为结构传递

struct DataClassA
{
int field1;
float field2;
...
double field29;
};

struct DataClassBA : DataClassA
{
int    m_iField30;
// ...
double m_iField40;
};
Run Code Online (Sandbox Code Playgroud)

所以首先我传递DataClassBAclassBA然后依次classBA传递DataClassAclassA.=> cons:类型DataClassBAclassBA类似的类型,除了一个包含操作而另一个不包含操作.此外,在将结构传递给构造函数时,会有副本和重复的惩罚.想象一下,对于每个不同的类,我们必须定义一个类似的结构,以便保存所有初始化数据.类及其对应结构之间的关键不同之处在于类包含一些方法,而结构纯粹用于传输数据.

方法3 >通过Set函数设置所有字段

classA
{
public:
    int Field1() const { return m_iField1; }
    classA& Field1(int field1)
    {
      m_iField1 = field1;
      return *this;
    }
    ...
}

classBA : public classA
{
public:
    int Field30() const { return m_iField30; }
    classBA& Field30(int field30)
    {
      m_iField30 = field30;
      return *this;
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

=>缺点:每个实例的创建都会导致许多函数调用并且非常昂贵.

方法4 >将映射传递给基类和派生类的所有构造函数.

=>缺点:虽然它使数据传递变得容易,但我认为这是个坏主意.

challenge-2 >基类的默认值由其不同的派生类决定.例如,默认值classA::m_iField2根据不同的派生类而不同.

为了解决这个问题,我可以找到两个解决方案,但没有一个能吸引我.

方法1 >将默认逻辑添加到派生类本身.

方法2 >将默认逻辑添加到工厂类本身.

我列出了我能想到的所有方法.但是,我仍然在寻找一个干净而专业的解决方案.如果有一个编写良好的API库,我可以用作参考来解决这个类似的问题,这将是最好的.欢迎任何评论.

谢谢

/////////////////////// framework ////////////////////////////////////////
// Note: 
// <1> the class hierarchy has to kept as this
// <2> getter and setter functions in each class have to kept as this
// <3> add new functions(i.e constructors) are allowed
// <4> add new classes or structures are allowed
/////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <map>
#include <string>
#include <iostream>
using namespace std;

/************************************************************************/
/* Class Name: classA (an abstract base class)
 * default value of m_iField2 is determined by its derived class
/************************************************************************/
class classA
{
public:
    virtual ~classA() = 0 {}
    // ...
private: // 
    int m_iField1;
    float m_iField2;  // one of the potential field that has to get the default value
    // ...
    double m_iField29;
};
/************************************************************************/
/* Class Name: classBA
 * If the pass-in parameters do NOT include value for the field classA::m_iField2
 * then assign its value as 200.0f
/************************************************************************/
class classBA : public classA
{
    // ...
private:
    int    m_iField30;
    // ...
    double m_iField40;
};

/************************************************************************/
/* Class Name: classCA
 * If the pass-in parameters do NOT include value for the field classA::m_iField2
 * then assign its value as 300.0f
/************************************************************************/
class classCA : public classA
{
    // ...
private:
    int m_iField50;
    // ...
    int m_iField60;
};

int main(int argc, char* argv[])
{
    map<string, string> mapStrsBA;
    mapStrsBA["name"] = "classBA";
    mapStrsBA["field1"] = "5";
    // ...
    mapStrsBA["field40"] = "1.89";
    // pass mapStrsBA to a factory class with function to create a new instance of class classBA

    map<string, string> mapStrsCA;
    mapStrsBA["name"] = "classCA";
    mapStrsBA["field1"] = "6";
    // ...
    mapStrsBA["field60"] = "19";
    // pass mapStrsCA to a factory class with function to create a new instance of class classCA
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

cel*_*chk 7

让我为挑战1添加一个方法5,这并不总是,但通常是适用的,特别是如果您碰巧有许多字段:查找严格属于一起的成员字段,并将它们收集到逻辑对象中.例如,假设您有一个如下所示的类Shape:

class Shape
{
public:
  Shape(pass initial values for all member variables);
  // ...
private:
  // bounding box coordinates
  int xmin, xmax;
  int ymin, ymax;
  // color
  int red, green, blue;
  int alpha;
  // center point (for rotations)
  int cx, cy;
};
Run Code Online (Sandbox Code Playgroud)

这是10个变量.然而,那些并非真正无关.它们中的大多数是x/y对,然后有一组指定颜色.因此,您可以按如下方式重写:

struct Point
{
  int x, y;
  Point(int ax, int ay): x(ax), y(ax) {}
};

struct Color
{
  int red, green, blue, alpha;
  Color(int r, int g, int b, int a): red(r), green(g), blue(b), alpha(a) {}
};

class Shape
{
public:
  // ...
private:
  // bounding box
  Point lower_left, upper_right;
  Color color;
  Point center;
};
Run Code Online (Sandbox Code Playgroud)

现在突然你只有四个变量传递.您甚至可以考虑制作一个由两个角点组成的矩形类型,并将其用于边界框,进一步将变量数量减少到3.

请注意,不仅要传递的参数数量减少,而且还增加了清晰度.

既然你没有提供关于你的课程的任何细节,我也不能说你的班级是否也可以进行这样的逻辑分组,但鉴于你提到的大量参数,如果不是,我会感到惊讶.


从挑战2开始,我认为最好将默认逻辑添加到派生类中.


小智 2

对于挑战1,我认为方法2更好。我不知道你的缺点是什么意思。即使您通过参数传递值,您仍然需要将值复制到 Class 成员。结构只会让你的构造函数变得简单。我认为你不需要继承。怎么样:

struct DataClassA
{
    int field1;
    float field2;
    ...
    double field29;
};

struct DataClassBA
{
    DataClassA a;
    int    m_iField30;
    // ...
    double m_iField40;

};
Run Code Online (Sandbox Code Playgroud)

对于挑战2,我认为你可以在数据结构中设置默认值。如果您不需要默认值,则可以更改该值。例如:

DataClassA::DataClassA()
{
    field1 = 1;
}

DataClassBA::DataClassBA()
{
    a.filed1 = 2;
}
Run Code Online (Sandbox Code Playgroud)