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)