此问题的背景基于一个实际示例,我想从一对用于管理对共享资源的读/写锁定访问的类中删除«friend»依赖项。
以下是该场景的原始结构设计的抽象:
标记为红色,我想从设计中删除这个丑陋的“朋友”依赖。
简而言之,为什么我在那里有这个东西:
ClassAProvider
分享了对ClassA
的引用
同时访问Client
个实例Client
个实例应仅通过ClassA
帮助程序类访问ClassAAccessor
管理内部的ClassA
隐藏了ClassAAccessor
作为受保护使用的所有方法。ClassA
可以确保Client
需要使用ClassAAccessor
实例这种模式主要用于确保将ClassA
的实例留在a中
定义状态,如果Client
操作失效(因为例如未捕获的异常)。考虑到
ClassA
提供(内部可见)配对操作,例如lock()
/ unlock()
或open()
/ close()
。
在任何情况下都应调用(state-)反转操作,尤其是当客户端崩溃时
例外。
这可以通过ClassAAcessor
的生命周期行为(析构函数)安全地处理
实施可以确保它。
以下序列图说明了预期的行为:
此外,Client
个实例可以轻松实现对ClassA
的轻松访问,只需使用即可
C ++范围块:
// ...
{
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» ...
正如我的问题标题所说
如何正确删除/重构朋友声明(最好从我的课程的UML设计开始)?
答案 0 :(得分:7)
让我们首先为重构设置一些约束:
第1步:介绍抽象界面
对于第一次拍摄,我将“朋友”刻板印象排除在外,并将其替换为类(界面)
InternalInterface
和适当的关系。
构成«朋友»依赖关系的东西被分成了一个简单的依赖关系(蓝色)和
对新InternalInterface
元素的«call»依赖(绿色)。
步骤2:将构成«call»依赖关系的操作移动到界面
下一步是成熟«call»依赖。为此,我按如下方式更改图表:
ClassAAccessor
InternalInterface
{I ClassAAccessor
包含internalInterfaceRef
私有变量ClassA
)。InternalInterface
移至InternalInterface
。ClassA
使用受保护的构造函数进行扩展,它在继承中很有用
仅InternalInterface
与protected
的«泛化»关联标记为ClassAAccessor
,
所以它是公开隐形的。第3步:在实施中将所有内容粘贴在一起
在最后一步中,我们需要为InternalInterface
获取ClassAAcessor
引用的方式建模。由于泛化不公开,ClassA
无法再从构造函数中传递的ClassA
引用初始化它。但是InternalInterface
可以访问setInternalInterfaceRef()
,并使用ClassAAcessor
中引入的额外方法class ClassAAccessor {
public:
ClassAAccessor(ClassA& classA);
void setInternalInterfaceRef(InternalInterface & newValue) {
internalInterfaceRef = &newValue;
}
private:
InternalInterface* internalInterfaceRef;
};
传递引用:
这是C ++实现:
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(ClassA& classA)
: internalInterfaceRef(0) {
classA.attachAccessor(*this);
}
因此,ClassAAccessor的构造函数可以通过以下方式重写:
InternalClientInterface
最后,您可以通过引入另一个friend
来解决实现问题:
至少有必要提一下,与使用friend
声明相比,这种方法有一些缺点:
protected
不需要引入抽象接口(可能会影响足迹,因此约束3.未完全实现)答案 1 :(得分:0)
依赖性对访问属性或操作一无所知。依赖关系用于表示模型元素之间的定义依赖关系! 如何从模型中删除所有依赖项并学习如何使用可见性。 如果您的朋友关系表示从特定类型(类)访问功能(属性或操作),则可以将属性或操作的可见性设置为Package。包可见性意味着可以从在同一包中定义的类的实例访问该属性值。
在同一个包中定义ClassAProvider和Client,并将classA属性可见性设置为Package可见性类型。客户端实例可以读取classA属性值,但是在同一个包中未定义的其他类型的实例不能。