本文介绍了如何正确删除/重构一个«朋友»依赖声明?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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





标记为红色,有一个丑陋的«朋友»依赖我想从设计中删除。



总之,为什么我有这个东西:


  1. c

  2. 所以 使用受保护的构造函数扩展,它在继承中有用
    protected ,
    ,因此公开不公开。






  3. 第3步:在实施中共同粘贴所有内容



    在最后一步,如何 ClassAAccessor 可以获得对 InternalInterface 的引用。由于泛化不是公开可见的, ClassAAcessor 无法从构造函数中传递的 ClassA 引用中初始化。但 ClassA 可以访问 InternalInterface ,并使用额外的方法传递引用 setInternalInterfaceRef / code>中引入 ClassAAcessor :








    这里是C ++实现: p>

      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); //内部接口可以在这里只传递
    //,因为它在受保护的作用域中继承了
    //。
    }

    因此,ClassAAccessor的构造函数可以用以下方式重写:

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






    实现甚至更多,通过引入另一个 InternalClientInterface 像这样:








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


    1. 代码更多

    2. 朋友不需要引入抽象接口(可能影响足迹,因此约束3.不完全满足)

    3. protected 泛化关系没有得到UML表示的很好的支持(我必须使用约束)


    The background of this question is based on a practical sample where I wanted to remove a «friend» dependency from a pair of classes that are used to manage read/write locked access to a shared resource.

    Here's an abstraction of the original structural design for that scenario:

    Marked in red, there's this ugly «friend» dependency I want to remove from the design.

    In short, why do I have this thing there:

    1. ClassAProvider shares a reference to a ClassA over a number ofconcurrently accessing Client instances
    2. Client instances should access ClassA solely through the ClassAAccessor helper classthat manages the internals
    3. ClassA hides all methods intended to be used from ClassAAccessor as protected.
    4. So ClassA can ensure that Client needs to use a ClassAAccessor instance

    This pattern comes primarily useful, when it's about ensuring to leave instances of ClassA in adefined state, if a Client operation bails out (because of e.g. an uncaught exception). Think ofClassA providing (internally visible) paired operations like lock()/unlock() or open()/close().

    The (state-)reversing operations should be called in any case, especially when a client crashes dueto an exception.
    This can be safely handled through the ClassAAcessor's life cycle behavior, the destructorimplementation can ensure it.The following sequence diagram illustrates what's the intended behavior:

    Additionally Client instances can achieve a fine control of accessing ClassA easily, just usingC++ scope blocks:

    // ...
    {
        ClassAAccessor acc(provider.getClassA());
        acc.lock();
        // do something exception prone ...
    } // safely unlock() ClassA
    // ...
    

    All fine so far, but the «friend» dependency between ClassA and ClassAAccessor should be removed for a number of good reasons

    1. In the UML 2.2 Superstructure, Section C.2 under Changes from previous UML it says: The following table lists predefined standard elements for UML 1.x that are now obsolete. ... «friend» ...
    2. Most coding rules and guidelines I've seen forbid, or strongly discourage using friend, to avoid the tight dependency from the exporting classes to the friends. This thing brings in some serious maintenance issues.

    As my question title says

    How can I remove/refactor a friend declaration properly (preferably starting out at the UML design for my classes)?

    解决方案

    Let's setup some constraints for refactoring first:

    1. The ClassAAccessor's publicly visible interface should change in no way
    2. The ClassA internal operations should not be visible/accessible from the public
    3. The overall performance and footprint of the original design should not be hurt


    Step 1: Introduce an abstract interface

    For a first shot, I factored out the «friend» stereotype, and replaced it with a class (interface)InternalInterface and the appropriate relations.

    What made up the «friend» dependency, was split up into a simple dependency relation (blue) anda «call» dependency (green) against the new InternalInterface element.


    Step 2: Move the operations, that make up the «call» dependency to the interface

    The next step is to mature the «call» dependency. To do this, I change the diagram as follows:

    • The «call» dependency turned into a directed association fromClassAAccessor to the InternalInterface (I.e. ClassAAccessor containsa private variable internalInterfaceRef).
    • The operations in question were moved from ClassA to InternalInterface.
    • InternalInterface is extended with a protected constructor, that it's useful in inheritanceonly.
    • ClassA's «generalization» association to InternalInterface is marked as protected,so it's made publicly invisible.

    Step 3: Glue everything together in the implementation

    In the final step, we need to model a way how ClassAAccessor can get a reference to InternalInterface. Since the generalization isn't visible publicly, ClassAAcessor can't initialize it from the ClassA reference passed in the constructor anymore. But ClassA can access InternalInterface, and pass a reference using an extra method setInternalInterfaceRef() introduced in ClassAAcessor:


    Here's the C++ implementation:

    class ClassAAccessor {
    public:
        ClassAAccessor(ClassA& classA);
        void setInternalInterfaceRef(InternalInterface & newValue) {
            internalInterfaceRef = &newValue;
        }
    private:
        InternalInterface* internalInterfaceRef;
    };
    

    This one is actually called, when the also newly introduced method ClassA::attachAccessor()method is called:

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

    Thus the constructor of ClassAAccessor can be rewritten in the following way:

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


    Finally you can decouple the implementations even more, by introducing another InternalClientInterface like this:


    It's at least necessary to mention that this approach has some disadvantages vs using friend declarations:

    1. It's complicating the code more
    2. friend doesn't need to introduce abstract interfaces (that may affect the footprint, so constraint 3. isn't fully fulfilled)
    3. The protected generalization relationsip isn't well supported by the UML representation (I had to use that constraint)

    这篇关于如何正确删除/重构一个«朋友»依赖声明?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

05-27 18:05
查看更多