Khi tôi muốn tạo một đối tượng tổng hợp các đối tượng khác, tôi thấy mình muốn cấp quyền truy cập vào các đối tượng bên trong thay vì tiết lộ giao diện cho các đối tượng bên trong với các chức năng thông qua.
Ví dụ: giả sử chúng ta có hai đối tượng:
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;
};
Và giả sử chúng ta muốn có một đối tượng Xe bao gồm cả Động cơ và ModelName (điều này rõ ràng là bị chiếm đoạt). Một cách có thể làm như vậy là cấp quyền truy cập cho từng
/* 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;
};
Sử dụng đối tượng này sẽ trông như thế này:
Car1 car1;
car1.getModelName()->setName( "Accord" );
car1.getEngine()->setSize( 2 );
car1.getEngine()->doStuff();
Một khả năng khác là tạo một chức năng công khai trên đối tượng Xe cho từng chức năng (mong muốn) trên các đối tượng bên trong, như sau:
/* 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;
};
Ví dụ thứ hai sẽ được sử dụng như thế này:
Car2 car2;
car2.setModelName( "Accord" );
car2.setEngineSize( 2 );
car2.doEngineStuff();
Mối quan tâm của tôi với ví dụ đầu tiên là nó vi phạm đóng gói OO bằng cách truy cập trực tiếp vào các thành viên tư nhân.
Mối quan tâm của tôi với ví dụ thứ hai là, khi chúng ta đạt đến các cấp cao hơn trong hệ thống phân cấp lớp, chúng ta có thể kết thúc với các lớp "giống như thần" có giao diện công cộng rất lớn (vi phạm "Tôi" trong RẮN).
Điều nào trong hai ví dụ đại diện cho thiết kế OO tốt hơn? Hay cả hai ví dụ đều thể hiện sự thiếu hiểu biết về OO?