views:

80

answers:

2

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, and 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];
}
+1  A: 

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

John Knoeller
So CompositeParameter would implement the Parameter interface and contain ParameterImpl objects?
Chris
Yes, that or any object that implemented the Parameter interface
John Knoeller
+1  A: 
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.

richj
That is the definition of a composite, my issue was that it was inheriting from the same thing it was composing.And my goal is not to hide bitwise tests, it is to create something that will generate all the possible values for testing purposes. It's the result of GetValue(...) that will be used in bitwise tests. I'm just using the name Parameter here for illustrative purposes, my real question was about the composing/inheriting design.
Chris
The definition of a composite is that it inherits from the same thing that it is composing. See "Design Patterns" pp164. If that's what your composite does, then it follows the pattern.
richj
According to the pattern, the Composite and Leaf both derive from the abstract base class Component. So if you want to implement the pattern as described in the book, Parameter would be abstract, and you would need a second sub-class LeafParameter.
richj
I don't really understand why a vector<unsigned int> doesn't meet your needs, but perhaps there are additional requirements that are not stated in the question?
richj
Ok, so you're right, it turns out what I was proposing is exactly the Composite Pattern (in my actual implementation Parameter was abstract). And yes, your Test() example is more or less what my GetValues() function was doing. Thanks for the help.
Chris