14

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:

Original Design using friend

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 of concurrently accessing Client instances
  2. Client instances should access ClassA solely through the ClassAAccessor helper class that 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 a defined state, if a Client operation bails out (because of e.g. an uncaught exception). Think of ClassA 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 due to an exception.
This can be safely handled through the ClassAAcessor's life cycle behavior, the destructor implementation can ensure it. The following sequence diagram illustrates what's the intended behavior:

The desired behavior of the overall construct

Additionally Client instances can achieve a fine control of accessing ClassA easily, just using C++ 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)?

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
  • completely off-topic but which uml tool did you use? – midor Dec 15 '14 at 20:09
  • I have setup this Q&A motivated by this question: [C++ OOP Only grant access to certain classes](http://stackoverflow.com/questions/27487362/c-oop-only-grant-access-to-certain-classes). That's the essence how to refactor a friend relationship, that I once wrote up in an article, and now provided as a Q&A here. – πάντα ῥεῖ Dec 15 '14 at 20:10
  • @midor [tag:enterprise-architect] originally. The diagram images posted here are screenshots from a PDF I have actually. – πάντα ῥεῖ Dec 15 '14 at 20:10
  • @πάντα ῥεῖ Don't try to force your code to the latest UML changes. UML is a good tool, but, it was originally designed "too tied" to Java, and eventually more flexible for other P.L. (s) . Some features of UML, either new or deprecated, are very conceptual to be applied to source code. "Friend" ("Package" in Java) is useful feature, maybe should be "retagged" in UML, but is not wrong to use it. – umlcat Dec 16 '14 at 23:25
  • @umlcat _"Don't try to force your code to the latest UML changes."_ I'm not doing so actually. My concerns are about c++ design primarily. The _friend_ relationship was discouraged in c++ designs (for the mentioned reasons) long before UML declared it obsolete. My point about using UML, is just to point out which changes need to be done in a particular order (or scheme) from a structural POV. – πάντα ῥεῖ Dec 17 '14 at 00:13
  • @πάντα ῥεῖ I ignore `friend` access was discouraged in C++, but, for me is a good feature, than can be applied well or bad. It's supported in several P.L., maybe with different syntax (Java, Object Pascal, Visual Basic, ...), maybe `interface` access could be a suitable replacement – umlcat Dec 17 '14 at 00:25
  • @umlcat Though this question is about c++ actually :-P ... – πάντα ῥεῖ Dec 17 '14 at 00:28

2 Answers2

7

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.

1st shot refactoring

What made up the «friend» dependency, was split up into a simple dependency relation (blue) and a «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:

Matured design

  • The «call» dependency turned into a directed association from ClassAAccessor to the InternalInterface (I.e. ClassAAccessor contains a 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 inheritance only.
  • 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:

Glue everything together


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:

enter image description here


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)
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
0

Dependency say nothing about accessing of attributes or operations. Dependency is used to represent definition dependency between model elements ! What about to remove all dependencies from your model and learn how to use visibility. If your friend relationship represents accessing of feature (attribute or operation) from specific type (class), you can set visibility of attribute or operation to Package. Package visibility means, that attribute value is accessible from instances which classes are defined in the same package.

Define ClassAProvider and Client in same package and set classA attribute visibility to Package visibility type. Client instance can read classA attribute value, but instances of other types not defined in the same package cannot.

Vladimir
  • 2,066
  • 15
  • 13
  • There's no such thing like _package visibility_ (by means of namespaces) available for [tag:c++] (compared to [tag:c#] or other languages). Your proposal is welcome but doesn't help for my actual case, that is concsiously bound to c++ solutions. – πάντα ῥεῖ Dec 16 '14 at 20:38
  • Ok , but visibility of package type in UML is equivalent to friend declaration i C++ – Vladimir Dec 17 '14 at 05:43
  • Well, `friend` may be a correct implementation of package visibility, and the current language standard supports this better regarding inline definition support of free standing `friend` functions from the namespace. For classes though, that introduces that _maintenance nightmare_ I've mentioned in my question (see reason 2.). When I'm sure what I'm doing, and can be pretty sure the maintenance issue is unlikely to arise, I also well ignore that «friend» relationships are obsolete in UML. I just wanted to show a way how it can be refactored in a proper, equivalent (by access policies) way. – πάντα ῥεῖ Dec 17 '14 at 17:44