[英]Access to 'inner' classes in case of composition
I have certain functionality encapsulated in classes which I use in another class. 我将某些功能封装在另一个类中使用的类中。 I think this is called 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;
};
If I have now a ClassB
which "knows" ClassA
and have to use/execute functionality01
and/or functionality02
of classes DoesSomething01
and/or DoesSomething02
I see two possibilities: 如果我现在拥有一个“知道” ClassA
的ClassB
,并且必须使用/执行DoesSomething01
和/或DoesSomething02
类的functionality01
和/或functionality02
DoesSomething01
, DoesSomething02
我会看到两种可能性:
a) Add methods like this to ClassA
to provide ClassB
direct access to DoesSomething01
and/or DoesSomething02
: a)将这样的方法添加到ClassA
以提供对ClassB
DoesSomething01
和/或DoesSomething02
直接访问:
DoesSomething01 *getDoesSomething01() { return *m_doesSomething01; }
DoesSomething02 *getDoesSomething02() { return *m_doesSomething02; }
ClassB
could then do something like this: 然后, ClassB
可以执行以下操作:
m_classA->getDoesSomething01()->functionality01();
b) Add (in this case four) methods to ClassA
which forwards the method calls to DoesSomething01
and DoesSomething02
like this: b)在ClassA
添加(在这种情况下为四个)方法,该方法将方法调用转发给DoesSomething01
和DoesSomething02
如下所示:
void doesSomething01Functionality01() { m_doesSomething01->functionality01(); }
void doesSomething01Functionality02() { m_doesSomething01->functionality02(); }
void doesSomething02Functionality01() { m_doesSomething02->functionality01(); }
void doesSomething02Functionality02() { m_doesSomething02->functionality02(); }
Which option is better and why? 哪个选项更好,为什么?
What are the advantages/disadvantages of each option? 每个选项的优点/缺点是什么?
First option can be considered a code smell. 第一选择可以被认为是代码气味。 According to Robert C. Martin's 'Clean Code' it is "Transitive Navigation" and should be avoided. 根据罗伯特·C·马丁(Robert C. Martin)的“清洁代码”,它是“传递式导航”,应避免使用。 Quoting the author: 引用作者:
In general we don't want a single module to know much about its collaborators. 通常,我们不希望单个模块对其协作者有太多了解。 More specifically, if A collaborates with B, and B collaborates with C, we don't want modules that use A to know about C. (For example, we don't want a.getB().getC().doSomething();.) 更具体地说,如果A与B合作,而B与C合作,我们不希望使用A的模块了解C。(例如,我们不希望a.getB()。getC()。doSomething( );.)
Second option looks better. 第二种选择看起来更好。 It is classical use of Facade pattern. 这是Facade模式的经典用法。 And it is better, because it hides other functionalities of classes DoesSomthing01
and DoesSomthing02
. 并且更好,因为它隐藏了类DoesSomthing01
和DoesSomthing02
其他功能。 Then you ve'got simplified view of it which is easier to use than 1st option. 然后,您获得了简化视图,它比第一个选项更易于使用。
Edit: there is also one more thing. 编辑:还有另外一件事。 You've got two classes which have the same functionalites and are aggregated by other class. 您有两个具有相同功能的类,并由其他类聚合。 You should consider using Stratey pattern here. 您应该在这里考虑使用Stratey模式 。 The your code will look like this: 您的代码将如下所示:
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;
};
Then you will need only two method instead of four: 然后,您将只需要两个方法,而不是四个:
void doesFunctionality01() { m_doesSomething->functionality01(); }
void doesFunctionality02() { m_doesSomething->functionality02(); }
The first scenario is a violation of law of Demeter, which says that a class can only talk to its immediate friends. 第一种情况是违反了Demeter的法律,Demeter认为一个班级只能与其直接的朋友交谈。 Basically the problem with the first approach is that any change in the inner classes DoSomething01 and DoSomething02 will trigger a change in Class A as well as Class B because both classes are now directly dependent on these inner classes. 基本上,第一种方法的问题是内部类DoSomething01和DoSomething02的任何更改都会触发类A和类B的更改,因为这两个类现在都直接依赖于这些内部类。
The second option is better as it encapsulates the class B from inner classes but a side effect of this solution is that now class A has a lot of methods that does nothing fancy except for delegating to its inner classes. 第二种方法更好,因为它封装了内部类中的类B,但是此解决方案的副作用是,现在类A有很多方法,除了委派给其内部类外别无所求。 This is fine but imagine if DoSomething01 has an inner class DoSomething03 and class B needs to access its functionality without directly knowing about it than the class A would need to have another method that would delegate to DoSomething01 that would in turn delegate to DoSomething03. 这很好,但是可以想象一下,如果DoSomething01具有内部类DoSomething03,并且类B需要在不直接了解其功能的情况下访问其功能,而不是类A需要具有另一个将委托给DoSomething01的方法,该方法又将委托给DoSomething03。 In this case I think it is better to let class B directly know about DoSomething01 otherwise class A is going to have a huge interface that simply delegates to its inner classes. 在这种情况下,我认为最好让B类直接了解DoSomething01,否则A类将具有一个巨大的接口,可以简单地委派给其内部类。
If there are many classes and/or many methods to be called it makes sense to invent an interface in the form of an abstract parent class: 如果要调用许多类和/或许多方法,那么以抽象父类的形式发明一个接口是有意义的:
class SomeInterface
{
public:
SomeInterface(){}
virtual void functionally01() = 0;
virtual void functionally02() = 0;
}
DoesSomthing01 and other classes would then inherit this class: 然后,DosSomthing01和其他类将继承该类:
class DoesSomthing01 : public SomeInterface
and implement the methods. 并实施这些方法。
If it make sense to associate a key with the instantiation of such a class 将键与此类的实例化关联是否有意义
you could store these objects in ClassA eg using a map (here I use an integer as the key): 您可以将这些对象存储在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;
}
};
From ClassB you could then access them 然后,您可以从ClassB访问它们
int somekey = ...;
SomeInterface *myInter = m_classA->getInterface(somekey);
if (myInter)
myInter->functionally01();
This way you have just one access method (getInterface()) independent of the number of objects. 这样,您只有一种访问方法(getInterface()),与对象的数量无关。
In order to encode the access to the methods using a key you could create a map which maps a key onto a closure or a simple switch statement: in SomeInterface: 为了使用键对对方法的访问进行编码,可以创建一个映射,该映射将键映射到闭包或简单的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);
Looks like a good case for a Mediator Pattern. 看起来是中介模式的一个很好的例子。
This pattern manage communication between 2 objects that he owns. 此模式管理他拥有的2个对象之间的通信。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.