假设您想要绘制 Map
的图表类(数学类,而不是制图类)。在 Ruby 中,您可能会遇到类似以下内容:
class Map
attr_accessor :nodes, :edges
def initialize
@nodes = Set.new
@edges = Set.new
end
def minimum_spanning_tree(&mst_algorithm)
mst_algorithm.call(@nodes, @edges)
end
...
end
当我开始绘制图表时,我的想法如下:
让我们尝试一下类图,因为我们正在讨论类。我将创建一个 Map
类(class)。还有一个Node
类(class)。还有一个Edge
类(class)。还有一个Set
类(class)。美好的。现在我将从 Map
绘制一条 Composit-Of(1:2) 线至Set
-- 每人一份@nodes
和@edges
。然后来自 Set
的 has-many(1:0..*) 行至Node
另一个来自 Set
至Edge
。但现在我想说的是,每组都可以有 Node
的任意组合。和 Edge
s,这不是真的。而且放下两个Set
也无济于事。图表上的元素(具有两条相应的 Composit-Of(1:1) 线),因为它们是同一个对象。
所以我想:好吧,也许 UML 希望我更加熟悉 C++/Java。模板化 Set<Node>
和Set<Edge>
在 UML 中不可能,但我可以创建子类:NodeSet
和EdgeSet
.
最后我考虑了对象图,但这是不对的。我说的是Set
类(Class),而非个人Set
实例。
有更好的答案吗?或者我已经找到了“最不坏”的那个?
稍后
Marc W的答案和 Pete Kirkham正如我第一次提出的那样,他们对这个问题的回答非常棒。问题是我试图对我的实际问题使用一个简单的类比,因为我无法揭示问题的存在。我实际上只是在了解如何让两个具有不同关系的同一类,但行为相同并具有相同的属性(尽管不是属性值)。
让我们用一些不同的模型再试一次:an ActiveDirectory
,一个Firewall
,和两个Router
s。一台路由器(LAN 路由器)引用了 ActiveDirectory
和 Firewall
;另一个(WAN 的)引用了 Firewall
以及一些公共(public)服务器(我们将在此图中忽略它们)。完全可以想象 Router
具有相同的品牌、型号等。它们将具有不同的序列号(对象不同),但它们肯定都是 Router
s。然而,为了将两者都放在类图上,我必须子类 Router
进入LANRouter
和WANRouter
。 Marc W 解决方案的类比是连接 Firewall
和ActiveDirectory
直接将实现( Router
)留给类来确定。但是如果要使用 UML 来实际构建系统,抽象就必须泄漏。
最佳答案
如果您正在对域而不是实现进行建模,UML 会提供关系的构造型。在这种情况下,您可以将节点和边标记为 {unordered}(与包或集合相关)和 {unique}(将其范围缩小为集合)。我看不到节点或边的类型有任何限制,因此如果不存在,请不要尝试添加它:
class Map
+nodes : { unordered, unique } object[0..*]
+edges : { unordered, unique } object[0..*]
+minimum_spanning_tree(mst_algorithm:callable)
一个好的代码生成器/逆向工程工具可以将 { unordered, unique } UML 集合映射到源代码中的 Sets 或从 Sets 映射出来。
如果该信息对您有用,您可能希望将 Map 设置为域模型中的参数类型。但由于在实现中没有使用这些信息,所以有点难以理解这一点,而在不失去实用性的情况下使事情尽可能简单始终是建模的目标。
如果您想记录某个特定类是使用集合实现的,则可以使用关联来实现。您是对的,C++ 或 Java 允许您声明 Set 中元素的类型,并且这些类型映射到 UML 中的参数类型。 AFAIK Ruby 没有实现此类类型限制的机制,因此要记录 Map 的 Ruby 实现,只需记录实现中的内容 - Map 有两个与 Set 的关联,并且对 Set 中的内容没有限制。 (您可能希望将其记录为不变约束,然后进行单元测试而不是类型约束,但是如果您想要限制性静态类型检查,那么很难理解为什么要在 Ruby 中工作)
关于uml - UML 中类和对象之间的中间地带在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/878863/