When I want to create an object which aggregates other objects, I find myself wanting to give access to the internal objects instead of revealing the interface to the internal objects with passthrough functions.
For example, say we have two objects:
class Engine;
using EnginePtr = unique_ptr<Engine>;
class Engine
{
public:
Engine( int size ) : mySize( 1 ) { setSize( size ); }
int getSize() const { return mySize; }
void setSize( const int size ) { mySize = size; }
void doStuff() const { /* do stuff */ }
private:
int mySize;
};
class ModelName;
using ModelNamePtr = unique_ptr<ModelName>;
class ModelName
{
public:
ModelName( const string& name ) : myName( name ) { setName( name ); }
string getName() const { return myName; }
void setName( const string& name ) { myName = name; }
void doSomething() const { /* do something */ }
private:
string myName;
};
And lets say we want to have a Car object which is composed of both an Engine and a ModelName (this is contrived obviously). One possible way to do so would be to give access to each of these
/* give access */
class Car1
{
public:
Car1() : myModelName{ new ModelName{ "default" } }, myEngine{ new Engine{ 2 } } {}
const ModelNamePtr& getModelName() const { return myModelName; }
const EnginePtr& getEngine() const { return myEngine; }
private:
ModelNamePtr myModelName;
EnginePtr myEngine;
};
Using this object would look like this:
Car1 car1;
car1.getModelName()->setName( "Accord" );
car1.getEngine()->setSize( 2 );
car1.getEngine()->doStuff();
Another possibility would be to create a public function on the Car object for each of the (desired) functions on the internal objects, like this:
/* passthrough functions */
class Car2
{
public:
Car2() : myModelName{ new ModelName{ "default" } }, myEngine{ new Engine{ 2 } } {}
string getModelName() const { return myModelName->getName(); }
void setModelName( const string& name ) { myModelName->setName( name ); }
void doModelnameSomething() const { myModelName->doSomething(); }
int getEngineSize() const { return myEngine->getSize(); }
void setEngineSize( const int size ) { myEngine->setSize( size ); }
void doEngineStuff() const { myEngine->doStuff(); }
private:
ModelNamePtr myModelName;
EnginePtr myEngine;
};
The second example would be used like this:
Car2 car2;
car2.setModelName( "Accord" );
car2.setEngineSize( 2 );
car2.doEngineStuff();
My concern with the first example is that it violates OO encapsulation by giving direct access to the private members.
My concern with the second example is that, as we get to higher levels in the class hierarchy, we could wind up with "god-like" classes that have very large public interfaces (violates the "I" in SOLID).
Which of the two examples represents better OO design? Or do both examples demonstrate a lack of OO comprehension?