在作曲的情况下访问'inner'类

Access to 'inner' classes in case of composition

本文关键字:inner 访问 情况下      更新时间:2023-10-16

我将某些功能封装在类中,然后在另一个类中使用。我想这叫做构图。

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;
};

如果我现在有一个"知道"ClassAClassB,并且必须使用/执行DoesSomething01和/或DoesSomething02类的functionality01和/或functionality02,我看到两种可能性:

a)给ClassA添加这样的方法,使ClassB可以直接访问DoesSomething01和/或DoesSomething02:
DoesSomething01 *getDoesSomething01() { return *m_doesSomething01; }
DoesSomething02 *getDoesSomething02() { return *m_doesSomething02; }

ClassB可以这样做:

m_classA->getDoesSomething01()->functionality01();

b)为ClassA添加(在本例中为四个)方法,将方法调用转发给DoesSomething01DoesSomething02,如下所示:

void doesSomething01Functionality01() { m_doesSomething01->functionality01(); }
void doesSomething01Functionality02() { m_doesSomething01->functionality02(); }
void doesSomething02Functionality01() { m_doesSomething02->functionality01(); }
void doesSomething02Functionality02() { m_doesSomething02->functionality02(); }

哪个选项更好,为什么?

每个选项的优缺点是什么?

第一个选项可以被认为是代码气味。根据Robert C. Martin的"Clean Code",这是"传递式导航",应该避免。引用作者:

一般来说,我们不希望单个模块知道太多关于它的信息合作者。更具体地说,如果A与B合作,B与C协作,我们不希望使用A的模块知道C。(例如,我们不希望a.getB(). getc (). dosomething ();)

第二个选项看起来更好。它是立面图案的经典使用。而且它更好,因为它隐藏了DoesSomthing01DoesSomthing02类的其他功能。这样你就得到了简化的视图这比第一个选项更容易使用。

编辑:还有一件事。您有两个具有相同功能的类,并且由其他类聚合。您应该考虑在这里使用策略模式。您的代码将看起来像这样:
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(); }

第一个场景违反了得墨忒耳定律,该定律规定一个类只能与它的直接好友通信。基本上,第一种方法的问题是,内部类DoSomething01和DoSomething02的任何变化都会触发类a和类B的变化,因为两个类现在都直接依赖于这些内部类。

第二个选项更好,因为它从内部类封装了类B,但这个解决方案的一个副作用是,现在类a有很多方法,除了委托给它的内部类之外,没有什么特别的功能。这很好,但是想象一下,如果DoSomething01有一个内部类DoSomething03,而类B需要在不直接知道它的情况下访问它的功能,那么类A将需要另一个方法来委托给DoSomething01,而DoSomething01又会委托给DoSomething03。在这种情况下,我认为最好让类B直接知道DoSomething01,否则类A将有一个巨大的接口,只是委托给它的内部类。

如果有许多类和/或许多方法需要调用,那么发明是有意义的抽象父类形式的接口:

class SomeInterface
{
    public:
    SomeInterface(){}
    virtual void functionally01() = 0;
    virtual void functionally02() = 0;
}

DoesSomthing01和其他类将继承这个类:

class DoesSomthing01 : public SomeInterface

并实现方法

如果将一个键与这样一个类的实例化相关联是有意义的
你可以将这些对象存储在类a中,例如使用map(这里是I使用整数作为键):

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())对象的个数

为了使用键对方法的访问进行编码,您可以创建一个map,将键映射到闭包或简单的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);

看起来是一个很好的中介模式。

这个模式管理他拥有的两个对象之间的通信