简体   繁体   English

组成时可进入“内部”课程

[英]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: 如果我现在拥有一个“知道” ClassAClassB ,并且必须使用/执行DoesSomething01和/或DoesSomething02类的functionality01和/或functionality02 DoesSomething01DoesSomething02我会看到两种可能性:

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添加(在这种情况下为四个)方法,该方法将方法调用转发给DoesSomething01DoesSomething02如下所示:

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 . 并且更好,因为它隐藏了类DoesSomthing01DoesSomthing02其他功能。 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM