电子科大软件系统架构设计——软件建模详细设计

news2024/12/24 5:12:07

文章目录

  • 软件建模详细设计概述
    • 软件建模详细设计目标
    • 软件建模详细设计原则
      • 开闭原则
      • 里氏 (Liskov) 替换原则
      • 依赖倒置原则
      • 接口分离原则
      • 单一职责原则
      • 最少知识原则(迪米特法则)
      • 高内聚原则
      • 松耦合原则
      • 可重用原则
    • 软件建模详细设计内容
  • UML 软件静态结构视图建模
    • 软件静态结构细化设计
    • 类的聚合关系细分
      • 专属聚合 (Exclusive0wns)
      • 从属聚合 (0wns)
      • 拥有聚合 (Has)
      • 成员聚合 (Member)
    • 泛化与继承
    • 实现继承
      • 扩展继承
      • 限制继承
      • 方便继承
    • 高级类图建模
      • 可见性
      • 导出信息
      • 限定关联
      • 关联类与具体化类
    • 接口与抽象类
      • 接口
      • 抽象类
      • 接口与抽象类的区别
    • 类内聚与耦合
      • 类内聚与耦合的启发规则
      • 类耦合的种类
      • 类之间 6 种关系的耦合强度依次增强
      • 迪米特法则应用
    • 设计类图建模实践
  • UML软件动态交互视图建模
    • 顺序图
    • 通信图
    • 类操作
      • 发现操作
      • 类操作的表示
    • 顺序图高级交互技术
      • 创建与销毁临时对象
      • 片段
      • 交互引用
  • UML软件状态机视图建模
  • UML 软件的实现视图建模
    • 实现视图
    • 构件图
      • 构件和构件之间的依赖关系
      • 构件与接口之间的关系
      • 端口
      • 端口连接
    • 部署图
    • 包图
      • 包的可见性
      • 包的嵌套
      • 包图
  • 图书借阅管理系统软件建模设计实践
    • 系统需求
    • 系统静态模型设计
    • 系统动态模型设计
      • 图书搜索功能逻辑设计
      • 图书借阅功能逻辑设计
      • 图书归还功能逻辑设计
      • Book对象的状态机图设计
      • Borrower对象的状态机图设计
      • 静态结构的类图模型细化设计

软件建模详细设计概述

软件建模详细设计目标

软件建模详细设计是在软件架构模型设计基础上,对系统中各模型进一步细化设计,给出软件程序实现的设计方案。

软件建模详细设计原则

开闭原则

  • 软件实体(类、构件等)应该对功能扩展具有开放性,对代码修改具有封闭性。
  • 当应用需求改变时,在不修改软件实体源代码的前提下,就可以扩展模块的功能,使其满足新的需求。

案例:“环境监测器”功能构件设计

image-20231222200751781

开闭原则设计的好处:

  • 在不修改原有代码基础上,可以扩展新功能。
  • 提高代码的可复用性及可维护性。

里氏 (Liskov) 替换原则

  • 子类可以扩展基类的功能,但不能改变基类原有的功能。
  • 子类在继承基类时,除了添加新的方法且完成新增功能外,不要重写基类的方法代码。
  • 子类必须遵守基类与外部类之间的隐含约定。

image-20231222201247864

里氏替换原则设计的好处:

  • 解决了继承重写父类带来的可复用性变差问题。
  • 提高代码的可复用性及可维护性。

案例:鸟类飞行时间计算模块程序设计

image-20231222202235280

//鸟类
class Bird {
    protected double flySpeed;
    public void setSpeed(double speed) {
        flySpeed = speed;
    }
    public double getFlyTime(double distance) {
        return (distance / flySpeed);
    }
}

//燕子类
class Swallow extends Bird {
}

//几维鸟类
class BrownKiwi extends Bird {
    public void setSpeed(double speed) {
        flySpeed = 0;
    }
}

image-20231222213807148

package principle;

public class LSPtest {
    public static void main(String[] args) {
        Bird bird1 = new Swallow();
        Bird bird2 = new BrownKiwi();
        bird1.setSpeed(120);
        bird2.setSpeed(120);
        System.out.println("如果飞行300公里: ");
        try {
            System.out.println("燕子将飞行" +
                    bird1.getFlyTime(300) + "小时.");
            System.out.println("几维鸟将飞行" +
                    bird2.getFlyTime(300) + "小时.");
        } catch (Exception err) {
            System.out.println("发生错误了!");
        }
    }
}

按照里氏替换原则重新设计该模块

image-20231222220441588

完整代码:

// 动物类
class Animal {
    protected double runSpeed;

    public void setRunSpeed(double speed) {
        runSpeed = speed;
    }

    public double getRunTime(double distance) {
        return (distance / runSpeed);
    }
}

// 鸟类
class Bird extends Animal {
    protected double flySpeed;

    public void setFlySpeed(double speed) {
        flySpeed = speed;
    }

    public double getFlyTime(double distance) {
        return (distance / flySpeed);
    }
}

// 燕子类
class Swallow extends Bird {
    // 燕子类可以继承 Bird 类的飞行行为
}

// 几维鸟类
class BrownKiwi extends Animal {
    // 几维鸟不能飞,所以我们不实现飞行相关的方法
    // 而是使用 Animal 类的跑步行为
    // 如果需要,这里可以覆盖跑步速度设置方法或行为
}

依赖倒置原则

  • 基于抽象类编程,不依赖具体类编程。
  • 面向接口编程,不要面向实现编程。

image-20231222220812371

依赖倒置原则设计的好处:

  • 降低客户类与实现类之间的耦合性,容易实现代码可扩展性。
  • 降低并行开发可能出现的风险性。

案例:顾客购物模块程序设计

顾客类 (Customer) 通过韶关网店(ShaoguanShop)类实现购物,其代码如下:

class Customer {
    public void shopping(ShaoguanShop shop) {
        //购物
        System.out.println(shop.sell());
    }
}

顾客若想从婺源网店 (WuyuanShop) 购物,需要对顾客类 (Customer )代码修改:

class Customer {
    public void shopping(WuyuanShop shop) {
        //购物
        System.out.println(shop.sell());
    }
}

解决方法:定义“婺源网店”和“韶关网店”的共同接口 Shop ,顾客类 (Customer )面向该接口编程,其代码修改如下:

image-20231222221518622

class Customer {
    public void shopping(Shop shop) {
        //购物
        System.out.println(shop.sell());
    }
}

完整代码:

// 定义 Shop 接口
interface Shop {
    String sell();
}

// 韶关网店实现 Shop 接口
class ShaoguanShop implements Shop {
    @Override
    public String sell() {
        // 实现卖出商品的方法,返回结果
        return "韶关网店销售的商品";
    }
}

// 婺源网店实现 Shop 接口
class WuyuanShop implements Shop {
    @Override
    public String sell() {
        // 实现卖出商品的方法,返回结果
        return "婺源网店销售的商品";
    }
}

// 顾客类,面向 Shop 接口编程
class Customer {
    public void shopping(Shop shop) {
        // 购物
        System.out.println(shop.sell());
    }
}

接口分离原则

  • 一个构件对外应提供不同服务的专用接口,而不要试图去建立一个很庞大的接口供所有依赖它的构件去调用,避免客户端依赖不必要的接口方法
  • 提供者构件应该为每个不同访问构件类型提供一个特定的接口

image-20231222223341534

接口分离原则设计的好处:

  • 符合高内聚低耦合设计思想,提高代码可扩展性。
  • 使得类具有很好的可读性、可扩展性和可维护性。

案例:学生成绩管理程序

image-20231222223506809

  • 学生成绩管理程序一般包含插入成绩、删除成绩、修改成绩、计算总分、计算均分、打印成绩信息、查询成绩信息等功能。
  • 如果将这些功能全部放到一个接口中对外提供访问服务,这显然不太合理。
  • 合理的做法是对外提供不同的功能服务访问接口。

单一职责原则

  • 一个类应该有且仅有一个功能职责,否则该类应该被拆分。
  • 单一职责原则的核心就是控制类的粒度大小,将对象解耦,提高其内聚性。

单一职责原则设计的好处:

  • 降低类的复杂度。一个类只负责一项职责,其代码逻辑肯定要比负责多项职责简单得多。
  • 提高类代码的可读性。复杂性降低,自然其可读性会提高。
  • 提高类代码的可维护性。可读性提高,那自然更容易维护了。

案例:学生工作管理

image-20231222225406643

学生工作主要包括学生生活辅导和学生学业指导两个方面的工作:

  • 生活辅导主要包括班委建设、出勤统计、心理辅导、费用催缴、班级管理等工学业指导主要包括专业引导、学习辅导、科研指导、学习总结等工作。
  • 如果将这些工作交给一位老师负责显然不合理,好的做法是生活辅导由辅导员负责,学业指导由学业导师负责。

最少知识原则(迪米特法则)

迪米特法则的定义是:只与你的朋友交谈,不跟“陌生人”说话。在程序设计中,如果两个软件实体没有直接关系,那么就不直接调用,可以通过第三方转发该调用。

迪米特法则具有以下好处:

  • 降低了类之间的耦合度,提高了模块的相对独立性。
  • 由于耦合度降低,从而提高了类的可复用性和系统的扩展性。

案例:明星与经纪人的关系

image-20231222230017098

  • 明星需全身心投入艺术,所以许多日常事务交给经纪人去处理,如与粉丝的见面会,与媒体公司的业务洽淡等。
  • 经纪人是明星的朋友,而粉丝和媒体公司对明星来讲是陌生人。

高内聚原则

内聚性意味着构件或者类只封装那些密切关联的属性和操作。

按照内聚性的级别,内聚性分为以下三种,它们的内聚性依次变弱:

  • 功能内聚:模块内的所有元素紧密配合完成同一个功能,即满足单一职责原则。
  • 分层内聚:系统采用分层结构,高层能够访问低层服务,但低层不能访问高层服务。
  • 通信内聚:模块内的所有元素都访问相同数据。通信内聚通常用于数据的查询、访问和存储。

一般来说,内聚性越高,构件越易于实现、测试和维护。

image-20231222231011912

松耦合原则

耦合性指不同对象之间相互关联的程度。如果一个对象过多地依赖于其它对象来完成自己的工作,这会增加测试、修改的难度,同时降低了对象的可重用性和可移植性。

在详细设计中要尽可能保持对象之间松耦合。但是软件必须进行内部和外部的通信,因此耦合是必然存在的。当两个对象必须相互联系时,应该通过类的公共接口实现耦合,不应该直接依赖于类的实例。

补充例子:

// 定义一个通信接口
interface Communicator {
    void sendMessage(String message);

    String receiveMessage();
}

// 实现Communicator接口的Email类
class EmailCommunicator implements Communicator {
    public void sendMessage(String message) {
        System.out.println("Sending email with message: " + message);
    }

    public String receiveMessage() {
        // 假设接收到消息
        return "Email message received";
    }
}

// 实现Communicator接口的SMS类
class SMSCommunicator implements Communicator {
    public void sendMessage(String message) {
        System.out.println("Sending SMS with message: " + message);
    }

    public String receiveMessage() {
        // 假设接收到消息
        return "SMS message received";
    }
}

// 使用Communicator接口的Client类
class Client {
    private Communicator communicator;

    public Client(Communicator communicator) {
        this.communicator = communicator;
    }

    public void communicate() {
        communicator.sendMessage("Hello World!");
        System.out.println(communicator.receiveMessage());
    }
}

// 在应用程序中使用Client和Communicator
public class CommunicationApp {
    public static void main(String[] args) {
        Communicator email = new EmailCommunicator();
        Communicator sms = new SMSCommunicator();

        Client emailClient = new Client(email);
        Client smsClient = new Client(sms);

        emailClient.communicate();
        smsClient.communicate();
    }
}

在这个例子中,Client 类不直接与 EmailCommunicatorSMSCommunicator 类耦合。相反,它通过 Communicator 接口与它们通信。这允许 Client 类在不知道通信细节的情况下发送和接收消息。因此,如果将来引入了一个新的 Communicator 实现,例如 WhatsAppCommunicatorClient 类不需要任何修改就能使用它,只需在创建 Client 实例时传入新的通信器即可。这就是通过类的公共接口实现耦合的一个典型例子。

可重用原则

  • 为了提高工作效率、减少错误、降低成本,要充分考虑软件元素的重用性
  • 重用性有两个方面的含义:尽量复用已有的类代码,如开发环境提供的类库;在设计新类时考虑将来的可重用性。
  • 设计一个可重用的软件比设计一个普通软件的代价要高,但是随着这些软件被重用次数的增加,分摊到它的设计和实现成本就会降低。

补充例子:

import java.util.ArrayList;
import java.util.List;

// 一个通用的接口定义了操作的契约
interface Operable {
    void operate();
}

// 一个具体的类实现了Operable接口
class Printer implements Operable {
    @Override
    public void operate() {
        System.out.println("Printing a document...");
    }
}

// 另一个具体的类实现了Operable接口
class Scanner implements Operable {
    @Override
    public void operate() {
        System.out.println("Scanning a document...");
    }
}

// 设计一个工作流程类,它可以利用Operable接口操作任何设备
class Workflow {
    private List<Operable> operations = new ArrayList<>();

    public void addOperation(Operable operable) {
        operations.add(operable);
    }

    public void runAll() {
        for (Operable operable : operations) {
            operable.operate();
        }
    }
}

// 演示如何重用Workflow类来操作不同的设备
public class OfficeAutomationApp {
    public static void main(String[] args) {
        // 创建设备的实例
        Printer printer = new Printer();
        Scanner scanner = new Scanner();

        // 创建工作流并添加操作
        Workflow officeWorkflow = new Workflow();
        officeWorkflow.addOperation(printer);
        officeWorkflow.addOperation(scanner);

        // 运行所有操作
        officeWorkflow.runAll();
    }
}

在这个例子中,Operable 接口允许我们定义一个可以操作任何设备的通用契约。PrinterScanner 类都实现了这个接口,使得它们可以在 Workflow 类中互换使用。这样的设计不仅提高了类的重用性,而且在设计新类时也考虑到了将来的可重用性。例如,如果我们想添加一个新设备类如 FaxMachine,我们只需要让它实现 Operable 接口,就可以轻松地添加到现有的工作流程中而不需要修改 Workflow 类的代码。

此外,这个例子还展示了如何复用Java开发环境提供的类库,例如 ArrayList 类,来管理 Operable 对象的集合。这种复用标准类库可以提高开发效率,减少错误,并降低开发成本。

以下客户功能模块存在什么设计问题?该如何完善?

接口分离原则, IClient 接口提供的方法太多或不是所有的客户端都需要这些方法,它违反了接口分离原则。客户端不应该被迫依赖于它们不使用的方法。 根据不同的职责将 IClient 接口拆分为更小的接口。详细代码如下:

// 接口负责连接管理
public interface IConnectionManager {
    void connect();
    void disconnect();
}

// 接口负责数据的发送和接收
public interface IDataTransfer {
    void send();
    void receive();
}

// 客户端类实现这两个接口
public class Client implements IConnectionManager, IDataTransfer {
    @Override
    public void connect() {
        // 实现连接逻辑
    }

    @Override
    public void disconnect() {
        // 实现断开连接逻辑
    }

    @Override
    public void send() {
        // 实现发送数据逻辑
    }

    @Override
    public void receive() {
        // 实现接收数据逻辑
    }
}

问题探讨:以下客户数据图形分析模块存在什么设计问题?该如何完善?

image-20231223123236184

违背单一职责原则,应将该类进行拆分,CustomerDataChart 类负责图表的创建和显示,一个 CustomerDAO 类负责数据访问操作,以及一个 DBUtil 类负责数据库连接。

代码如下:

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

class DBUtil {
    public Connection getConnection() {
        // 实现数据库连接的逻辑
        // 例如使用 JDBC 连接到数据库
        return null; // 应返回实际的数据库连接对象
    }
}

class CustomerDAO {
    private DBUtil util;

    public CustomerDAO(DBUtil util) {
        this.util = util;
    }

    public List<Customer> findCustomers() {
        // 使用 util.getConnection() 获取数据库连接
        // 执行查询并返回客户列表
        return new ArrayList<>(); // 应返回实际查询到的客户列表
    }
}

class CustomerDataChart {
    private CustomerDAO dao;

    public CustomerDataChart(CustomerDAO dao) {
        this.dao = dao;
    }

    public void createChart() {
        // 使用 dao.findCustomers() 获取数据
        // 根据获取的数据创建图表
    }

    public void displayChart() {
        // 显示图表的逻辑
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建 DBUtil 实例
        DBUtil dbUtil = new DBUtil();

        // 使用 DBUtil 实例来创建 CustomerDAO 实例
        CustomerDAO customerDAO = new CustomerDAO(dbUtil);

        // 使用 CustomerDAO 实例来创建 CustomerDataChart 实例
        CustomerDataChart dataChart = new CustomerDataChart(customerDAO);

        // 创建图表
        dataChart.createChart();

        // 显示图表
        dataChart.displayChart();
    }
}

图形展示如下:

image-20231223123656209

软件建模详细设计内容

  • 在分析类基础上,对软件静态结构模型的类图进行详细设计。各个功能构件均需给出设计类模型。
  • 针对构件功能,分析对象之间的交互关系,给出功能构件逻辑实现的动态行为交互图模型详细设计。
  • 分析对象生命周期的状态变化,给出对象状态变迁的状态机图模型详细设计。
  • 对反映软件物理实现模型的构件图部署图进行详细设计。

UML 软件静态结构视图建模

软件静态结构细化设计

  • 静态结构视图用来表示软件系统的类程序组成结构,同时也反映各个类中封装的数据与操作方法。
  • 静态结构视图支撑面向对象程序编程。

image-20231223131811928

类的聚合关系细分

专属聚合 (Exclusive0wns)

专属聚合是类之间语义最强的聚合,部分类与整体类之间满足 4 个条件:

  1. 部分类对整体类具有存在依赖性,删除整体类时,部分类也被删除。
  2. 具有传递性,如果对象 C1是 B1 的一部分,并且 B1 是 A1 的一部分, C1 也是 A1 的一部分。
  3. 具有非对称性,如果 B1 是 A1 的一部分,则 A1 不是 B1 的一部分。
  4. 具有固定性,如果 B1 是 A1 的一部分,则它绝不是 Ai (i!=1)的一部分。

image-20231223133005855

从属聚合 (0wns)

从属聚合的语义比专属聚合稍弱,部分类与整体类之间满足 3 个条件:

  1. 部分类对整体类具有存在依赖性,删除整体类时,部分类也被删除。
  2. 具有传递性,如果对象 C1 是 B1 的一部分,并且 B1 是 A1 的一部分,则 C1 是 A1 的一部分。
  3. 具有非对称性,如果 B1 是 A1 的一部分,则 A1 不是 B1 的一部分。

image-20231223133208338

拥有聚合 (Has)

拥有聚合的语义比从属聚合弱,部分类与整体类之间满足 2 个条件:

  1. 具有传递性,如果对象 C1 是 B1 的一部分,并且 B1 是 A1 的一部分,则 C1 是 A1 的一部分。
  2. 具有非对称性,如果 B1 是 A1 的一部分,则 A1 不是 B1 的一部分。

image-20231223133256847

成员聚合 (Member)

成员聚合的语义最低,仅仅是将一组对象组合在一起。

  • 不具有存在依赖性、传递性、非对称性、固定性。
  • Member 聚合具有组合独立对象的特征。
  • 表示一组对象作为一个高层复合对象来考虑。
  • 一个对象可以同时属于一个以上的复合对象,因此 member 聚合的多重性可以是多对多。

image-20231223133531574

泛化与继承

  • 泛化是指多个类的公共特征抽象到一个更一般化的类中,它描述类之间的一般与具体关系。
  • 继承是指子类复用基类(超类)属性与操作的机制。

image-20231223133655398

实现继承

实现继承是指在子类中组合基类(超类)的特征,并允许在必要时以新的实现来重载这些特征。

扩展继承

image-20231223133923122

在扩展继承中,子类组合来自超类的特征,并增加新的特征。

限制继承

限制继承是指子类组合来自超类的特征,并重载部分继承来的特征。

image-20231223134137099

问题:重载导致子类没有包括超类的所有特征,会带来维护上的问题,可替换性原则失效。

方便继承

某些类之间具有一定相似的实现,但它们之间不存在泛化关系,即没有概念上的分类关系。方便继承是指将它们中一个类作为其它类的超类。

image-20231223134310533

问题:子类没有包括超类的所有特征,子类与超类之间没有泛化关系,可替换性原则无效。尽量避免采用这种方式实现继承。

高级类图建模

软件开发的实际情况很复杂,复杂的问题不会有简单的解决方案。本节引入类图建模的高级概念,包括:

  • 可见性
  • 导出信息
  • 限定关联
  • 关联类与具体化类

可见性

通过设置可见性可以确定类内部的封装程度,决定其他类能否访问该类的元素。应用于类的属性和操作的可见性类型包括:

  • + public公共可见性: 该元素对任何类都可见;
  • - private私有可见性: 该元素只对本类可见;
  • # protected保护可见性: 该元素对本类及子类可见;
  • ~ package包可见性: 该元素对同一包中的元素可见.

在设计良好的面向对象系统中,大多数操作是公共的,大多数属性是私有的。

image-20231223135711089

public class Visibility {
    private int privateAttribute;
    public int publicAttribute;
    protected int protectedAttribute;
    int packageAttribute; // 默认访问修饰符,也称为包访问权限

    private void privateOperation() {
    }

    public void publicOperation() {
    }

    protected void protectedOperation() {
    }

    void packageOperation() { // 默认访问修饰符,也称为包访问权限
    }
}

( 1 )保护可见性

  • 保护可见性指类的属性或操作对本类及其子类可见。
  • 应用于继承的情况,允许子类对象访问父类对象中的属性和操作。

image-20231223140102952

( 2 )包可见性

  • 具有包可见性类的属性和操作能够被处于同一包中的其他类访问。
  • 包可见性是 Java 语言默认的可见性。

image-20231223140200495

( 3 )友元可见性友元可见性用于 c++语言。当一个类需要访问另一个类的私有属性或操作时,可以采用友元可见性,在授予友元关系的类中声明友元操作。

image-20231223140224108

class Book {
private:
    void putOnBookShelf();
};

class BookShelf {
public:
    friend void Book::putOnBookShelf();
};

导出信息

导出信息是指从其他元素计算得到的信息。它作为一种冗余信息,其目的是为了增加可读性和信息存取速度。导出信息包括导出属性导出关联。

  • 导出属性是指可以由其他信息导出的属性。
  • 导出关联是指由其它关联导出的关联。
  • 导出信息的 UML 表示是在导出属性名或导出关联名前面加上一条斜线“/”。

image-20231223140818360

限定关联

限定关联是一对多或多对多关联的约束条件表示。限定关联在二元关联的一端设定属性限定符,即在框中给出一个或多个属性用于约束条件。

image-20231223140934691

关联类与具体化类

如果两个类之间是多对多的关联,且每个关联实例都具有属性值,这时可以使用关联类。关联类具有一个约束,要求对于每一对链接起来的类实例,只能作为关联类的一个实例。如果不能满足这个约束的话,就需要将关联具体化,即使用一个具体化类来代替关联类。这个具体化类与之前的两个类均具有二元关联。

image-20231223141222186

接口与抽象类

接口

接口用于定义一个类或者构件的功能操作函数集。

  • 接口定义一组操作,但不定义操作的实现。
  • 接口一般不定义属性,只有定义操作,并且所有操作都隐含是公共的、抽象的可视性。
  • 在实现接口的类中,这些操作都要被实现。
  • 一个类可以实现多个接口,一个接口可以被多个类实现

image-20231223143811716

接口是一种特殊的类,所有的接口都是构造型为<<interface>>的类。在UML中,接口的表示方式有三种: 第一种是接口的图标表示,第二种是接口的修饰表示,第三种是接口的构造型标签表示。

image-20231223144103353

image-20231223144123081

  • 如果接口使用 构造型标签表示,实现关系线是一条末端带有空心三角的虚线,箭头指向接口,虚线上可以加上构造型关键字《implement》。
  • 如果接口使用图标表示,实现关系线则是一条实线,没有箭头,实线上也可以加上构造型关键字《implement》。
  • 一个类使用接口,表明该类依赖该接口,用指向接口的一条虚线箭头表示,箭头上可以加上构造型关键字《use》。

案例: IElectricalEquipment接口定义了turnOn操作和turnoff操作,TV类和Player类均实现了该接口,即实现了turnOn操作和turnoff操作,RemoteControl类使用了IElectricalEquipment接口。

image-20231223144513328

抽象类

抽象类指不具有实例的类,它通常作为父类,不创建实例对象。抽象类的子类可以实例化。

  • 抽象类一般至少有一个抽象操作。
  • 抽象类的作用是为其子类描述它们的公共属性和行为。
  • UML 采用斜体表示抽象类的名称,也可以使用 {abstract) 约束来表示。

image-20231223144739041

补充代码:

// 定义一个 Vehicle 基类
abstract class Vehicle {
    private String color; // 车辆颜色属性

    public Vehicle(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    // 定义一个抽象方法 drive
    public abstract void drive();
}

// Plane 类继承自 Vehicle
class Plane extends Vehicle {
    public Plane(String color) {
        super(color);
    }

    // 实现 drive 方法
    public void drive() {
        System.out.println("Plane is flying.");
    }
}

// Bus 类继承自 Vehicle
class Bus extends Vehicle {
    public Bus(String color) {
        super(color);
    }

    // 实现 drive 方法
    public void drive() {
        System.out.println("Bus is driving on the road.");
    }
}

// Driver 类与 Vehicle 有关联关系
class Driver {
    private Vehicle vehicle;

    // Driver 可以驾驶任何类型的 Vehicle
    public void driveVehicle(Vehicle vehicle) {
        this.vehicle = vehicle;
        vehicle.drive();
    }
}

接口与抽象类的区别

  • 抽象类是对一组具有相同属性和方法、逻辑上有关系的类抽象。
  • 接口是对一组具有相同属性和方法、逻辑上不相关的类抽象。
  • 抽象类可提供部分方法的代码实现,而接口不实现任何方法的代码实现。
  • 抽象类体现一种泛化关系,而接口体现契约关系。
  • 抽象是在问题域分析中发现,而是接口是在设计发现。

类内聚与耦合

类内聚是指类内部元素之间相关性的程度。类耦合是指类之间联系的程度。

  • 内聚和耦合彼此相关联,更强的内聚会导致更弱的耦合,反之亦然。

  • 良好的程序设计应确保在类的内聚与耦合之间达到良好平衡。

类内聚与耦合的启发规则

为了取得类内聚和类耦合之间的平衡, Riel提出了一些启发规则:

  • 属性及其相关操作封装在一个类中;
  • 类之间只通过公共接口产生依赖;
  • 每个类应该只实现单一职责功能;
  • 类中不存在一个操作子集只与一个属性子集相关的情况;
  • 系统功能应尽可能在各个类中均匀分配。

类耦合的种类

( 1 ) x 类有属性指向 Y 类的实例

( 2 ) x 类有方法引用了 Y 类的实例,如x 使用 Y 类对象作为局部变量;或者返回 Y 类对象

image-20231223145855463

( 3 ) x 类对象调用 Y 类对象方法发送消息

image-20231223150051051

( 4 ) Y 类是 X 类的直接或间接子类

image-20231223150243469

( 5 )X 类方法中输入参数为 Y 类的实例。

image-20231223150330305

( 6 ) Y 类是一个接口,而 X 类实现了该接口。

image-20231223150352801

类之间 6 种关系的耦合强度依次增强

1 )依赖关系一耦合度最弱

image-20231223150456224

2 )关联关系一耦合度其次弱

image-20231223150507795

3 )聚合关系一耦合度一般

image-20231223150609064

4 )组合关系一耦合度较高

image-20231223150621436

5 )继承关系一耦合度高

image-20231223150654467

6 )实现关系一耦合度最高

image-20231223150736520

迪米特法则应用

类耦合对于对象之间协作实现特定功能是必要的,但它应该尽量被限制在类的层次内,即层内耦合,层间耦合应该最小化。

为了限制类间任意通信,采用迪米特法则(最少知识原则)设计,指明在类操作中只允许将以下对象作为消息目标,即没有直接关系的类之间不交互通信,仅仅有直接关系类进行通信。

  • 当前对象本身
  • 当前操作中作为参数的对象
  • 当前对象的属性指向的对象
  • 当前操作创建的对象
  • 全局变量引用的对象

设计类图建模实践

  • 设计类图建模是在分析类图基础之上对系统分析类图进行细化与完善设计,从而在设计层面描述系统的静态结构。
  • 在设计类图中除了详细定义各种类属性与方法外,还需定义类之间关系的设计约束。

Power Designer 建模实践:雇员职位管理设计类图建模

image-20231223151832857

ChatGPT4:

  1. Person 类
    • 属性:name (String), sex (char)
    • 方法:getName() 返回 String,getSex() 返回 char
    • 这个类似乎代表了具有基本属性如姓名和性别的普通人。
  2. Employee 类(继承自 Person 类)
    • 属性:username (String), password (String)
    • 方法:usernamepassword 的获取和设置方法,一个接受用户名和密码并返回布尔值的 login() 方法,以及重写的 getName()getSex() 方法。
    • Employee 类继承自 Person 类,这意味着每个 Employee 都是 Person,但具有特定于雇员的额外属性和行为,如登录系统的凭证。
  3. Post 类
    • 属性:postname (String)
    • 方法:getPostname() 返回 String,setPostname() 接受一个 String,addPost() 返回一个布尔值。
    • 这个类似乎代表 Employee 可以持有的职位或职称。1..1 表示每个 Employee 有且仅有一个 Post
  4. Department 类
    • 属性:departmentname (String)
    • 方法:departmentname 的获取和设置方法,addDepartment() 返回布尔值。
    • 代表组织中的一个部门。每个 Employee 与一个 Department 关联,而一个 Department 可以有多个雇员(1..* 关系)。
  5. Power 类
    • 属性:powername (String)
    • 方法:powername 的获取和设置方法。
    • 代表与 Post 关联的权力或权限。每个 Post 有且仅有一个 Power,表示一对一的关系。
  6. Poweritem 类(继承自 Power 类)
    • 属性:itemname (String), isvalue (int)
    • 方法:itemnameisvalue 的获取和设置方法,addItem() 返回布尔值。
    • 这个类继承自 Power 类,暗示每个权限可以有多个 Poweritem 实例,这些是更具体的权限或许可。

类图还展示了类之间的关联关系:

  • 一个 Employee 与一个 Person 相关联(继承关系)。
  • 一个 Employee 可以有一个或多个 Post0..*1..1 的关系),但每个 Post 都确切地与一个 Employee 相关联。
  • 每个 Post 与一个 Power 关联,每个 Power 与一个 Post 关联(1..1 关系)。
  • 一个 Department 可以有多个 Employee 实例,但每个 Employee 都与一个 Department 相关联。

这个模型旨在管理组织中雇员的角色和责任。它捕捉了不同组织实体如人员、职位、部门以及与这些职位相关联的权限之间的层次结构和关系。该系统可用于跟踪员工的职位角色、所属部门及其在组织内的相应权限。使用继承和关联确保了清晰、可维护的结构,能够准确反映组织结构。

1 .分析说明如下模型中类之间设计该关系依据是什么?以及它们的耦合度高低。

2 .针对如下 UML 设计类图给出 Vehicle 类、 Car 类、 Bus 类的 Java 编程代码。

image.png

1)

2)

import java.util.ArrayList;
import java.util.List;

// Vehicle.java
public interface Vehicle {
    void run();
}

// Car.java
public class Car implements Vehicle {
    public String name;
    public List<Driver> drivers;

    public Car(String name) {
        this.name = name;
        this.drivers = new ArrayList<>();
    }

    @Override
    public void run() {
        // Car的run实现
    }

    public void beforeRun(Oil oil) {
        // Car的beforeRun实现
    }

    public void addDriver(Driver driver) {
        this.drivers.add(driver);
    }
}

// Bus.java
public class Bus extends Car {
    public Bus(String name) {
        super(name);
    }
    // Bus可能有特定的行为和属性,这里只提供基础构造方法
}

完整代码:

import java.util.ArrayList;
import java.util.List;

// Vehicle.java
interface Vehicle {
    void run();
}

// Car.java
class Car implements Vehicle {
    public String name;
    public List<Driver> drivers;

    public Car(String name) {
        this.name = name;
        this.drivers = new ArrayList<>();
    }

    @Override
    public void run() {
        // Car的run实现
    }

    public void beforeRun(Oil oil) {
        // Car的beforeRun实现
    }

    public void addDriver(Driver driver) {
        this.drivers.add(driver);
    }
}

// Bus.java
class Bus extends Car {
    public Bus(String name) {
        super(name);
    }
    // Bus可能有特定的行为和属性,这里只提供基础构造方法
}

// Driver.java
class Driver {
    public String name;
    public int age;
    public List<Car> cars;

    public Driver(String name, int age) {
        this.name = name;
        this.age = age;
        this.cars = new ArrayList<>();
    }

    public void addCar(Car car) {
        this.cars.add(car);
    }
}

// Oil.java
class Oil {
    public String type;

    public Oil(String type) {
        this.type = type;
    }

    public void add() {
        // Oil的add方法实现
    }
}

UML软件动态交互视图建模

软件动态交互视图用于描述系统功能的对象之间动态交互行为。顺序图和通信图均属于动态交互模型图。

顺序图

顺序图也称序列图,它按照从上至下顺序描述对象之间的消息交互,其目的是展示功能逻辑的设计实现。

  • 顺序图由一组对象及它们之间的消息组成,强调消息的先后时间顺序。
  • 顺序图也包括生命线、激活、片段等图形元素。

image-20231223163902262

通信图

通信图是另一种类型的动态交互图,它用于表现对象之间的协作关系。通信图由对象以及其关联组成,它强调对象间的连接关系,对象之间消息通信顺序使用序列号表示。

image-20231223165901323

类操作

类操作(方法)是指类所能提供的服务或功能函数。类将一组操作函数作为服务提供给系统中的其他类访问。

发现操作

  • 可从顺序图或通信图的对象交互中发现类操作。顺序图和通信图中的每一条消息,都必须有目标对象的一个操作为其服务。
  • 根据类的职责确定操作,如数据记录创建、读取、更新和删除操作。

image-20231223170220267

类操作的表示

在 UML 中,类操作的表示语法为:

[可见性] 操作名称 [(参数表)] [:返回类型] [{属性字符串}]

操作名称是动词或动词短语,单词首字母小写。如果有多个单词,则将这些单词连接起来,第一个单词首字母小写,其他单词首字母大写。

image-20231223170420445

ChatGPT4:

类图代表了使用数据传输对象(DTO)模式的用户注册系统设计。这种模式在面向对象设计中常用于软件应用子系统之间的数据传输。这里是类图的每个部分的详细解释:

UserDTO 类:

这是一个简单的 DTO 类,包含两个属性:

  • userAccount:一个 String 类型,用来存储用户的账户名。
  • userPassword:一个 String 类型,用来存储用户的密码。

它还提供了两个属性的获取和设置方法,允许其他类访问和修改这些值:

  • getUserAccount():返回 userAccount 字符串。
  • setUserAccount(String userAccount):设置 userAccount 字符串。
  • getUserPassword():返回 userPassword 字符串。
  • setUserPassword(String userPassword):设置 userPassword 字符串。

RegisterForm 类:

这个类似乎代表用户填写的注册表单。它包含:

  • userUserDTO 的一个实例,用来保存与用户相关的表单数据。
  • userDaoIUserDAO 接口的一个实例(下面会解释),可能用于将用户添加到某种类型的数据存储中。

IUserDAO 接口:

这是一个定义用户数据访问对象(DAO)契约的接口,包含以下方法:

  • addUser(UserDTO user):一个接受 UserDTO 对象并返回一个表示添加用户到数据存储是否成功的 boolean 值的方法。

OracleUserDAO 类:

这是 IUserDAO 接口的一个具体实现,专门设计用来与 Oracle 数据库交互。它实现了 IUserDAO 接口的 addUser 方法。

关系

  • RegisterFormUserDTO 有一个组合关系,由实心菱形表示,意味着 RegisterForm 包含并控制 UserDTO 实例的生命周期。
  • RegisterFormIUserDAO 有一个关联关系,由箭头表示,意味着它使用 IUserDAO 接口来访问用户数据。
  • IUserDAOOracleUserDAO 有一个实现关系,由虚线和空心箭头表示。这意味着 OracleUserDAO 承诺实现 IUserDAO 定义的契约。

案例:发现操作

image-20231223170723340

顺序图高级交互技术

创建与销毁临时对象

  • 当创建临时对象时,发送者发出一个实例化消息后将创建临时对象。
  • 当销毁临时对象时,发送者发出消息销毁临时对象,并回收其拥有的资源。
  • 在顺序图中,在对象的生命线上标记“×”符号,表示对象被销毁。

image-20231223171503752

片段

在模块功能详细设计中,需要描述功能处理的控制逻辑,如展示条件分支循环处理,以及并行执行。在顺序图中采用不同类型的片段来表示这些控制逻辑。

UML 2.x中预定义了如下片段:

  • opt选项片段
  • alt条件片段
  • loop循环片段
  • para并行片段

(1) loop循环片段

在模块功能逻辑设计中,使用loop片段表示重复执行一些操作。

image-20231223172428127

(2)para并行片段

在模块功能逻辑设计中,使用para片段表示执行一些并行操作。

image-20231223172616562

(3)alt条件片段

在模块功能逻辑设计中,使用alt片段表示根据条件执行操作。alt片段表达if-then-else逻辑。

image-20231223172942095

(4) opt选项片段

在模块功能逻辑设计中,使用opt片段根据当条件执行选项操作。opt片段表达if…then逻辑。

image-20231223173118811

交互引用

在设计有较多相同处理逻辑的模块时,可以采用交互引用复用已有处理逻辑,被引用的处理逻辑采用标签 ref 标注。

image-20231223173248501

在如下订单提交模块序列图模型中,包含了哪些处理逻辑?并分别说明各交互对象的对象类型。

image.png

订单结账和信用卡支付。

Customer、Payment Processor为参与者, Order Checkout、CreditCard Payment为控制类对象, Checkout Page为边界类对象 ,Order、OrderItem、Item为实体类对象。

UML软件状态机视图建模

状态机图是对系统中对象的状态变化逻辑进行建模,它描述类对象随时间变化的行为状态,以及对象受事件影响如何进行状态变迁。

状态机图建模步骤:

  • 发现对象状态
  • 分析对象变迁的事件与条件
  • 建立对象状态图

案例:微波炉工作的状态机图

image-20231223175114284

ChatGPT4:

状态:

  • Waiting(等待):微波炉的初始状态,显示时间,等待用户输入。
  • Full Power(全功率):微波炉在全功率模式下工作,功率设置为600W。
  • Half Power(半功率):微波炉在半功率模式下工作,功率设置为300W。
  • Set Time(设置时间):用户设置微波炉的工作时间。
  • Enabled(启用):微波炉门关闭,准备开始工作,显示“Ready”。
  • Disabled(禁用):微波炉门开启,不允许操作,显示“Waiting”。
  • Operation(操作):微波炉开始工作。

转换和触发事件:

  • 用户选择全功率或半功率,微波炉进入相应的功率状态。
  • 在任何功率状态下,设置时间后,微波炉进入设置时间的状态。
  • 当时间被设置后,如果微波炉门关闭,微波炉变为启用状态。
  • 如果微波炉门打开,微波炉将变为禁用状态。
  • 从启用状态,开始操作将使微波炉进入操作状态并开始加热。
  • 用户可以在任何时候取消操作,微波炉将返回到等待状态。
  • 在操作结束后,微波炉将完成加热过程并返回到等待状态。

针对如下电梯对象的状态机图模型,如何编写其Java控制程序?

image.png

代码均为简化代码,参考思路即可:

1.定义电梯的状态和事件:

public enum ElevatorState {
    IDLE,
    MOVING_UP,
    MOVING_DOWN,
    OPEN_DOORS,
    CLOSED_DOORS
}

public enum ElevatorEvent {
    CALL_UP,
    CALL_DOWN,
    MOVE_UP,
    MOVE_DOWN,
    DOORS_OPEN,
    DOORS_CLOSE,
    TIMEOUT
}

2.创建电梯的控制类

public class ElevatorControl {
    private ElevatorState currentState;

    public ElevatorControl() {
        this.currentState = ElevatorState.IDLE;
    }

    public void handleEvent(ElevatorEvent event) {
        switch (currentState) {
            case IDLE:
                switch (event) {
                    case CALL_UP:
                        openDoors();
                        currentState = ElevatorState.OPEN_DOORS;
                        break;
                    case CALL_DOWN:
                        // Similar logic for CALL_DOWN
                        break;
                    // Handle other events
                }
                break;
            case MOVING_UP:
                switch (event) {
                    case MOVE_UP:
                        // Logic for moving up
                        break;
                    case TIMEOUT:
                        currentState = ElevatorState.IDLE;
                        break;
                    // Handle other events
                }
                break;
            // Implement logic for other states
        }
    }

    private void openDoors() {
        // Logic to open elevator doors
        System.out.println("Doors are opening.");
    }

    // Define other methods such as closeDoors(), moveUp(), moveDown(), etc.
}

3.使用控制类响应事件

public class ElevatorSystem {
    public static void main(String[] args) {
        ElevatorControl elevator = new ElevatorControl();

        // Simulate events
        elevator.handleEvent(ElevatorEvent.CALL_UP);
        elevator.handleEvent(ElevatorEvent.DOORS_CLOSE);
        elevator.handleEvent(ElevatorEvent.MOVE_UP);
        // Add more events as needed
    }
}

UML 软件的实现视图建模

实现视图

实现视图是对系统的实现结构建模,包括如下模型图:

  • 构件图:建模构件和构件间的接口和依赖关系,表示系统构件组成结构;:
  • 部署图:建模在系统运行时,构件在系统各节点上的物理部署关系及其运行环境;
  • 子系统与包图:子系统可以用包来体现,可使用包图表示系统逻辑架构。

构件图

  • 构件图是用来表示系统中构件与构件之间、构件与接口之间关系的图。
  • 构件图可以从多种层次粒度描述系统实现架构,如系统分解哪几个子系统,子系统包括哪些构件,构件由哪些类封装组成。

构件和构件之间的依赖关系

image-20231223181134347

构件与接口之间的关系

  • 构件与构件之间通过定义良好的接口进行协作。
  • 构件图中,构件之间的关系表现为依赖关系实现关系
  • 实现关系指一个构件实现了一个接口,使用实线连接到小圆上。该接口被称为供给接口
  • 依赖关系指一个构件使用了一个接口,使用实线连接到半圆上。该接口被称为请求接口

image-20231223181425907

端口

端口是指构件一个对外交互点。端口显示为一个小正方形,放置在构件方框的边界上。

image-20231223181609946

端口连接

在 UML 软件架构建模设计中,主要使用如下连接器将构件的端口进行连接。

  • 装配连接器:用于连接构件之间的端口
  • 委托连接器:用于连接构件供给端口与内部供给端口或内部请求端口与构件请求端口

image-20231223182203179

作为一种选择, UML 还允许将连接器按如下方案连接构件端口。

image-20231223182641103

若有多个构件使用相同接口访问另一构件接口, UML 允许将连接器按如下方案连接构件端口。

image-20231223184949103

身份认证构件 (Authentication)端口与订单构件 (Orders) 端口均使用相同请求接口(lCustomers)连接客户构件(:Customers )端口的供给接口lCustomers。

委托连接器是将构件的端口连接到实现该行为的对象。它表示消息到达一个端口后,将被传递给内部目标对象进行处理。

image-20231223185338266

image-20231223185434470

案例:电子商务系统的构件图

image-20231223185547292

部署图

  • 部署图用于对系统的物理实现情况进行建模。
  • 部署图用于表示软件构件如何部署到硬件环境中,它显示系统中的不同构件在何处物理地运行,以及彼此之间如何进行通信。

节点

  • 节点是指人工制品和构件在系统中部署的硬件设备及其运行环境。
  • 节点之间通过通信线路将连接起来实现信息传输。

image-20231223190533597

构件在节点中部署

构件需要在节点设备中运行。在建模时,需要将构件在节点中进行部署

image-20231223190606027

案例:电子商务系统部署图

image-20231223190641805

包图

  • 在 UML 模型图中包用来对模型元素进行分组,简化 UML 图,使其更容易理解。
  • 包图用于对模型自身组织进行建模,它是由一系列模型元素(类、用例等)构成的包所组成的模型,描述包与包之间的关系。

UML 中包表示为左上方带有小矩形的矩形。包的名称采用字符串,简单名仅包含名称字符串,全限定名则以本包所处的外围包的名称作为前缀。

image-20231223191018148

包的可见性

包可以控制protected所包含的内部元素的可见性,即外部元素访问包内元素的权限:

  • + public公共可见性: 对它所在包和内嵌包的对象可见,也对所有引入它的包及其子包中对象可见;
  • - private私有可见性: 只对它所在包中对象可见;
  • # protected保护可见性: 对它所在包和内嵌包中对象可见;

image-20231223191331202

包的嵌套

  • 包可以拥有其他包,子包又可以拥有自己的子包,从而构成嵌套结构。
  • 包的嵌套可以清晰地表现模型元素之间的层次结构关系。
  • 但是包的嵌套不宜过深,层数一般以 2到 3 层为宜

image-20231223191441457

包图

  • 包图描述包与包之间的关系,可用于描述软件系统的逻辑模型。
  • 包与包之间可以存在依赖关系。两个包之间存在着依赖关系通常是指这两个包所包含的模型元素之间存在着一个或多个依赖关系。
  • 包的依赖关系使用一根虚线箭线表示,箭头指向被依赖的包。依赖关系可以标上标签,在《》中包含依赖关系的类型。

image-20231223191748930

案例:应用包图描述系统的软件架构

image-20231223191823052

image-20231223191939764

针对如下股票投资分析系统构件模型,分析构件之间关系如何设计?

image.png

ChatGPT4:

在这个股票投资分析系统的构件模型中,构件之间的关系是按照客户端-服务器架构设计的。客户端包含用户界面(GUI)、图形库(Graphing Lib)、客户端会话(Client Session)和股票组合(Stock Portfolio)构件。服务器端包含认证服务器(Authentication Server)、服务器会话(Server Session)、股票市场分析器(Stock Market Analyzer)和投资组合数据库(Portfolio DB)构件。

客户端的GUI用于展示信息和接收用户输入,图形库用于数据可视化。客户端会话管理用户与系统的交互,而股票组合构件负责管理用户的个人投资组合信息。这些客户端构件通过API与服务器端的构件交互。服务器端的认证服务器负责处理登录和安全认证,服务器会话维护与客户端的连接状态。股票市场分析器是系统的核心,负责分析股票市场数据和生成投资建议,而投资组合数据库存储用户的投资组合信息。

客户端与服务器之间的交互通过网络进行。例如,用户通过GUI发起请求,客户端会话将请求发送到服务器,服务器会话接收请求并与认证服务器进行认证,然后由股票市场分析器处理请求。结果通过服务器会话返回给客户端会话,并最终通过GUI展示给用户。

此外,系统还接入了第三方服务,如彭博数据库(Bloomberg DB),这提供了额外的市场数据,股票市场分析器可能会使用这些数据来增强分析结果的准确性。

针对如下房地产按揭交易系统部署模型,分析各要素之间关系如何设计?

image.png

针对如下电商系统软件架构模型,分析包之间关系如何设计?

image.png

《use》关系表示使用依赖,Web Shopping依赖Payment,Mobile Shopping依赖Payment…

《merge》关系表示合并,Web Shopping、Mobile Shopping、Phone Shopping、和Mail Shopping合并之后就拥有了Shopping Cart的全部功能 。也表明了它们共享一些在“Web Shopping”包中定义的通用功能。

《Access》关系表示私有引入,比如代码中的指定包名类名 。Payment可以访问Shopping Cart中的特定功能或类,但这种访问是受限的。

《Import》关系表示公共引入,比如Java中的import之后,就可以直接使用import包中的类了。一旦“Shopping Cart”公共导入了“Inventory”,它就可以直接使用“Inventory”中定义的公共类和接口。

图书借阅管理系统软件建模设计实践

系统需求

image-20231223200450359

image-20231223200521472

image-20231223200634562

系统静态模型设计

建立系统静态模型类图。建立类图需要确定系统中的类,确定类的属性,确疋类之间的关系。根据系统动态模型确定类的操作并完善类图。

( 1 )确定系统中的类

  • 系统用户有管理员 (Administrator) 、图书管理员 (Librarian) 和借阅者(Borrower) 三个参与者,可将他们抽取为的角色实体类。
  • 系统需存储图书信息,可抽取出Book类;需要存储借阅信息,可抽取出Loan类;需要存储预定信息,可抽取出Reservation类;这些类是系统中的主要业务实体类。

( 2 )确定类的属性

image-20231223200936822

( 3 )确定类之间的关系,创建系统类图

image-20231223201018670

系统动态模型设计

系统详细设计阶段,针对用例图中的每一个用例,考虑如何通过对象之间的协作来完成用例的功能。采用顺序图或通信图对用例功能进行设计建模,采用状态机图对类的行为状态进行设计建模。

图书搜索功能逻辑设计

image-20231223201152550

image-20231223201207722

图书借阅功能逻辑设计

image-20231223201319348

图书归还功能逻辑设计

image-20231223201354929

Book对象的状态机图设计

Book 对象是图书管理系统中具有明显状态变化的对象,图书借阅业务都与Book对象状态有关。Book对象具有如下状态: 新书 (NewBook) 、可借阅预定(Available) 、被预定 (Reserved) 、被借阅 (Borrowed) 、被删除 (Deleted)。

image-20231223201509834

Borrower对象的状态机图设计

Borrower对象也具有比较明显的状态,因此也采用状态机图对其建模。

Borrower 对象具 有 如下状态: 新建 (NewUser) 、未预定未借书 (NoLoanReserve) 、有预定 (HasReserve) 、有借书 (HasLoan) 、有预定有借书 (HasLoanReserve) 、被删除 (Deleted)。

image-20231223201645185

静态结构的类图模型细化设计

  • Borrower类需要提供

    • checkBorrower操作
    • checkNumBooks操作
    • addLoan操作
    • deleteLoan操作
  • Book类需要提供

    • searchBook操作
    • checkBook操作
    • setLoanStatus操作
  • Loan类需要提供

    • addLoan操作
    • deleteLoan操作
    • checkOverdue操作

image-20231223201905272

针对图书借阅管理系统如下构件,如何设计系统部署图模型,并在部署图中给出部署关系和运行环境(Apache、JBoss、PostgreSQL、Ubuntu)。

1)页面构件:borrowBook.jsp、returnBook.jsp、reserveBook.jsp、searchBook.jsp、viewBook.jsp、borrowerInfo.jsp,

2)控制构件:borrowBook.java、returnBook.java、reserveBook.java、searchBook.java、viewBook.java、borrowerInfo.java,

3)实体构件:book.java、reservation.java、loan.java、borrower.java,

4)数据库表:BookInfo、Reservation、Loan、Borrower、BookType

ChatGPT4:

我们可以按照三层架构来组织这些构件:

  • 表示层(通过Apache和JBoss托管): 包括所有的JSP页面,如 borrowBook.jsp, returnBook.jsp 等。
  • 业务逻辑层(运行在JBoss上): 包括控制和实体构件,如 borrowBook.java, book.java 等。
  • 数据层(使用PostgreSQL): 包含所有数据库表,如 BookInfo, Loan 等。

现在,我将详细设计一个系统部署图模型来展示这些构件是如何部署和相互作用的:

  1. Apache Web Server: 托管所有的JSP页面。这些页面通过HTTP请求与JBoss服务器通信。
  2. JBoss Application Server: 处理来自Apache服务器的请求,并运行Java控制和实体构件。JBoss可以部署这些Java构件,并提供必要的运行环境。
  3. PostgreSQL Database Server: 存储所有的数据库表,由JBoss服务器通过JDBC进行访问和操作。
  • Apache Web Server托管JSP页面,并将请求转发给JBoss服务器。
  • JBoss服务器处理业务逻辑,并与数据库服务器进行交互以存储或检索数据。
  • PostgreSQL数据库服务器存储和管理数据,为业务逻辑层提供数据支持。

所有这些组件都部署在Ubuntu操作系统上,确保系统兼容性和稳定性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1333436.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CSS3:绘制多边形

clip-path&#xff1a;该属性使用裁剪方式创建元素的可显示区域&#xff0c;区域内的显示&#xff0c;区域外的不显示。 构建一个三角形 <div class"mybox"></div><style>.mybox {width: 100px;height: 100px;background-color: yellow;clip-path…

Openai的openai新版本调用方式

最近大家有没有发现Openai的openai已经更新到1.6.1了,而且API的调用方式发生了巨大的变化,下面来看看openai新的调用方式吧。 欢迎关注公众号 module ‘openai’ has no attribute ChatCompletion. 提示openai的版本过低。(pip install -U openai) 1. Chat API from openai…

Spark Shell的简单使用

简介 Spark shell是一个特别适合快速开发Spark原型程序的工具&#xff0c;可以帮助我们熟悉Scala语言。即使你对Scala不熟悉&#xff0c;仍然可以使用这个工具。Spark shell使得用户可以和Spark集群交互&#xff0c;提交查询&#xff0c;这便于调试&#xff0c;也便于初学者使用…

【Java中创建对象的方式有哪些?】

✅Java中创建对象的方式有哪些&#xff1f; ✅使用New关键字✅使用反射机制✅使用clone方法✅使用反序列化✅使用方法句柄✅ 使用Unsafe分配内存 ✅使用New关键字 这是我们最常见的也是最简单的创建对象的方式&#xff0c;通过这种方式我们还可以调用任意的构造函数 (无参的和有…

Spring Boot学习随笔- 第一个Thymeleaf应用(基础语法th:,request、session作用域取值)

学习视频&#xff1a;【编程不良人】2021年SpringBoot最新最全教程 第十五章、Thymeleaf Thymeleaf是一种现代化的服务器端Java模板引擎&#xff0c;专门用于Web和独立环境。Thymeleaf在有网络和无网络的环境下皆可运行&#xff0c;即可以让美工在浏览器查看页面的静态效果&am…

数组元素反序

和前面的字符串逆向输出有异曲同工之妙 第一位和最后一位交换位置&#xff0c;然后用比大小循环 那么接下来修改一下这个程序&#xff0c;我们接下来解释一下p的概念 画图解释&#xff1a; 在最前面的 定义的时候&#xff0c;我们将p&#xff08;0&#xff09;定义在了1上&…

Ps:直方图 - 统计数据

使用扩展视图或全部通道视图时&#xff0c;直方图 Histogram的下方会显示一组实时统计数据。 提示&#xff1a; 要在直方图面板控制菜单中勾选&#xff08;默认&#xff09;“显示统计数据” Show Statistics。 源 Source --整个图像 Entire Image 默认选项。显示整个图像&am…

Spring 依赖注入概述、使用以及原理解析

前言 源码在我github的guide-spring仓库中&#xff0c;可以克隆下来 直接执行。 我们本文主要来介绍依赖注入的使用示例及其原理 依赖注入 什么是依赖注入 依赖注入&#xff08;Dependency Injection&#xff0c;简称DI&#xff09;是一种设计模式&#xff0c;它用于实现对…

【MySQL学习笔记008】多表查询

1、多表关系 概述&#xff1a;项目开发中&#xff0c;在进行数据库表结构设计时&#xff0c;会根据业务需求及业务模块之间的关系&#xff0c;分析并设计表结构&#xff0c;由于业务之间相互关联&#xff0c;所以各个表结构之间也存在着各种联系&#xff0c;基本上可分为三种&a…

在linux操作系统Centos上安装服务器相关软件

如果您的服务器没有图形界面(GUI),您可以通过命令行(终端)来安装和配置Tomcat、JDK和MySQL等软件。以下是在没有图形界面GHome的 Linux 系统上安装这些软件的基本步骤: 对于CentOS Stream 9,您可以按照以下步骤在命令行上安装Tomcat、JDK 和 MySQL 数据库: 1. 安装JD…

设计模式--迭代器模式

实验18&#xff1a;迭代器模式 本次实验属于模仿型实验&#xff0c;通过本次实验学生将掌握以下内容&#xff1a; 1、理解迭代器模式的动机&#xff0c;掌握该模式的结构&#xff1b; 2、能够利用迭代器模式解决实际问题。 [实验任务]&#xff1a;JAVA和C常见数据结构迭代…

基于遗传算法特征选择及单层感知机模型的IMDB电影评论文本分类案例

基于遗传算法特征选择及单层感知机模型的IMDB电影评论文本分类案例 1.数据载入及处理2.感知机模型建立3.模型训练4.遗传算法进行特征选择注意 5.联系我们 1.数据载入及处理 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import Dat…

线程的同步与互斥

抢票的例子 竞争过程 进程A被切走 进程B被切走 结论&#xff1a; 互斥 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); mutex: 指向要初始化的互斥锁的指针。attr: 用于设置互斥锁属性的指针&#xff0c;通常可以传入 NULL 以使用默认属性…

【贪心】最小生成树Kruskal算法Python实现

文章目录 [toc]问题描述最小生成树的性质证明 Kruskal算法时间复杂性Python实现 个人主页&#xff1a;丷从心 系列专栏&#xff1a;贪心算法 问题描述 设 G ( V , E ) G (V , E) G(V,E)是无向连通带权图&#xff0c; E E E中每条边 ( v , w ) (v , w) (v,w)的权为 c [ v ] …

【图神经网络 · 科研笔记5】异构信息网络,利用注意力选择元路径;利用进化邻域和社群实现自监督动态图嵌入,交叉监督对比学习;近期科研思维导图小汇总;

记录部分科研文献阅读相关内容【划重点】,主题“图神经网络”,仅学习使用。 🎯作者主页: 追光者♂🔥 🌸个人简介: 📝[1] CSDN 博客专家📝 🏆[2] 人工智能领域优质创作者🏆 🌟[3] 2023年城市之星领跑者TOP1(哈尔滨)🌿 🌿[4] 2022年度…

maven下载jar包失败

配置国内镜像 设置国内的仓库,比如: <!--阿里仓库--><mirror><id>alimaven</id><name>aliyun maven</name><url>https://maven.aliyun.com/repository/public/</url><mirrorOf>central</mirrorOf></mirror>…

CnosDB如何确保多步操作的最终一致性?

背景 在时序数据库中&#xff0c;资源的操作是一个复杂且关键的任务。这些操作通常涉及到多个步骤&#xff0c;每个步骤都可能会失败&#xff0c;导致资源处于不一致的状态。例如&#xff0c;一个用户可能想要在CnosDB集群中删除一个租户&#xff0c;这个操作可能需要删除租户…

Alnet网络分析与demo实例

参考自 up主的b站链接&#xff1a;霹雳吧啦Wz的个人空间-霹雳吧啦Wz个人主页-哔哩哔哩视频这位大佬的博客 Fun_机器学习,pytorch图像分类,工具箱-CSDN博客 数据集下载 http://download.tensorflow.org/example_images/flower_photos.tgz 包含 5 中类型的花&#xff0c;每种…

嵌入式开发——PWM高级定时器

学习目标 加强掌握PWM开发流程理解定时器与通道的关系掌握多通道配置策略掌握互补PWM配置策略掌握定时器查询方式掌握代码抽取优化策略掌握PWM调试方式学习内容 需求 点亮8个灯,采用pwm的方式。 定时器 通道 <

Netty-4-网络编程模式

我们经常听到各种各样的概念——阻塞、非阻塞、同步、异步&#xff0c;这些概念都与我们采用的网络编程模式有关。 例如&#xff0c;如果采用BIO网络编程模式&#xff0c;那么程序就具有阻塞、同步等特质。 诸如此类&#xff0c;不同的网络编程模式具有不同的特点&#xff0c…