如何在UML中可视化协议和扩展?

11

使用UML接口来可视化Swift协议似乎是合理的。但是,我应该如何可视化提供特定协议默认实现的扩展呢?我是否应该使用类似于<<extension>>ProtocolName的类来继承该协议?


也许您会对这个编程问题的问答感兴趣,特别是@Klaim的回答。链接为https://dev59.com/yXA65IYBdhLWcg3wrgsa,回答链接为https://dev59.com/yXA65IYBdhLWcg3wrgsa#3686405。 - dfrib
2个回答

8

一个扩展存在于某个类的范围内,只是为其添加了一些功能。因此,我会将该扩展表示为子类(最终可能更喜欢打包)。为了表明它是一个扩展,我会使用一个构造型。对被扩展的类的依赖有些可选,因为在上下文中这是一种命名约定。

enter image description here

如果扩展还需要遵循某些协议,您只需向相应的接口类添加实现关系即可。这是一种表达方式。由于没有本地UML结构来表示扩展,因此您在此处相对自由地发明自己的习语。

有趣。我有印象这可以应用于类扩展。但是协议扩展会为所有符合条件的类扩展协议。这意味着它们突然继承了自己没有意识到的特性,更像是抽象类而不是接口,不是吗? - Christophe
@Christophe 嗯,我差不多放弃了Swift,因为它的类型检查军事原则就像《第二十二条军规》一样。上面的内容是我很久以前想出来的。可能有更好的方法,但我不太热衷于深入探索它。 - qwerty_so
1
哦,我认为你的回答对很多人有所帮助,对于类来说肯定是一种有效的方法。就我而言,我仍在探索这个有趣的语言,尽管这些接口一旦被扩展,就会变得更加复杂(更不用说枚举类型了,它们表现得像具有可变属性的类);-) - Christophe

0

简而言之

Swift 协议原则上是 UML 接口:类并不从接口/协议继承任何东西,但必须实现接口/协议所承诺的功能。

协议扩展改变了这种语义等价性:协议扩展可以提供符合类将会继承的特征。这与 UML 接口不兼容,对应于抽象类的语义。但使用抽象类的话,在多重继承方面会令人困惑。「协议」类型标记似乎更可取。

更多解释

协议

Swift 协议如果没有被扩展,实际上对应于 UML 接口,尽管措辞上有细微差别:

Swift: 协议定义了适用于特定任务或功能块的方法、属性和其他要求的蓝图。然后,该协议可以由类、结构体或枚举采用,以提供这些要求的实际实现。

相比之下:

类扩展

UML:接口是一种分类器,它表示一组公共特征和义务的声明,这些特征和义务共同构成了一个连贯的服务。接口指定了一个合同;实现接口的任何分类器实例都应该履行该合同。

有不同类型的扩展。类扩展为它们所扩展的类添加功能。如果扩展发生在同一文件中,它甚至可以访问类的私有成员。

class MyClass {
    func hello()->Void { print ("Hello, World !"); }
}
var c = MyClass()
extension MyClass {  // first extension
    func hellobye() -> Void { hello(); print(" Good bye!"); }
}
extension MyClass {  // second extension
    func bye() -> Void { print("Au revoir!"); }
}
c.hello()      // the object has all the features of class + extension
c.hellobye()   // even if it was defined befor the extension    
c.bye()

该扩展没有明确的名称:它只是重新定义了原始类型,类的行为就像初始定义和扩展是相同的分类器,只是在源代码中分开:

  • 如果扩展和初始类在同一个包中定义,则在UML图中最清晰的表示方式是丰富初始类。
  • 如果扩展在另一个包中定义,则可以将完整的扩展集合表示为自己的类,并使用合并包来组合两者。由于扩展需要原始类才能工作,因此还可以显示两者之间的依赖关系。或者,您也可以考虑使用“类扩展”构造型。但是,由于UML具有唯一命名约束,因此仍然将所有扩展分组到包中的同一类中。

协议扩展

协议扩展改变了底层协议的性质。虽然原始协议是一个没有任何实现的接口,但扩展协议将提供一些实现:

protocol MyProto {
    var v1:String { get  }
    func op1() -> Void
    func op2() -> Void
}

class Test : MyProto {
    var v1:String = "abc"
    func op1() -> Void { print("Op 1"); }
    func op2() -> Void { print("Op 2"); }
}

extension MyProto {
    var dev1:String { get { return "de"+v1; }}
    func combo() -> Void { op1(); op2(); print("Combo 1 and 2"); }
}

var test = Test()
test.combo()
print (test.dev1);

在UML中,扩展协议将对应于一个«abstract»类,即一个由于某些缺失特性而无法直接实例化的类,但可能具有一些明确定义的继承特性。由于没有建模方式将接口转换为抽象类,因此UML并不完全支持这种情况。
最干净的方法是使用定义了«protocol»原型的UML配置文件,作为«abstract»类的一种特殊形式。然后,协议扩展将像上面解释的类一样处理,使用«protocol extension»来处理«class extension»的情况。

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