Composing objects of a class you inherit from?
I have a class Parameter, the purpose of which is to represent the possible values a certain parameter could hold (implements two key methods, GetNumValues() and GetValue(int index)).
Often one logical parameter (parameter values are bit flags) is best represented by 2 or more instances of the Parameter class (i.e. a Parameter that can be 1 or 2, and a Parameter than can be 4 or 8, rather than one Parameter than can be 5, 6, 9, or 10). To handle that, I would like to create a CompositeParameter class which contains Parameters, and will implement the GetNumValues() and GetValue() functions based on the combinations of the Parameters it holds.
And since CompositeParameter is combining Parameters to have them act as one single Parameter, the "CompositeParameter is a Parameter" relationship makes sense. So I find myself in the situation where I have a class which composes objects of a class it inherits from, which just doesn't seem right. But at the same time, I don't see why the higher level code shouldn't be able to treat CompositeParameters and Parameters exactly the same.
The only option I can think of is to have CompositeParameter simply compose Parameters, a开发者_开发技巧nd the higher level code would only deal with CompositeParameters. However, that is somewhat wasteful b/c the general case would be CompositeParameters which contained just one Parameter.
Thoughts?
class Parameter
{
public:
virtual unsigned int GetNumValues() const {...}
virtual unsigned int GetValue(unsigned int index) const {...}
}
class CompositeParameter : public Parameter
{
public:
// product of GetNumValues() of each item in mParamList
virtual unsigned int GetNumValues() const {...}
// allow all the possible combinations of the items in mParamList to be
// treated as one parameter. i.e. if mNumParams = 2, this would be analogous
// to getting the row and col index of a matrix from index, and combining
// the mParamList[0]->GetValue(row) and mParamList[1]->GetValue(col)
virtual unsigned int GetValue(unsigned int index) const {...}
private:
static const unsigned int MAX_PARAMS = 10;
unsigned int mNumParams;
const Parameter* mParamList[MAX_PARAMS];
}
This seems like a perfectly reasonable design. The only change I would make would be to change Parameter from a class to an interface.
Then you could have a Parameter class (or perhaps a ParameterImpl class) that implements the Parameter, and also have a CompositeParameter class that also implements the Parameter interface
I have a class which composes objects of a class it inherits from,
which just doesn't seem right.
Isn't that the definition of a composite?
(parameter values are bit flags)
This is the part of the design that I would question. Perhaps a better name for Parameter would be FlagSet?
It's fine to hide the bitwise tests behind an interface, but it seems like inheritence might be overkill to solve a problem with well known solutions in basic computer science.
However, that is somewhat wasteful b/c the general case would be
CompositeParameters which contained just one Parameter.
The point of the composite pattern is that a Leaf object represents the simple case, a Composite object represents the complex case, and client code can treat both cases the same. If your interface requires the client code to distinguish between the two, or iterate through the base class components, then you're not really getting any value out of using the pattern.
For example, if your main concern is testing, then the base class could have a method:
bool Test() const;
The leaf class implementation would look like:
bool LeafTester::Test() { return _DoTest(); }
The composite class implementation would look like:
bool CompositeTester::Test() {
bool success = true;
for (int i = 0; i < m_count; i++)
success &= m_components[i].Test();
return success;
}
And clients would always use the code like this:
// tester could be a Composite or a leaf, but we don't care:
bool testResult = tester.Test();
I've used a for loop to keep the example simple. In practice I would use STL instead.
精彩评论