这个问题的背景是基于一个实际示例,我想从一对用于管理对共享资源的读/写锁定访问的类中删除“ friend ”依赖项。
这是该场景的原始结构设计的抽象:
标记为红色,我想从设计中删除这个丑陋的“ friend ”依赖项。
总之,我为什么会有这个东西:
ClassAProvider
共享一个对ClassA
的引用。 同时访问Client
实例Client
实例应仅通过ClassAAccessor
辅助类访问ClassA
管理内部的ClassA
将ClassAAccessor
中打算使用的所有方法隐藏为 protected 。- 所以
ClassA
可以保证Client
需要使用一个ClassAAccessor
实例
这种模式主要用于确保将 ClassA
的实例留在
定义的状态,如果 Client
操作退出(例如因为未捕获的异常)。考虑到
ClassA
提供(内部可见的)配对操作,例如 lock()
/unlock()
或 open()
/close()
.
在任何情况下都应该调用(状态)反转操作,尤其是当客户端由于崩溃而崩溃时
异常(exception)。
这可以通过 ClassAAcessor
的生命周期行为、析构函数安全地处理
实现可以保证。
以下序列图说明了预期的行为:
另外,Client
实例可以轻松实现对ClassA
的精细控制,只需使用
C++ 范围 block :
// ...
{
ClassAAccessor acc(provider.getClassA());
acc.lock();
// do something exception prone ...
} // safely unlock() ClassA
// ...
到目前为止一切都很好,但是出于多种原因应该删除 ClassA
和 ClassAAccessor
之间的 «friend» 依赖关系
- 在 UML 2.2 上层结构的 C.2 节中,从以前的 UML 的变化中说:
下表列出了现在已过时的 UML 1.x 的预定义标准元素。 ... « friend » ...
- 我见过的大多数编码规则和指南都禁止或强烈反对使用 friend ,以避免导出类对 friend 的紧密依赖。这会带来一些严重的维护问题。
正如我的问题标题所说
如何正确删除/重构友元声明(最好从我的类的 UML 设计开始)?
最佳答案
让我们首先为重构设置一些约束:
- ClassAAccessor 的公开可见界面不得更改
- ClassA 内部操作不应被公众看到/访问
- 不应损害原始设计的整体性能和占用空间
第 1 步:引入抽象接口(interface)
在第一次尝试中,我排除了“ friend ”的刻板印象,并将其替换为一个类(接口(interface))
InternalInterface
和相应的关系。
构成 «friend» 依赖的内容被拆分为一个简单的依赖关系(蓝色)和
对新 InternalInterface
元素的 «call» 依赖项(绿色)。
第 2 步:将构成 «call» 依赖项的操作移至接口(interface)
下一步是完善 «call» 依赖项。为此,我将图表更改如下:
- «call»依赖变成了一个定向关联
ClassAAccessor
到InternalInterface
(即ClassAAccessor
包含 一个私有(private)变量internalInterfaceRef
)。 - 相关操作已从
ClassA
移至InternalInterface
。 InternalInterface
使用 protected 构造函数进行扩展,它在继承中很有用 仅限。ClassA
与InternalInterface
的 «generalization» 关联被标记为protected
, 所以它是公开不可见的。
第 3 步:在实现中将所有内容粘合在一起
在最后一步,我们需要对 ClassAAccessor
如何获得对 InternalInterface
的引用进行建模。由于泛化不是公开可见的,ClassAAcessor
不能再从构造函数中传递的 ClassA
引用初始化它。但是 ClassA
可以访问 InternalInterface
,并使用 ClassAAcessor
中引入的额外方法 setInternalInterfaceRef()
传递引用:
这是 C++ 实现:
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's inherited
// in the protected scope.
}
因此 ClassAAccessor 的构造函数可以改写如下:
ClassAAccessor::ClassAAccessor(ClassA& classA)
: internalInterfaceRef(0) {
classA.attachAccessor(*this);
}
最后,您可以通过引入另一个 InternalClientInterface
来进一步解耦实现,如下所示:
至少有必要提到,这种方法与使用 friend
声明相比有一些缺点:
- 代码更加复杂
friend
不需要引入抽象接口(interface)(这可能会影响足迹,因此约束 3. 没有完全满足)- UML 表示不能很好地支持
protected
泛化关系(我不得不使用该约束)
关于c++ - 如何正确删除/重构«friend»依赖声明?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27492132/