Wenn ich ein Objekt erstellen möchte, das andere Objekte aggregiert, möchte ich Zugriff auf die internen Objekte gewähren, anstatt die Schnittstelle zu den internen Objekten mit Passthrough-Funktionen anzuzeigen.
Angenommen, wir haben zwei Objekte:
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;
};
Nehmen wir an, wir möchten ein Autoobjekt haben, das sowohl aus einer Engine als auch aus einem ModelName besteht (dies ist offensichtlich erfunden). Ein möglicher Weg, dies zu tun, wäre, jedem von ihnen Zugang zu gewähren
/* 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;
};
Die Verwendung dieses Objekts würde folgendermaßen aussehen:
Car1 car1;
car1.getModelName()->setName( "Accord" );
car1.getEngine()->setSize( 2 );
car1.getEngine()->doStuff();
Eine andere Möglichkeit wäre, eine öffentliche Funktion für das Autoobjekt für jede der (gewünschten) Funktionen für die internen Objekte wie folgt zu erstellen:
/* 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;
};
Das zweite Beispiel würde folgendermaßen verwendet:
Car2 car2;
car2.setModelName( "Accord" );
car2.setEngineSize( 2 );
car2.doEngineStuff();
Mein Anliegen beim ersten Beispiel ist, dass es die OO-Kapselung verletzt, indem es den privaten Mitgliedern direkten Zugriff gewährt.
Meine Sorge mit dem zweiten Beispiel ist, dass wir, wenn wir zu höheren Ebenen in der Klassenhierarchie gelangen, mit "gottähnlichen" Klassen enden könnten, die sehr große öffentliche Schnittstellen haben (was das "Ich" in SOLID verletzt).
Welches der beiden Beispiele steht für ein besseres OO-Design? Oder zeigen beide Beispiele einen Mangel an OO-Verständnis?