Cuando quiero crear un objeto que agregue otros objetos, quiero dar acceso a los objetos internos en lugar de revelar la interfaz a los objetos internos con funciones de paso a través.
Por ejemplo, digamos que tenemos dos objetos:
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;
};
Y digamos que queremos tener un objeto Car que esté compuesto por un Engine y un ModelName (obviamente, está ideado). Una posible forma de hacerlo sería dar acceso a cada uno de estos
/* 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;
};
El uso de este objeto se vería así:
Car1 car1;
car1.getModelName()->setName( "Accord" );
car1.getEngine()->setSize( 2 );
car1.getEngine()->doStuff();
Otra posibilidad sería crear una función pública en el objeto Car para cada una de las funciones (deseadas) en los objetos internos, como esta:
/* 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;
};
El segundo ejemplo se usaría así:
Car2 car2;
car2.setModelName( "Accord" );
car2.setEngineSize( 2 );
car2.doEngineStuff();
Mi preocupación con el primer ejemplo es que viola la encapsulación OO al dar acceso directo a los miembros privados.
Mi preocupación con el segundo ejemplo es que, a medida que llegamos a niveles más altos en la jerarquía de clases, podríamos terminar con clases "divinas" que tienen interfaces públicas muy grandes (viola el "I" en SÓLIDO).
¿Cuál de los dos ejemplos representa un mejor diseño OO? ¿O ambos ejemplos demuestran una falta de comprensión de OO?