【问题标题】:Association or Aggregation relationship for Facade design pattern?外观设计模式的关联或聚合关系?
【发布时间】:2020-10-23 13:52:58
【问题描述】:

我正在研究 GoF 设计模式,尤其是 Facade 模式。我了解它的使用和实现,但我对它的 UML 模型有疑问。 我的教授提出的解决方案总结如下:

public class Facade{
    private ClassA c1;
    private ClassB c2;
    private ClassC c3;

    public Facade(){
        this.c1 = new ClassA;
        this.c2 = new ClassB;
        this.c3 = new ClassC;
    }

    public void FacadeMethod(){
        ...
        c1.operationA();
        c2.operationB();
        c3.operationC();
        ...
    }

}

提出的UML模型是这样的:

Facade Class 与 ClassA、ClassB、ClassC 类有关联关系。但是这些应该是聚合关系吗? Facade Class 引用 c1 到 ClassA,c2 到 ClassB,c3 到 ClassC,所以我认为这是一个“HAS-A”关系。有什么想法吗?

【问题讨论】:

    标签: java design-patterns uml aggregation facade


    【解决方案1】:

    初步说明

    许多来源倾向于使用UML-aggregation 以图形方式表示object-composition。鼓励这种趋势的流行来源是例如wikipedia。但不建议这样做。

    您的教授(几乎)对于符号和外观示例是正确的

    您的教授在外观实现的代码中使用对象组合,并用正确的navigable association 表示这一点。一些专家声称使用association end ownership的点表示法会更好

    您的教授使用对象组合和对对象的前向外观调用。这是外观的有效实现:

    • GoF 明确指出第 187 页“[外观] 将客户端请求委托给适当的子系统对象”,这清楚地允许对象组合。

    • 虽然这不是实现外观的最常用方法(通常使用类方法代替),但 GoF 在第 188 页进一步描述了实现替代方案:

      [抽象外观类]的子类化的替代方法是使用不同的子系统对象配置外观对象。要自定义外观,只需替换其一个或多个子系统对象即可。


    附加参数

    您可以使用 UML 聚合来表示对象组合吗?

    显然,使用 UML 聚合对对象组合进行建模似乎并没有根本错误:UML 没有很好地定义聚合语义,并为解释留下了空间。在UML specs 的第 110 页上解释说:

    • 聚合是指使用一个实例“将一组实例组合在一起” - 但没有什么禁止该组仅限于一个成员。
    • 共享聚合(又名白色菱形)的精确语义因应用领域和建模者而异” - 那么为什么不(误用)使用它用于对象合成

    虽然这是一个有效的解释,但它也存在一些缺陷:

    • 鉴于“一组实例”的措辞,许多建模者可能会误解聚合默认具有* 多重性,并且默认情况下集合不是单例。当 UML 聚合用于对象组合时,应明确表示 1 的多重性以避免误解。

    • 更仔细地阅读第 110 页表明,聚合实际上是为了对部分-整体关系进行建模。因此,在其他情况下将其用于对象组合是对 UML 聚合的滥用(没有错,但不是意图):

      一个属性有一个聚合属性(...);代表整个群体的实例由属性的所有者分类,代表分组的个人的实例由属性的类型分类。

    • 这种对对象组的解释在第 198 页得到了加强,理解 UML 复合聚合和共享聚合之间的主要区别在于聚合项的所有权:

      二元关联可以表示复合聚合(即整体/部分关系)。

    • UML 的创始人 Booch、Rumbaugh 和 Jacobson 在他们的非规范但更易读的书“UML 用户指南”中证实了这一点:

      (...) 聚合,表示“has-a”关系,意思是 一个整体的对象有部分的对象。聚合是 实际上只是一种特殊的关联,由装饰指定 与整个末端未填充的钻石的简单关联。

    考虑到这种弱语义,我们可以总结: UML-聚合可以使用对象组合来实现。但并非所有对象组合都实现 UML 聚合。这不是两个概念之间的一对一映射。

    你应该使用聚合吗?

    我们可以以 Martin Fowler 在他的优秀著作“UML Distilled”中的引言作为总结,他在其中分析了解释聚合和正常关联之间差异的难度,与任何实施考虑无关:

    聚合严格来说是没有意义的;因此,我建议你 在您自己的图表中忽略它。如果你在别人的眼里看到 图表,您需要更深入地了解它们的含义。 不同的作者将其用于不同的目的。

    【讨论】:

    • 关于多重性,当它缺失时通常认为它是 1(我遵循 BoUML 中代码生成的(非)规则),但在第 202 页的顶部是规范说NOTE. If no multiplicity is shown on the diagram, no conclusion may be drawn about the multiplicity in the model。所以认为聚合的多重性是“*”就像认为它是非聚合的 1 一样滥用
    • 但是no multiplicity is shown on the diagram可以从几个方面理解,可能是因为绘图设置的多重性没有显示,也可能是聚合的定义中没有指定多重性。谈论图表是相当令人不安的。紫外线为新的好答案。
    • @bruno 是的,我完全同意:考虑 * 或 1 是滥用,因为未指定是未指定的。解释应该始终谨慎并自担风险:-) UML 标准会受到启发,明确地赋予聚合“有”语义(Booch 表示法在他的 UML 前表示法中提供了这种语义,OMT 也有它,甚至使用白色钻石):这将非常优雅地提供聚合和对象组合。
    • @sixpain 这就是我对对象组合滥用 UML 聚合表示法的意思:Java 对象具有引用语义。 java中的对象组合与拥有对象引用相同。虽然可以捍卫白色钻石,但在没有部分/整体关系的情况下,这不是一个好的做法。这种做法非常古老:UML 之前的 OMT 使用白色钻石来表示“有”,许多人将其用于对象组合。部分/整体是后来才出现的,当时 OMT 和 Booch 与 OOSE 合并在一起形成 UML。
    • @sixpain 有趣的是,GoF(使用 OMT 表示法而不是 UML)不使用适配器中的菱形。然而,它们的用法也不一致。他们将它与桥梁、策略和装饰器的对象组合语义一起使用。他们在组合、享元、状态和解释器中将其与聚合语义一起使用。他们在命令和备忘录中使用不明确的语义。并且他们不会在使用组合或聚合的其他模式中使用它。我已经编辑添加了 Martin Fowler 的报价,其中包含有关 UML 聚合使用的建议
    【解决方案2】:

    但是这些应该是聚合关系吗?

    不,Facade 不是由 ClassA ClassB 和 ClassC 组成的,例如这些类不是 Facade 的一部分

    【讨论】:

    • 谢谢!这种情况和(例如)适配器模式有什么区别?由于 Adapter 具有对 Adaptee 对象的引用,因此 Adapter Class 与 Adaptee Class 具有聚合关系。也许我误解了java中的聚合含义。 imgur.com/a/uwz08xT
    • @sixpain 对我来说使用适配器模式的聚合是一个错误。聚合的典型用法是Car <>--->Engine,因为 Car 由(至少)一个 Engine 组成。适配器需要并使用适配器,但不是由它组成
    • 我同意你的观点,但在维基百科、我的书和我教授的模型上,它使用了聚合关系。看到 java 实现,我给自己的唯一解释是 Adapter 引用了一个 Adaptee 对象,所以 Adapter "HAS-A" Adaptee.it.wikipedia.org/wiki/Adapter_pattern#/media/…
    • @sixpain 一个实现可以表示一个组合(至少在 C++ 中,在 Java 中不确定)但绝不是一个聚合
    【解决方案3】:

    聚合是一种更具体的关联形式,所以教授的解决方案没有错,但我认为你的解决方案也可以

    【讨论】:

      猜你喜欢
      • 2017-08-11
      • 2014-04-08
      • 1970-01-01
      • 1970-01-01
      • 2019-02-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-01
      相关资源
      最近更新 更多