[英]How can I remove/refactor a «friend» dependency declaration properly?
这个问题的背景是基于一个实际的示例,我想从一对用于管理对共享资源的读/写锁定访问的类中删除«friend»依赖项。
这是该场景的原始结构设计的抽象:
标记为红色,我想从设计中删除这个丑陋的“朋友”依赖。
简而言之,为什么我在那里有这个东西:
ClassAProvider
通过多个并发访问的Client
实例共享对ClassA
的引用 Client
实例应仅通过管理内部的ClassAAccessor
帮助程序类来访问ClassA
ClassA
将所有打算从ClassAAccessor
使用的方法隐藏为受保护。 ClassA
可以确保Client
需要使用ClassAAccessor
实例 如果Client
操作失效(因为例如未捕获的异常),则此模式主要用于确保将ClassA
实例保留在已定义的状态。 想想ClassA
提供(内部可见)配对操作,如lock()
/ unlock()
或open()
/ close()
。
在任何情况下都应调用(state-)反转操作,尤其是当客户端由于异常而崩溃时。
这可以通过ClassAAcessor
的生命周期行为安全地处理,析构函数实现可以确保它。 以下序列图说明了预期的行为:
此外,只需使用C ++范围块, Client
实例就可以轻松实现对访问ClassA
的精细控制:
// ...
{
ClassAAccessor acc(provider.getClassA());
acc.lock();
// do something exception prone ...
} // safely unlock() ClassA
// ...
到目前为止一切正常,但ClassA
和ClassAAccessor
之间的“朋友”依赖应该被删除,原因很多
The following table lists predefined standard elements for UML 1.x that are now obsolete. ... «friend» ...
The following table lists predefined standard elements for UML 1.x that are now obsolete. ... «friend» ...
正如我的问题标题所说
如何正确删除/重构朋友声明(最好从我的课程的UML设计开始)?
让我们首先为重构设置一些约束:
第1步:介绍一个抽象接口
对于第一个镜头,我考虑了«friend»构造型,并将其替换为类(接口) InternalInterface
和相应的关系。
构成«friend»依赖关系的内容被分成了一个简单的依赖关系(蓝色)和一个«call»依赖关系(绿色)对新的InternalInterface
元素。
步骤2:将构成«call»依赖关系的操作移动到接口
下一步是成熟«call»依赖。 为此,我按如下方式更改图表:
ClassAAccessor
到InternalInterface
有向关联(即ClassAAccessor
包含私有变量internalInterfaceRef
)。 ClassA
移到了InternalInterface
。 InternalInterface
使用受保护的构造函数进行扩展,它仅在继承中有用。 ClassA
与InternalInterface
的“泛化”关联被标记为protected
,因此它被公开隐藏。 第3步:在实现中将所有内容粘合在一起
在最后一步中,我们需要为ClassAAccessor
如何获取对InternalInterface
的引用ClassAAccessor
。 由于泛化不公开,因此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
不需要引入抽象接口(可能会影响足迹,因此约束3.未完全实现) protected
泛化关系(我不得不使用该约束) 依赖性对访问属性或操作一无所知。 依赖关系用于表示模型元素之间的定义依赖关系! 如何从模型中删除所有依赖项并学习如何使用可见性。 如果您的朋友关系表示从特定类型(类)访问功能(属性或操作),则可以将属性或操作的可见性设置为Package。 包可见性意味着可以从在同一包中定义的类的实例访问该属性值。
在同一个包中定义ClassAProvider和Client,并将classA属性可见性设置为Package可见性类型。 客户端实例可以读取classA属性值,但是在同一个包中未定义的其他类型的实例不能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.