Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/124.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/tfs/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何删除/重构a«;朋友»;依赖项声明是否正确?_C++_Refactoring_Friend - Fatal编程技术网

C++ 如何删除/重构a«;朋友»;依赖项声明是否正确?

C++ 如何删除/重构a«;朋友»;依赖项声明是否正确?,c++,refactoring,friend,C++,Refactoring,Friend,这个问题的背景是基于一个实际示例,我想从一对用于管理对共享资源的读/写锁定访问的类中删除一个«friend»依赖项 以下是该场景的原始结构设计的抽象: 用红色标记,我想从设计中删除这个丑陋的«friend»依赖项 简言之,我为什么会有这样的东西: ClassA提供者在多个域上共享对ClassA的引用 并发访问客户端实例 客户端实例应仅通过ClassA接受器helper类访问ClassA 管理内部的 ClassA将所有拟用于受保护的ClassA Accessor的方法隐藏起来 因此ClassA可

这个问题的背景是基于一个实际示例,我想从一对用于管理对共享资源的读/写锁定访问的类中删除一个«friend»依赖项

以下是该场景的原始结构设计的抽象:

用红色标记,我想从设计中删除这个丑陋的«friend»依赖项

简言之,我为什么会有这样的东西:

  • ClassA提供者
    在多个域上共享对
    ClassA
    的引用 并发访问
    客户端
    实例
  • 客户端
    实例应仅通过
    ClassA接受器
    helper类访问
    ClassA
    管理内部的
  • ClassA
    将所有拟用于受保护的
    ClassA Accessor
    的方法隐藏起来
  • 因此
    ClassA
    可以确保
    Client
    需要使用
    ClassA accessor
    实例
  • 当需要确保将
    ClassA
    的实例保留在 定义状态,如果
    客户端
    操作退出(例如,由于未捕获的异常)。想想
    
    ClassA
    提供(内部可见)成对操作,如
    lock()
    /
    unlock()
    open()
    /
    close()

    在任何情况下都应该调用(状态)反转操作,尤其是当客户端由于错误而崩溃时 例外情况。
    这可以通过
    classa访问器的生命周期行为(析构函数)安全地处理
    实施可以确保这一点。
    以下序列图说明了预期的行为:

    另外,只要使用 C++作用域块:

    // ...
    { 
        ClassAAccessor acc(provider.getClassA());
        acc.lock();
        // do something exception prone ...
    } // safely unlock() ClassA
    // ...
    
    到目前为止一切正常,但是出于一些好的原因,应该删除
    ClassA
    ClassA接受者之间的«friend»依赖关系

  • 在UML2.2上层结构中,C.2节“对以前的UML的更改”中说:
    下表列出了UML1.x的预定义标准元素,这些元素现在已经过时了。。。«朋友»…
  • 我见过的大多数编码规则和指导原则都禁止或强烈反对使用friend,以避免将类导出到friends时的紧密依赖性。这会带来一些严重的维护问题
  • 正如我的问题标题所说


    如何正确删除/重构好友声明(最好从类的UML设计开始)?让我们先设置一些重构约束:

  • ClassaaAccessor的公共可见接口不应以任何方式更改
  • ClassA内部操作不应在公众中可见/可访问
  • 不应损害原始设计的整体性能和占地面积

  • 步骤1:引入抽象接口

    第一步,我分解了«friend»原型,并用一个类(接口)替换它
    内部接口
    和适当的关系

    构成«friend»依赖关系的内容被分解为一个简单的依赖关系(蓝色)和 针对新
    InternalInterface
    元素的«调用»依赖项(绿色)


    步骤2:将构成«调用»依赖项的操作移动到接口

    下一步是使«调用»依赖关系成熟。为此,我将图表更改如下:

    • «调用»依赖项从转换为定向关联
      ClassAAccessor
      内部接口
      (即
      ClassAAccessor
      包含 一个私有变量
      internalInterfaceRef
    • 相关操作已从
      ClassA
      移动到
      InternalInterface
    • InternalInterface
      通过受保护的构造函数进行扩展,在继承中非常有用 只是
    • ClassA
      内部接口的«泛化»关联
      标记为
      受保护
      , 所以它被公开化了

    步骤3:将实施中的所有内容粘合在一起

    在最后一步中,我们需要建模
    ClassAAccessor
    如何获得对
    InternalInterface
    的引用。由于泛化不公开可见,
    ClassA访问器
    无法再从构造函数中传递的
    ClassA
    引用对其进行初始化。但是
    ClassA
    可以访问
    InternalInterface
    ,并使用
    ClassA访问程序中引入的额外方法
    setInternalInterfaceRef()
    传递引用:


    这是C++实现:

    class ClassAAccessor {
    public:
        ClassAAccessor(ClassA& classA);
        void setInternalInterfaceRef(InternalInterface & newValue) {
            internalInterfaceRef = &newValue;
        }
    private:  
        InternalInterface* internalInterfaceRef;
    };
    
    当新引入的方法
    ClassA::attachAccessor()
    方法被称为:

    class ClassA : protected InternalInterface {
    public:
        // ...
        attachAccessor(ClassAAccessor & accessor);
        // ...
    };
    
    ClassA::attachAccessor(ClassAAccessor & accessor) {
        accessor.setInternalInterfaceRef(*this); // The internal interface can be handed
                                                 // out here only, since it's inherited 
                                                 // in the protected scope.
    }
    
    因此,ClassA Accessor的构造函数可以按以下方式重写:

    ClassAAccessor::ClassAAccessor(ClassA& classA)
    : internalInterfaceRef(0) {
        classA.attachAccessor(*this);
    }
    

    最后,通过引入另一个
    InternalClientInterface
    ,您可以进一步解耦实现:


    至少有必要提到,与使用
    friend
    声明相比,这种方法有一些缺点:

  • 这使代码更加复杂
  • friend
    不需要引入抽象接口(这可能会影响封装外形,因此约束3.未完全实现)
  • protected
    泛化关系SIP没有得到UML表示的很好支持(我不得不使用该约束)

  • 依赖关系不涉及属性或操作的访问。依赖关系用于表示模型元素之间的定义依赖关系! 如何从您的系统中删除所有依赖项