UML关系 - 虚线 vs 实线

90

这两个关系之间有什么区别?

在此输入图片描述

编辑:如果您能提供一个简单的代码示例来说明区别,那将非常有帮助!

6个回答

61

我试图给出两种类型线的简单例子。

在第一个图中,实线表示关联:

PlantUML directed association 图

如果这些类在Java中声明,那么就像 ClassAClassB 存储为属性引用(可以通过构造函数传递、创建等方式)。例如:

public class ClassA {
    ClassB theClassB = ...
    ...
}
在第二个图表中,展示了一种依赖关系: 依赖关系的PlantUML图表 依赖关系比关联关系要弱得多。引用自《UML Distilled》:
“在类中,存在各种各样的依赖关系:一个类向另一个类发送消息;一个类将另一个类作为其数据的一部分;一个类将另一个类作为操作的参数进行提及。 [...] 每当您想要显示如何更改一个元素可能会改变其他元素时,就可以使用依赖关系。”
在Java中,还有几个例子:将类型为ClassB的参数传递给方法,或者方法声明类型为ClassB的局部变量:
public class ClassA {
    ...
    public void someMethod(ClassB arg1) {...}
    ...
    public void someOtherMethod() {
        ClassB localReferenceToClassB = ...
    }
    ...
}

以下是ClassA未使用关联关系而依赖于ClassB的其他方法(不是详尽列表):

  • ClassB具有一个静态方法,ClassA调用该方法
  • ClassA捕获类型为ClassB的异常
  • 每当修改ClassB时,ClassA也必须进行修改(例如,某些逻辑被共享)

1
从你的回答中,我认为这两个想法可以通过 ClassB 对象的范围进行区分:对于关联,它具有类级别的作用域,而另一个仅具有方法级别的作用域。 - VimNing

27

你的问题给了我一个学习自己的好机会,下面是我发现的 -

enter image description here

关联(Association):拥有另一种类型的所有权(例如,“A”拥有“B”)

//@assoc  The Player(A) has some Dice(B)
class Player {
    Dice myDice;
}

依赖关系:使用另一种类型(例如,“C” 使用了“D”)

//@dep    The Player(C) uses some Dice(D) when playing a game
class Player {
    rollYahtzee(Dice someDice);
}

我找到了一篇简明易懂的参考资料- Association vs. Dependency


22

这个网页应该已经说得够清楚了,以下文字摘自该网页,足以理解二者的区别。

简单来说,实线表示关联(association),虚线/点线表示依赖关系(dependency)。

关联也可以是单向的,其中一个类知道与另一个类的关系,但另一个类却不知道。这种关联需要一个开放箭头来指向已知的类,只有已知的类才能拥有角色名称和多重性。在本例中,Customer 类知道任意数量的产品购买,但 Product 类不知道任何客户。多重性“0..*”表示零或多个。

依赖是两个类之间的一种弱关系,用虚线表示。在本例中,Point 和 LineSegment 之间存在依赖关系,因为 LineSegment 的 draw() 操作使用 Point 类。它表示即使没有该类型的属性,LineSegment 也必须知道 Point。该示例还说明了类图如何集中显示上下文中重要的内容,因为您通常不想显示所有类操作的详细依赖关系。

由于我的声望值只有8点,我不能直接上传图片,但您可以在我上面提到的网页上查找。

[编辑]

我这里没有代码示例,但是我个人认为这就像一辆汽车和一个门。当一辆车有门(或更多)时,它就是一辆车。

Car --- has a --> Door

但是当你有一扇门可以被打开时,门类将会有一个类似的函数。


public void openDoor(){
this.open();
}

要使用上述函数,汽车将需要创建一个门的实例

Class Car(){
Door door1 = new Door();

door1.open();
}

这样一来,您就创建了依赖关系。

所以实线只是将一个对象1指向另一个对象2,但当您开始使用对象1时,它就变成了依赖项。


很遗憾,我仍然无法分辨它们之间的差别。你认为你能提供一些简单的代码示例来说明这两种关系之间的区别吗? - NPS
6
我认为上面的代码示例不正确。没有直接的 UML 到代码的翻译,但如果有的话,一个关联通常会转换为相反类型的属性。所以一辆汽车拥有一个类型为 SteeringWheel 的属性,或者一个类型为 List<Door> 的属性可能是对关联的翻译。依赖关系通常是一种较弱的关系。类似于一辆汽车有一个 DriveTo(Location destination) 的操作。Location 是已知并被 Car 使用的,因此存在与 Location 的依赖关系。 - Geert Bellekens
我认为它稍微更具体。你在这里描述了两个关联,即“汽车有门”和“汽车的门可以打开”。它们是被动的,实际上没有使用Door的对象。 对于依赖项,您积极使用Door的对象:如果Door有“Width”和“Max_aperture_angle”对象,并且Car有一个使用这两个对象的“Max_car_width”方法,则存在依赖关系。 - Gabriel123

15

好的,既然您不接受第一个答案,那我再试试。

箭头1:普通关联

enter image description here

UML有不同类型的线和箭头。上面是简单的关联箭头,这意味着一个类可以与另一个类建立联系。下面我将用代码示例来解释每种类型。

  • 在第一个示例中,您可以看到没有明确指定谁知道谁(谁是关系的所有者)。动物可以认识人类,人类可以认识动物。这并没有得到具体说明,因此对程序员没有太大帮助。
  • 在第二个示例中,艺术家可以拥有一把吉他。因为有箭头且另一侧没有箭头,所以我们知道吉他不知道艺术家。吉他是一个可以完全独立存在而不需要任何人的对象。
  • 在第三个示例中,您可以看到一场婚礼。非常简单;丈夫认识妻子,妻子认识她的丈夫。在我们的情况下,丈夫只有一个妻子,反之亦然。

我们通常如何在代码中实现这一点?

class Husband{
    Wife bestWomanInTheWorld;

    public Husband(Wife theWife){
        this.bestWomanInTheWorld = theWife;
    }
}

因为丈夫总是需要妻子,所以我们将必需的关系放在构造函数中。因为艺术家可以有吉他,所以我们会把构造函数留空,就像这样:

class Artist{
    List<Guitar> guitars;

    public Artist(){
    }

    public AddGuitarToCollection(Guitar newGuitar){
        Guitars.Add(newGuitar);
    }
}

所以,这就是我们通常在代码中实现此操作的方式!如果您是编程新手,则通常不需要不同类型的线条和箭头。保持简单。

箭头2:依赖关系

好的,我们知道关于通常情况下使用的普通关联。但是什么时候才会使用“依赖性”箭头呢?那么,让我们定义一下依赖(维基百科):

Dependency is a weaker form of bond which indicates that one class depends on 
another because it uses it at some point in time. One class depends on 
another if the independent class is a parameter variable or local variable of 
a method of the dependent class. This is different from an association, where 
an attribute of the dependent class is an instance of the independent class. 
Sometimes the relationship between two classes is very weak. They are not 
implemented with member variables at all. Rather they might be implemented as 
member function arguments.

如果存在一个连接、关系、关联等需要存在于classA才能工作的情况,那就是一种依赖关系。例如:丈夫需要妻子才能存在。汽车需要轮子才能成为汽车(并行驶)。汽车工厂需要汽车类才能从中制造出对象。您的RSSNewsItem类需要一个XMLReader类才能做任何事情。

什么时候使用哪个?

好吧,在我看来,这是唯一有效的问题;因为谷歌显示了很多关于您问题的有效答案。尽量不要在类图中使用依赖关系,因为这通常意味着您不够具体。始终以关联、实现等为目标。仅在必须使用其他类而又不必维护关系时(例如实用程序类(如XMLReader)),才使用实现(在我看来)。

阅读完整说明后,如果您有任何问题,请随时问。 :-)


3
请注意不要混淆可导航性(箭头)和所有权。所有权被建模为关联末尾的一个圆点(在你的示例中未显示)。 - Geert Bellekens
1
是的,这是正确的,但问题在于,几乎没有人使用所有权符号,它超出了答案的范围,在大多数情况下也不必要(只有在不清楚的情况下才需要)。这也是我不鼓励任何人使用依赖关联的原因。 - guidsdo
那么在Wife类中,应该以与Husband相同的方式实现,因为她也需要他,对吧? - testing_22

7
点线表示依赖关系(指向箭头的方向)。假定您已经将源代码整齐地分别放入每个类的文件和标头中,其中一个明显的特征是代码包括#include ClassB.h这一行。
然而,事实是所有类关系(泛化、实现、组合、聚合、关联等)都继承了依赖关系。因此,在记录代码时,我从不使用点状箭头。在可能的情况下,我会尝试使用更具体的术语来记录关系,例如钻石形状、三角形等。如果我不知道确切的关系,我的起点是带箭头的实线(一个关联, 带有(隐式)依赖)。
尽管如此,点状箭头符号在UML建模的其他方面可以很有用,例如在用例分析中显示对需求的依赖。
注意,思想警察要求我们尽可能地使用接口(纯虚拟类)来降低类之间的耦合和依赖关系。
虽然纯虚类提供了多重继承和最紧密的类之间耦合的前景,但接口类的优点在于它们完全由暗物质制成,因此对警察来说是完全不可见的。有了这个想法,就可以编写C++代码,其中类之间似乎没有耦合——他们喜欢这样做,因为他们从来就不理解所有那些奇怪的符号。

代码包含了#include这一行,让我大吃一惊。 - testing_22

-8

虚线表示实现(接口) 实线表示继承(基类)


7
这是完全错误的。泛化(extends)和实现(implements)都有一个闭合箭头。 - Geert Bellekens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接