Given the following pre-existing framework, I need to find good design patterns to create different instances of derived class.
The main challenges that I have are as follows:
challenge-1> Each class has more than 10 fields and how to pass those fields to derived class and then to base class effectively.
To target this issue, I can figure out four solutions, but none of them appealing to me.
Method 1> pass all parameters in simple format
classA::classA(int field1, float field2, ..., double field29)
=> cons: it is not a good idea to create a function with more than 6~7 pass-in parameters
Method 2> pass all parameters as a structure
struct DataClassA
{
int field1;
float field2;
...
double field29;
};
struct DataClassBA : DataClassA
{
int m_iField30;
// ...
double m_iField40;
};
So first I pass DataClassBA
to classBA
and then in turn classBA
pass DataClassA
to classA
.
=> cons: type DataClassBA
and classBA
are similar types and except one contains operations while the other doesn't. Also, when passing structure to the constructors, there is a penalty of copies and duplicates. Imagine for each different class, we have to define a similar structure in order to hold all initialization data. The key different between the class and its corresponding structure is that class contains some methods while structure is purely used for transferring data.
Method 3> set all fields by Set functions
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;
}
...
}
=> cons: each creation of an instance will cause many functions calls and very expensive.
Method 4> pass the map to all constructors of base and derived class.
=> cons: I really think this is bad idea though it makes the data passing easy.
challenge-2> The default value of base class is determined by its different derived class.
For example, the default value of classA::m_iField2
is different based on different derived class.
To target this issue, I can figure out two solutions, but none of them appeal to me.
Method 1> Add the default logic to the derived class itself.
Method 2> Add the default logic to the factory class itself.
I have listed all methods I could think about. However, I still look for a clean and professional solution to this problem. It will be best, if there is a well-written API library where I can use as reference to address this similar issue. Any comment is welcome.
thank you
/////////////////////// 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;
}
A base class is also called parent class or superclass. Derived Class: A class that is created from an existing class. The derived class inherits all members and member functions of a base class. The derived class can have more functionality with respect to the Base class and can easily access the Base class.
When a class serves as base class for many derived classes, the situation is called: polymorphism.
The class whose members are inherited is called the base class, and the class that inherits those members is called the derived class. A derived class can have only one direct base class. However, inheritance is transitive.
12. How many classes can be derived from a derived class? Explanation: When a class is to be derived from another derived class, the derived class behaves as a normal base class hence there are no restriction on how many class can be derived from a derived class.
Let me add a method 5 to your challenge 1, which is not always, but often applicable, especially if you happen to have many fields: Find member fields which strictly belong together, and collect them into logical objects. For example, assume you have a class Shape
which looks like this:
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;
};
This is 10 variables. However, those are not really unrelated. Most of them are x/y pairs, and then there's a set specifying color. Thus you may rewrite this as follows:
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;
};
Now suddenly you only have four variables to pass around. You might even think of making a rectangle type consisting of two corner points and using that for the bounding box, further reducing the number of variables to 3.
Note that not only is the number of parameters to pass reduced, it also adds clarity.
Now since you didn't give any detail about your classes, I cannot say if such logical grouping is possible for your class, too, but given the large amount of parameters you mentioned, I'd be surprised if not.
As of challenge 2, I'd consider it better to add the default logic to the derived class.
For challenge 1, I think method 2 is better. I don't know what your cons mean. Even you pass the value by parameters, you still need copy the value to Class s member. Struct only makes your constructor simple. And I think you don't need inheritance. How about:
struct DataClassA
{
int field1;
float field2;
...
double field29;
};
struct DataClassBA
{
DataClassA a;
int m_iField30;
// ...
double m_iField40;
};
For challenge 2, I think you can set the default value in the data struct. And you change the value if you don't want default value. For example:
DataClassA::DataClassA()
{
field1 = 1;
}
DataClassBA::DataClassBA()
{
a.filed1 = 2;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With