Ответ 1
Сначала установите некоторые ограничения для рефакторинга:
- Открытый доступ к общедоступному интерфейсу ClassAAccessor не должен
- Внутренние операции ClassA не должны быть видимыми/доступными из общедоступных
- Общая производительность и площадь оригинального дизайна не должны быть повреждены.
Шаг 1: Представьте абстрактный интерфейс
Для первого снимка я угадал стереотип "друга" и заменил его классом (интерфейсом)
InternalInterface
и соответствующие отношения.
Что составляло зависимость "friend", было разделено на простое отношение зависимостей (синее) и
зависимость "вызова" (зеленая) от нового элемента InternalInterface
.
Шаг 2: Переместите операции, которые составляют зависимость "вызова" к интерфейсу
Следующий шаг - созреть зависимость "звонок". Для этого я меняю диаграмму следующим образом:
- Зависимость "call" превратилась в направленную ассоциацию из
ClassAAccessor
кInternalInterface
(I.e.ClassAAccessor
содержит частная переменнаяinternalInterfaceRef
). - Операции, о которых идет речь, были перенесены с
ClassA
наInternalInterface
. -
InternalInterface
расширен с помощью защищенного конструктора, что он полезен в наследовании только. -
ClassA
Связь "обобщения" сInternalInterface
отмечена какprotected
, поэтому он стал невидимым.
Шаг 3: склейте все вместе в реализации
На последнем этапе нам нужно смоделировать способ, которым ClassAAccessor
может получить ссылку на InternalInterface
. Поскольку обобщение не видно публично, ClassAAcessor
не может инициализировать его из ссылки ClassA
, переданной в конструкторе. Но ClassA
может получить доступ к InternalInterface
и передать ссылку с помощью дополнительного метода setInternalInterfaceRef()
, представленного в ClassAAcessor
:
Здесь реализация С++:
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); // The internal interface can be handed
// out here only, since it inherited
// in the protected scope.
}
Таким образом, конструктор ClassAAccessor можно переписать следующим образом:
ClassAAccessor::ClassAAccessor(ClassA& classA)
: internalInterfaceRef(0) {
classA.attachAccessor(*this);
}
Наконец, вы можете еще больше отделить реализации, введя еще один InternalClientInterface
следующим образом:
По крайней мере, необходимо упомянуть, что этот подход имеет некоторые недостатки против использования объявлений friend
:
- Это усложняет код больше
-
friend
не нужно вводить абстрактные интерфейсы (которые могут повлиять на след, поэтому ограничение 3. не полностью выполнено) - Сопровождение обобщения
protected
недостаточно хорошо поддерживается представлением UML (мне пришлось использовать это ограничение)