首页 > 解决方案 > 如何在 UML 对象和序列图中分别显示同一对象的引用类型和对象类型

问题描述

在此处输入图像描述 该图显示了示例类图和序列图中这些类的对象的用法。

在上图中,myCar可以通过 ShowroomItem的引用或接口Vehicle的引用来引用。因此,客户 Driver/SalesEngineer 将获得功能访问权限。

我同意在实现阶段(例如Java),这里不需要类型标识,我们将myCar 视为自己使用的基本类型(任一接口)的实例。

但是在序列图中,(为了清楚起见)我无法指出myCarDriver的引用应该是Vehicle,而SalesEngineer的引用应该是ShowroomItem

我在 UML 2.0 书籍中搜索,我没有得到合适的符号。根据目前的理解,我可以将其显示为"myCar : Vehicle""myCar : ShowroomItem",但这并不表示它的汽车对象称为接口。这个缺点并不强制 playMusic 在称为 Vehcile 时不能工作。

有没有任何符号可以显示这种细节?

由于我对提供的任何答案都不满意,因此我尝试添加以下内容以使问题更加清晰,解决答案中提出的一些异议,并提出一种解决方案供专家审查。

看评论,我觉得要么人们没有得到核心问题,要么我没有突出核心问题。首先让我演示代码不会中断。以下代码允许 SalesEngineer 使用 sell() 和 buy() 功能访问 Abstraction,而 Driver 可以仅使用 start() 和 stop() 功能访问 Abstraction [按这种方式设计]。这是向不同客户端发布不同抽象的接口的一个最强特性。Java 集合使用相同的多种基本类型,即TreeSet 中的ObjectComparable,一种用于 equals(),另一种用于实体上的 compare()。

package com.se.stackoverflow;

interface Vehicle {
    abstract void start();
    abstract void stop();
}

interface ShowroomItem {
    abstract void buy();
    abstract void sell();
}

class Car implements ShowroomItem, Vehicle {
    // **Car IS-A Vehicle and ShowroomItem BY-DEFINITION**
    // **and as per SOLID principle interface segregation**

    public void start() { System.out.println("Started");}
    public void stop() { System.out.println("Stopped");}
    public void sell() { System.out.println("Sold");}
    public void buy() { System.out.println("Baught");}
}

class SalesEngineer {
    private ShowroomItem item = null;
    public SalesEngineer(ShowroomItem item) { this.item = item;}
    public void doTransaction() {item.buy(); item.sell();}
}

class Driver {
    private Vehicle veh = null;
    public Driver(Vehicle veh) {this.veh = veh;}
    public boolean testDrive() {veh.start(); veh.stop(); return true;}
}

public class ShowroomOwner {
    public void makeDeal(Car carForDeal) {
        Driver driver = new Driver(carForDeal);
        SalesEngineer engineer = new SalesEngineer(carForDeal);
        if (driver.testDrive()) {
            engineer.doTransaction();
        }
    }

    public static void main(String[] args) {
        // simulates client as ShowroomOwner to save space
        new ShowroomOwner().makeDeal(new Car());
    }
}

在参考了 Jim Arlow 的“UML 2 和统一流程”之后,我发现我们可以在序列图中显示生命线上不断变化的状态。我觉得我们可以使用类似的符号来显示不断变化的类型对象[我没有在 UML 的任何地方看到这个记录,但它是我对 UML 组的建议]。

例如,这里的 myCar 是其类 Car 的对象(对象类永远不能更改),但它的引用类型根据左侧的不同而有所不同,如 ShowroomItem 或 Vehicle。

可能是下面的序列图可以显示它。[示例类仅用于突出自动类型转换效果]

在此处输入图像描述

标签: umlclass-diagramsequence-diagramooadobject-diagram

解决方案


UML 符号问题

您需要为序列图的每个生命线选择要显示的类型,因为 UML 只允许一个单一的生命线。由于myCar是一个Carwhich implements VehicleShowroomItem您可以选择 3 种类型中的任何一种。

一旦选择了类型,UML 就无法在同一个图中提供该类型的替代视图。您可以myCar使用Car. 但是其他生命线必须符合他们所知道的接口(前提是没有其他使用依赖项使他们能够访问完整的 Car),并且由您来确保一致性。这可能很容易出错,正如您使用playMusic().

您可以通过图表中的一个或多个注释来解决您的问题,以纯文本形式提醒读者与接口相关的约束。但更好的方法是保持简单,并在两个单独的图表中显示SalesEngineerCar之间的交互。这更接近您的设计的实际情况,并促进了关注点的合理分离DriverCar

OOP 设计问题

Qwerty_soBruno已经指出了您设计中的弱点。我完全同意他们的看法。确实, aCar是 a Vehicle。但是 aCar 不是a ShowroomItem:aCar可以暂时起到陈列室物品的作用。或者反过来说,陈列室物品可能在给定时间对应于特定汽车。

如果汽车不是陈列室物品,则它既不应该从这样的类继承也不应该实现这样的接口。因此,更喜欢组合而不是继承,例如:

  • SalesEngineer以旧换新ShowroomItem
  • CarForSale工具ShowroomItem
  • CarForSale与一个相关联Car
  • Driver驾驶a Vehicle注意:偏向滚动车辆,因为飞机上没有司机;-)
  • Car工具Vehicle
  • Car可以与 a 相关联CarForSale(但仅当它由 a 出售时SalesEngineer

这种设计确保了更好的关注点分离。例如,您可以sell()buy()只有真正待售的汽车,而不是任何汽车。只有待售汽车才会有价格。

另一个优点是您可以在单个序列图中以更健壮的方式显示这个完整的画面,因为不同的职责是由不同的对象实现的


推荐阅读