How can I remove/refactor a «friend» dependency declaration properly?
Let's setup some constraints for refactoring first:
- The ClassAAccessor's publicly visible interface should change in no way
- The ClassA internal operations should not be visible/accessible from the public
- 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) 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:
- The «call» dependency turned into a directed association from
ClassAAccessor
to theInternalInterface
(I.e.ClassAAccessor
contains
a private variableinternalInterfaceRef
). - The operations in question were moved from
ClassA
toInternalInterface
. InternalInterface
is extended with a protected constructor, that it's useful in inheritance
only.ClassA
's «generalization» association toInternalInterface
is marked asprotected
,
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:
- It's complicating the code more
friend
doesn't need to introduce abstract interfaces (that may affect the footprint, so constraint 3. isn't fully fulfilled)- 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
Is the 'Override' Keyword Just a Check For a Overridden Virtual Method
Can Any One Provide Me a Sample of Singleton in C++
Understanding of Pthread_Cond_Wait() and Pthread_Cond_Signal()
Should I Use an Exception Specifier in C++
Looking For C++ Stl-Like Vector Class But Using Stack Storage
How to Detect Unnecessary #Include Files in a Large C++ Project
C++ Custom Stream Manipulator That Changes Next Item on Stream
Why Is the Size of an Empty Class in C++ Not Zero
Difference Between a Pointer and Reference Parameter
C++, _Try and Try/Catch/Finally
How to Parse a Url in C++ Cross Platform
Order of Member Constructor and Destructor Calls
Alternative Virtual Function Calls Implementations
How to Code a Modulo (%) Operator in C/C++/Obj-C That Handles Negative Numbers