[英]How can I remove/refactor a «friend» dependency declaration properly?
The background of this question is based on a practical sample where I wanted to remove a «friend» dependency from a pair of classes that are used to manage read/write locked access to a shared resource. 这个问题的背景是基于一个实际的示例,我想从一对用于管理对共享资源的读/写锁定访问的类中删除«friend»依赖项。
Here's an abstraction of the original structural design for that scenario: 这是该场景的原始结构设计的抽象:
Marked in red, there's this ugly «friend» dependency I want to remove from the design. 标记为红色,我想从设计中删除这个丑陋的“朋友”依赖。
In short, why do I have this thing there: 简而言之,为什么我在那里有这个东西:
ClassAProvider
shares a reference to a ClassA
over a number of concurrently accessing Client
instances ClassAProvider
通过多个并发访问的Client
实例共享对ClassA
的引用 Client
instances should access ClassA
solely through the ClassAAccessor
helper class that manages the internals Client
实例应仅通过管理内部的ClassAAccessor
帮助程序类来访问ClassA
ClassA
hides all methods intended to be used from ClassAAccessor
as protected. ClassA
将所有打算从ClassAAccessor
使用的方法隐藏为受保护。 ClassA
can ensure that Client
needs to use a ClassAAccessor
instance ClassA
可以确保Client
需要使用ClassAAccessor
实例 This pattern comes primarily useful, when it's about ensuring to leave instances of ClassA
in a defined state, if a Client
operation bails out (because of eg an uncaught exception). 如果
Client
操作失效(因为例如未捕获的异常),则此模式主要用于确保将ClassA
实例保留在已定义的状态。 Think of ClassA
providing (internally visible) paired operations like lock()
/ unlock()
or open()
/ close()
. 想想
ClassA
提供(内部可见)配对操作,如lock()
/ unlock()
或open()
/ close()
。
The (state-)reversing operations should be called in any case, especially when a client crashes due to an exception. 在任何情况下都应调用(state-)反转操作,尤其是当客户端由于异常而崩溃时。
This can be safely handled through the ClassAAcessor
's life cycle behavior, the destructor implementation can ensure it. 这可以通过
ClassAAcessor
的生命周期行为安全地处理,析构函数实现可以确保它。 The following sequence diagram illustrates what's the intended behavior: 以下序列图说明了预期的行为:
Additionally Client
instances can achieve a fine control of accessing ClassA
easily, just using C++ scope blocks: 此外,只需使用C ++范围块,
Client
实例就可以轻松实现对访问ClassA
的精细控制:
// ...
{
ClassAAccessor acc(provider.getClassA());
acc.lock();
// do something exception prone ...
} // safely unlock() ClassA
// ...
All fine so far, but the «friend» dependency between ClassA
and ClassAAccessor
should be removed for a number of good reasons 到目前为止一切正常,但
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» ...
The following table lists predefined standard elements for UML 1.x that are now obsolete. ... «friend» ...
As my question title says 正如我的问题标题所说
How can I remove/refactor a friend declaration properly (preferably starting out at the UML design for my classes)? 如何正确删除/重构朋友声明(最好从我的课程的UML设计开始)?
Let's setup some constraints for refactoring first: 让我们首先为重构设置一些约束:
Step 1: Introduce an abstract interface 第1步:介绍一个抽象接口
For a first shot, I factored out the «friend» stereotype, and replaced it with a class (interface) InternalInterface
and the appropriate relations. 对于第一个镜头,我考虑了«friend»构造型,并将其替换为类(接口)
InternalInterface
和相应的关系。
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. 构成«friend»依赖关系的内容被分成了一个简单的依赖关系(蓝色)和一个«call»依赖关系(绿色)对新的
InternalInterface
元素。
Step 2: Move the operations, that make up the «call» dependency to the interface 步骤2:将构成«call»依赖关系的操作移动到接口
The next step is to mature the «call» dependency. 下一步是成熟«call»依赖。 To do this, I change the diagram as follows:
为此,我按如下方式更改图表:
ClassAAccessor
to the InternalInterface
(Ie ClassAAccessor
contains a private variable internalInterfaceRef
). ClassAAccessor
到InternalInterface
有向关联(即ClassAAccessor
包含私有变量internalInterfaceRef
)。 ClassA
to InternalInterface
. ClassA
移到了InternalInterface
。 InternalInterface
is extended with a protected constructor, that it's useful in inheritance only. InternalInterface
使用受保护的构造函数进行扩展,它仅在继承中有用。 ClassA
's «generalization» association to InternalInterface
is marked as protected
, so it's made publicly invisible. ClassA
与InternalInterface
的“泛化”关联被标记为protected
,因此它被公开隐藏。 Step 3: Glue everything together in the implementation 第3步:在实现中将所有内容粘合在一起
In the final step, we need to model a way how ClassAAccessor
can get a reference to InternalInterface
. 在最后一步中,我们需要为
ClassAAccessor
如何获取对InternalInterface
的引用ClassAAccessor
。 Since the generalization isn't visible publicly, ClassAAcessor
can't initialize it from the ClassA
reference passed in the constructor anymore. 由于泛化不公开,因此
ClassAAcessor
不能再从构造函数中传递的ClassA
引用初始化它。 But ClassA
can access InternalInterface
, and pass a reference using an extra method setInternalInterfaceRef()
introduced in ClassAAcessor
: 但是
ClassA
可以访问InternalInterface
,并使用ClassAAcessor
引入的额外方法setInternalInterfaceRef()
传递引用:
Here's the C++ implementation: 这是C ++实现:
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: 当调用新引入的方法
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.
}
Thus the constructor of ClassAAccessor can be rewritten in the following way: 因此,ClassAAccessor的构造函数可以通过以下方式重写:
ClassAAccessor::ClassAAccessor(ClassA& classA)
: internalInterfaceRef(0) {
classA.attachAccessor(*this);
}
Finally you can decouple the implementations even more, by introducing another InternalClientInterface
like this: 最后,通过引入另一个类似于此的
InternalClientInterface
,您可以进一步解耦实现:
It's at least necessary to mention that this approach has some disadvantages vs using friend
declarations: 至少有必要提一下,这种方法与使用
friend
声明相比有一些缺点:
friend
doesn't need to introduce abstract interfaces (that may affect the footprint, so constraint 3. isn't fully fulfilled) friend
不需要引入抽象接口(可能会影响足迹,因此约束3.未完全实现) protected
generalization relationsip isn't well supported by the UML representation (I had to use that constraint) protected
泛化关系(我不得不使用该约束) Dependency say nothing about accessing of attributes or operations. 依赖性对访问属性或操作一无所知。 Dependency is used to represent definition dependency between model elements !
依赖关系用于表示模型元素之间的定义依赖关系! What about to remove all dependencies from your model and learn how to use visibility.
如何从模型中删除所有依赖项并学习如何使用可见性。 If your friend relationship represents accessing of feature (attribute or operation) from specific type (class), you can set visibility of attribute or operation to Package.
如果您的朋友关系表示从特定类型(类)访问功能(属性或操作),则可以将属性或操作的可见性设置为Package。 Package visibility means, that attribute value is accessible from instances which classes are defined in the same package.
包可见性意味着可以从在同一包中定义的类的实例访问该属性值。
Define ClassAProvider and Client in same package and set classA attribute visibility to Package visibility type. 在同一个包中定义ClassAProvider和Client,并将classA属性可见性设置为Package可见性类型。 Client instance can read classA attribute value, but instances of other types not defined in the same package cannot.
客户端实例可以读取classA属性值,但是在同一个包中未定义的其他类型的实例不能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.