How to Remove/Refactor a «Friend» Dependency Declaration Properly

How can I remove/refactor a «friend» dependency declaration properly?

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:

Sample Image


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)

Remove friend class dependency in the following case

Independent of your friend issue, being required to write this

b.alphaObj->someFunctionOfAlpha();

is not the best design. You should rather call (*):

b.someFunctionOfAlpha();

Now it is also obvious how to remove the friends:

class bar
{
public:
void someFunctionOfAlpha() { alphaObj->someFunctionOfAlpha(); }

private:
alpha *alphaObj;
};

(*) This guideline has a name, I just cannot find it at the moment. In general, calling a method should be like: "Do that!". Calling a method should not be like "Show me your internals so I can do what I want".

Why is a friend class not verified for existence?

A friend class declaration that uses an elaborated type specifier with non-qualified name of the class is actually a declaration of that class. It introduces the class as a member of the enclosing namespace. It does not require it to be pre-declared

class C
{
friend class X; // OK, introduces '::X'
};

But if you use a qualified name in a friend class declaration, it will be subjected to a regular qualified name lookup. And it must refer to a previously declared name

class X {};

class C
{
friend ::X; // OK, refers to '::X'
friend ::Y; // Error, no '::Y' in sight
};

So, if you want your class name to be "verified for existence", use qualified names in friend declarations.

Class templates and friend Classes

If you lookup the syntax for template friends, you'll find the right way to do it:

class A {
template<typename T>
friend class B; // every B<T> is a friend of A

template<typename T>
friend void f(T) {} // every f<T> is a friend of A
};

Although you probably just want to friend the specific one:

friend class BinaryTree<T>;

Two classes with same, standalone, hidden functionality

You can just omit any declarations of foo from any headers, and mark it static or define it in an anonymous namespace.

AB.h

class A { int bar(); };
class B { int baz(); };

AB.cpp

static int foo(int x) { return x+x; }
/* or
namespace {
int foo(int x) { return x+x; }
}
*/


int A::bar() { return foo(1); }
int B::baz() { return foo(2); }


Related Topics



Leave a reply



Submit