Ketika saya ingin membuat objek yang menggabungkan objek lain, saya mendapati diri saya ingin memberikan akses ke objek internal alih-alih mengungkapkan antarmuka ke objek internal dengan fungsi passthrough.
Misalnya, kita memiliki dua objek:
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;
};
Dan katakanlah kita ingin memiliki objek Mobil yang terdiri dari Engine dan ModelName (ini jelas dibuat-buat). Salah satu cara yang mungkin untuk melakukannya adalah memberikan akses ke masing-masing
/* 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;
};
Menggunakan objek ini akan terlihat seperti ini:
Car1 car1;
car1.getModelName()->setName( "Accord" );
car1.getEngine()->setSize( 2 );
car1.getEngine()->doStuff();
Kemungkinan lain adalah membuat fungsi publik pada objek Mobil untuk setiap fungsi (yang diinginkan) pada objek internal, seperti ini:
/* 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;
};
Contoh kedua akan digunakan seperti ini:
Car2 car2;
car2.setModelName( "Accord" );
car2.setEngineSize( 2 );
car2.doEngineStuff();
Kekhawatiran saya dengan contoh pertama adalah bahwa hal itu melanggar enkapsulasi OO dengan memberikan akses langsung ke anggota pribadi.
Kekhawatiran saya dengan contoh kedua adalah bahwa, ketika kita mencapai level yang lebih tinggi dalam hirarki kelas, kita dapat berakhir dengan kelas "seperti dewa" yang memiliki antarmuka publik yang sangat besar (melanggar "I" dalam SOLID).
Manakah dari dua contoh yang mewakili desain OO yang lebih baik? Atau apakah kedua contoh menunjukkan kurangnya pemahaman OO?