The title is a bit vague but I can’t come up with a better wording, here’s the deal:
class A
{
public:
A();
A( const PropertyB& b );
PropertyA GetPropertyA();
PropertyB GetPropertyB();
SetPropertyA( const PropertyA& b );
SetPropertyB( const PropertyB& b );
//assignment not allowed
};
Suppose I want to use an std::vector< A >, but, it only makes sense to have a vector of A if all it’s elements have the same value for PropertyB. The current solutions is to supply a contrcutor-like method to create such array which guarantees all elements of the returned array have the same value for PropertyB, and a method that checks if that is the case:
Array MakeArray( size_t, const PropertyB& );
bool CheckIfArrayIsSane( const Array& );
So users can still call SetPropertyB() on the elements, but have a utility to check it and bail out if someone did:
Array x( MakeArray( 3, someValue ) );
x.SetPropertyA( aaa ); //should be allowed
x.SetPropertyB( someOtherValue ); //should NOT be allowed
//somewhat further in the program
if( !CheckIfArrayIsSane( x ) )
throw InsaneArrayExcpetion();
While this works, it’s error-prone since it is hard to force this check everywhere and not forget it, and clutters the code with checks.
Approcahes that do not work:
- Making SetPropertyB() private, and making MakeArray a friend function: then SetPropertyB() is not accessible anymore for users that simply want to use A without caring about the array.
- Wrapping std::vector< A > in a seperate class and only returning const A& references: this would mean the other setters like SetPropertyA() can also not be called, but users should be able to call them. It’s only SetPropertyB() that should be disallowed.
One more intrusive approach that would work but feels a bit unelegant, and needs extra functions to convert between the two etc:
class AWithFixedPropertyB
{
public:
A( const PropertyB& b );
PropertyA GetPropertyA();
PropertyB GetPropertyB();
SetPropertyA( const PropertyA& b );
};
class A : public AWithFixedPropertyB
{
public:
//contrcutors etc
SetPropertyB( const PropertyB& b );
};
//ok, users cannot modify property B in this Array
typedef std::vector< AWithFixedPropertyB > Array;
What would be the most elegant solution for this problem?
This doesn’t make sense, from an OO design viewpoint. Remember the Liskov Susbsitution Principle: A is-a B whenever an A object can be used in the place of a B object? Per that rule, the elements of your proposed
std::vector<A>fail the is-an-A test, since you can’t set their B property. Yetstd::vector<A>is supposed to be a container of A objects.In C++ we have private inheritance, and this makes explicit that the derived class does not have an is-a relationship with its parent. You can use that as follows:
You can now create a
std::vector<A_fixed_B>which behaves as you’d probably expect.