[英]Access to 'inner' classes in case of composition
我將某些功能封裝在另一個類中使用的類中。 我認為這稱為合成。
class DoesSomething01
{
public:
DoesSomething01();
void functionality01();
void functionality02();
};
class DoesSomething02
{
public:
DoesSomething02();
void functionality01();
void functionality02();
};
class ClassA
{
public:
ClassA();
private:
DoesSomething01 *m_doesSomething01;
DoesSomething02 *m_doesSomething02;
};
如果我現在擁有一個“知道” ClassA
的ClassB
,並且必須使用/執行DoesSomething01
和/或DoesSomething02
類的functionality01
和/或functionality02
DoesSomething01
, DoesSomething02
我會看到兩種可能性:
a)將這樣的方法添加到ClassA
以提供對ClassB
DoesSomething01
和/或DoesSomething02
直接訪問:
DoesSomething01 *getDoesSomething01() { return *m_doesSomething01; }
DoesSomething02 *getDoesSomething02() { return *m_doesSomething02; }
然后, ClassB
可以執行以下操作:
m_classA->getDoesSomething01()->functionality01();
b)在ClassA
添加(在這種情況下為四個)方法,該方法將方法調用轉發給DoesSomething01
和DoesSomething02
如下所示:
void doesSomething01Functionality01() { m_doesSomething01->functionality01(); }
void doesSomething01Functionality02() { m_doesSomething01->functionality02(); }
void doesSomething02Functionality01() { m_doesSomething02->functionality01(); }
void doesSomething02Functionality02() { m_doesSomething02->functionality02(); }
哪個選項更好,為什么?
每個選項的優點/缺點是什么?
第一選擇可以被認為是代碼氣味。 根據羅伯特·C·馬丁(Robert C. Martin)的“清潔代碼”,它是“傳遞式導航”,應避免使用。 引用作者:
通常,我們不希望單個模塊對其協作者有太多了解。 更具體地說,如果A與B合作,而B與C合作,我們不希望使用A的模塊了解C。(例如,我們不希望a.getB()。getC()。doSomething( );.)
第二種選擇看起來更好。 這是Facade模式的經典用法。 並且更好,因為它隱藏了類DoesSomthing01
和DoesSomthing02
其他功能。 然后,您獲得了簡化視圖,它比第一個選項更易於使用。
編輯:還有另外一件事。 您有兩個具有相同功能的類,並由其他類聚合。 您應該在這里考慮使用Stratey模式 。 您的代碼將如下所示:
class DoesSomething
{
public:
virtual void functionality01() = 0;
virtual void functionality02() = 0;
}
class DoesSomething01 : DoesSomething
{
public:
DoesSomething01();
void functionality01();
void functionality02();
};
class DoesSomething02 : DoesSomething
{
public:
DoesSomething02();
void functionality01();
void functionality02();
};
class ClassA
{
public:
ClassA();
DoesSomething* doesSomething(); // Getter
void doesSomething(DoesSomething* newDoesSomething); // Setter
// ...
private:
DoesSomething *m_doesSomething;
};
然后,您將只需要兩個方法,而不是四個:
void doesFunctionality01() { m_doesSomething->functionality01(); }
void doesFunctionality02() { m_doesSomething->functionality02(); }
第一種情況是違反了Demeter的法律,Demeter認為一個班級只能與其直接的朋友交談。 基本上,第一種方法的問題是內部類DoSomething01和DoSomething02的任何更改都會觸發類A和類B的更改,因為這兩個類現在都直接依賴於這些內部類。
第二種方法更好,因為它封裝了內部類中的類B,但是此解決方案的副作用是,現在類A有很多方法,除了委派給其內部類外別無所求。 這很好,但是可以想象一下,如果DoSomething01具有內部類DoSomething03,並且類B需要在不直接了解其功能的情況下訪問其功能,而不是類A需要具有另一個將委托給DoSomething01的方法,該方法又將委托給DoSomething03。 在這種情況下,我認為最好讓B類直接了解DoSomething01,否則A類將具有一個巨大的接口,可以簡單地委派給其內部類。
如果要調用許多類和/或許多方法,那么以抽象父類的形式發明一個接口是有意義的:
class SomeInterface
{
public:
SomeInterface(){}
virtual void functionally01() = 0;
virtual void functionally02() = 0;
}
然后,DosSomthing01和其他類將繼承該類:
class DoesSomthing01 : public SomeInterface
並實施這些方法。
將鍵與此類的實例化關聯是否有意義
您可以將這些對象存儲在ClassA中,例如使用地圖(這里我使用整數作為鍵):
class ClassA
{
private:
std::map<int, SomeInterface*> m_Interfaces;
public:
SomeInterface* getInterface(const int key)
{
std::map<int, SomeInterface*>::iterator it(m_Interfaces.find(key));
if (it != m_Interfaces.end())
return it->second;
else
return NULL;
}
};
然后,您可以從ClassB訪問它們
int somekey = ...;
SomeInterface *myInter = m_classA->getInterface(somekey);
if (myInter)
myInter->functionally01();
這樣,您只有一種訪問方法(getInterface()),與對象的數量無關。
為了使用鍵對對方法的訪問進行編碼,可以創建一個映射,該映射將鍵映射到閉包或簡單的switch語句上:在SomeInterface中:
public:
void executeMethod(const int key)
{
switch(key)
{
case 1: functionally01(); break;
case 2: functionally01(); break;
default:
// error
}
int methodKey = ...;
int objectKey = ...;
SomeInterface *myInter = m_classA->getInterface(objectKey);
if (myInter)
myInter->executeMethod(methodKey);
看起來是中介模式的一個很好的例子。
此模式管理他擁有的2個對象之間的通信。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.