Quando voglio creare un oggetto che aggrega altri oggetti, mi trovo a voler dare accesso agli oggetti interni invece di rivelare l'interfaccia agli oggetti interni con le funzioni passthrough.
Ad esempio, diciamo che abbiamo due oggetti:
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;
};
E diciamo che vogliamo avere un oggetto Car che sia composto sia da un Motore che da un Nome Modello (questo è ovviamente concepito). Un modo possibile per farlo sarebbe quello di dare accesso a ciascuno di questi
/* 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;
};
L'uso di questo oggetto sarebbe simile a questo:
Car1 car1;
car1.getModelName()->setName( "Accord" );
car1.getEngine()->setSize( 2 );
car1.getEngine()->doStuff();
Un'altra possibilità sarebbe quella di creare una funzione pubblica sull'oggetto Car per ognuna delle funzioni (desiderate) sugli oggetti interni, in questo modo:
/* 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;
};
Il secondo esempio dovrebbe essere usato in questo modo:
Car2 car2;
car2.setModelName( "Accord" );
car2.setEngineSize( 2 );
car2.doEngineStuff();
La mia preoccupazione per il primo esempio è che viola l'incapsulamento OO dando accesso diretto ai membri privati.
La mia preoccupazione per il secondo esempio è che, quando arriviamo a livelli più alti nella gerarchia delle classi, potremmo finire con classi "divine" che hanno interfacce pubbliche molto grandi (viola la "I" in SOLID).
Quale dei due esempi rappresenta un design OO migliore? Oppure entrambi gli esempi dimostrano una mancanza di comprensione OO?