跳转至

2022 年软件系统设计考题解答

来源:exams/软统2022试卷.pdf「软件系统设计(A卷)」2022年6月 教师:张贺、潘敏学 | 闭卷考试 注:本试卷通过 OCR 提取并人工校对。


一、简答题(共60分,每题6分)

1. Please explain the Liskov Substitution Principle and how it contributes to the Open-Closed Principle. (6 points)

里氏代换原则(LSP)

里氏代换原则(Liskov Substitution Principle, LSP): - 所有引用基类(父类)的地方必须能透明地使用其子类的对象 - 即:如果对每一个类型为 S 的对象 o1,都有类型为 T 的对象 o2,使得以 T 定义的所有程序 P 在所有的对象 o1 都代换成 o2 时,程序 P 的行为没有变化,那么类型 S 是类型 T 的子类型

LSP 如何促进开闭原则(OCP)

LSP 是实现 OCP 的重要方式之一:

  1. OCP 要求:软件实体应当对扩展开放,对修改关闭
  2. LSP 是实现 OCP 的基础:因为使用基类对象的地方都可以使用子类对象,所以在程序中可以尽量使用基类类型来定义对象,而在运行时再确定其子类类型,用子类对象来替换父类对象
  3. 通过 LSP 实现扩展:当需要扩展功能时,只需添加新的子类(对扩展开放),而无需修改使用基类的客户端代码(对修改关闭)

示例:在策略模式中,Duck 类通过 FlyBehavior 接口引用具体策略,任何新的飞行行为(如 RocketFly)只需实现 FlyBehavior 接口即可,无需修改 Duck 类——这同时遵循了 LSP(子类可替换父类)和 OCP(对扩展开放,对修改关闭)。

参考课件slides/01面向对象设计原则.pdf(LSP 和 OCP),slides/02策略模式.pdf


2. Please explain how the Factory Method Pattern and Abstract Factory Pattern adhere to the Open-Closed Principle. (6 points)

工厂方法模式(Factory Method Pattern)

工厂方法模式通过以下方式遵循 OCP:

  • 定义一个用于创建对象的接口(Creator),让子类决定实例化哪一个类
  • 当需要新增一种产品时,只需添加一个新的 ConcreteCreator 子类和一个新的 ConcreteProduct 类,无需修改已有的 Creator 和 Product 代码
  • Creator 依赖于抽象的 Product 接口(依赖倒转),对具体产品类型的变化是封闭的,但对新增产品类型是开放的

抽象工厂模式(Abstract Factory Pattern)

抽象工厂模式通过以下方式遵循 OCP:

  • 提供一个创建一系列相关或相互依赖对象的接口,无需指定它们具体的类
  • 当需要支持新的一系列产品时(如新的 GUI 主题),只需添加一个新的 ConcreteFactory,无需修改已有的 AbstractFactory 接口和客户端代码
  • 客户端只依赖于 AbstractFactory 和 AbstractProduct 接口(依赖倒转)

共同的核心机制

两种工厂模式都通过抽象化 + 多态来实现 OCP: - 客户端面向抽象(接口/抽象类)编程 - 新功能通过添加新类实现(扩展),而非修改已有类(修改)

参考课件slides/03工厂模式.pdf(工厂方法模式和抽象工厂模式),slides/01面向对象设计原则.pdf(OCP)


3. What is the benefit of decoupling the Receiver from the Invoker in the Command Pattern? (6 points)

解耦 Receiver 和 Invoker 的好处

在命令模式中,Invoker(调用者)不直接知道 Receiver(接收者),而是通过 Command 对象间接调用:

  1. 易于扩展新命令:添加新命令只需实现 Command 接口,无需修改 Invoker 或 Receiver
  2. 支持撤销/重做(Undo/Redo):Command 对象可以存储操作的历史状态,支持撤销和恢复
  3. 支持命令队列和日志:命令可以被排队、记录日志,在系统崩溃后可重新执行
  4. 支持宏命令(组合命令):可以将多个 Command 组合成一个复合命令
  5. 降低耦合度:Invoker 只依赖 Command 接口,不依赖具体的 Receiver,可以在运行时动态替换
  6. 支持事务:可以将一系列命令作为一个原子操作

结构示意

Invoker → Command(接口) → ConcreteCommand → Receiver
         (调用者只知接口)    (具体命令封装调用)  (真正执行业务逻辑)

参考课件slides/05状态与命令模式.pdf(命令模式)


4. Observer Pattern: push model vs pull model. Trade-offs? (6 points)

Push Model(推送模型)

  • 方式:Subject 将所有数据作为参数推送给 Observer
  • 优点:Observer 实现简单,不需要再向 Subject 查询数据
  • 缺点
  • Subject 可能发送 Observer 不需要的数据
  • 当 Subject 数据字段增加时,需要修改 Observer 接口
  • 耦合度较高——Subject 需要了解 Observer 需要什么数据

Pull Model(拉取模型)

  • 方式:Subject 仅通知 Observer 状态已改变(最小通知),Observer 自己向 Subject 拉取所需数据
  • 优点
  • Observer 可以按需获取数据,灵活性更高
  • 当 Subject 添加新字段时,不需要修改已有的 Observer
  • 耦合度较低
  • 缺点
  • Observer 需要持有 Subject 引用
  • Observer 需要更多的代码来实现数据拉取

选择指导

场景 推荐模型
Subject 数据量小,所有 Observer 都需要全部数据 Push
不同 Observer 需要不同数据子集 Pull
Subject 数据结构经常变化 Pull
需要最小化 Observer 复杂度 Push

参考课件slides/06行为型模式.pdf(观察者模式)


5. Difference between Creational Patterns and Structural Patterns? What categories do Prototype and Flyweight fall into? Explain why. (6 points)

创建型模式 vs 结构型模式

维度 创建型模式(Creational) 结构型模式(Structural)
目的 主要用于创建对象 主要用于处理类或对象的组合
关注点 如何创建对象、隐藏创建逻辑 如何组合类和对象形成更大的结构
代表模式 工厂方法、抽象工厂、建造者、原型、单例 适配器、桥接、组合、装饰、外观、享元、代理

Prototype(原型模式)→ 创建型模式

  • 分类:创建型模式、对象模式
  • 原因:原型模式的核心目的是创建新对象——通过复制(克隆)原型实例来创建新对象,而不是通过 new。它关注的是对象的创建方式(如何创建),而非对象的结构组织。属于对象模式是因为处理的是对象之间的克隆关系

Flyweight(享元模式)→ 结构型模式

  • 分类:结构型模式、对象模式
  • 原因:享元模式的核心目的是组织对象的结构——通过共享技术有效地支持大量细粒度对象,减少内存占用。它关注的是如何组织已有对象(结构组织),而非如何创建新对象。属于对象模式是因为享元工厂管理的是对象实例

参考课件slides/00概述.pdf(设计模式分类表),slides/04创建型模式.pdf(原型模式),slides/09结构型模式.pdf(享元模式)


6. Describe the difference and relationship between software requirements, quality attributes, and ASR. (6 points)

定义与关系

软件需求(Software Requirements)
├── 功能性需求:系统必须做什么,说明系统如何为利益相关者提供价值
└── 非功能性需求(质量需求)
    ├── 质量属性(QA):系统在功能需求之上提供的整个系统的合乎需求的特性
    └── 约束:技术限制、商业限制、法律法规等
         └── ASR(架构攸关需求):对架构设计决策产生深远影响的需求

区别与联系

概念 定位
软件需求(Software Requirements) 最广泛的概念,包含功能性和非功能性需求
质量属性(Quality Attributes) 非功能性需求的一种反映,是系统在功能之上的理想特征(性能、可用性、安全性等)
ASR 质量属性要求越困难、越重要,就越有可能成为 ASR。如果需求影响关键架构设计决策,它就是一个 ASR

关键关系

非功能性需求/体系结构需求是质量属性的替代术语。质量属性由业务目标决定。QA → 重要的 QA → 成为 ASR。

参考课件slides/2026SUG_SysArch2_quality attributes.pdfslides/2026SUG_SysArch1_introduction.pdf


7. Define 'Availability' as a quality attribute. What do MTBF and MTTR stand for? How to calculate the availability (e.g., SLA)? (6 points)

可用性定义

可用性(Availability) 是系统在指定的时间间隔内,能够在要求的范围内提供指定服务的能力。可将可用性计算为在指定时间间隔内系统能提供指定服务的概率

MTBF 和 MTTR

缩写 全称 含义
MTBF Mean Time Between Failures(平均无故障时间) 系统正常运行的平均时间长度
MTTR Mean Time To Repair(平均维修时间) 系统从故障中恢复所需的平均时间

可用性计算公式

\[\text{Availability} = \frac{\text{MTBF}}{\text{MTBF} + \text{MTTR}}\]

示例:如果一个系统的 MTBF = 99 小时,MTTR = 1 小时,则:

\[\text{Availability} = \frac{99}{99 + 1} = 0.99 = 99\%\]

这对应 SLA 中的"两个9"(99% 可用性)。常见的 SLA 等级: - 99%(两个9):年停机时间约 3.65 天 - 99.9%(三个9):年停机时间约 8.76 小时 - 99.99%(四个9):年停机时间约 52.6 分钟 - 99.999%(五个9):年停机时间约 5.26 分钟

参考课件slides/2026SUG_SysArch2_quality attributes.pdf(可用性质量属性)


8. What are the generic design strategies applied in designing software? Give a concise working example with software architecture for these strategies. (6 points)

六种通用设计策略:

策略 示例
分解(Decomposition) 将整个系统分解为前端展示层、业务逻辑层、数据访问层(三层架构)
抽象(Abstraction) 将系统抽象为组件(Component)和连接器(Connector),屏蔽实现细节
逐步求精/分而治之 对每个模块分别设计:先定义接口,再逐步细化实现
生成与测试(Generate and Test) 设计评审后针对关键路径生成测试用例进行验证
迭代-增量细化 使用 ADD 方法多次迭代直到满足所有 ASR
复用元素(Reuse of Elements) 重用现有架构模式如 MVC、分层架构、消息队列等

参考课件slides/Lecture 01 - Attributes Driven Design.pdf


9. Why should a software architecture be documented using different views? Give the names and purposes of four example views. (6 points)

为什么需要不同视图

  1. 不同视图支持不同的目标和用户,突出不同的系统元素和关系
  2. 不同视图将不同质量属性暴露出不同的程度
  3. 单一视图无法描述软件架构的全部内容
  4. 分离关注点(Separation of Concerns),管理架构复杂性

四种视图及其目的

视图 目的
逻辑视图(Logical View) 描述对架构重要的元素及其关系(功能需求),面向最终用户
过程视图(Process View) 描述元素间的并发和交互(进程、线程、同步),面向系统集成者
开发视图(Development View) 描述软件模块的内部组织联系,面向程序员和开发团队
物理视图(Physical View) 描述主要过程和组件如何映射到硬件,面向系统工程师

参考课件slides/2026SUG_SysArch1_introduction.pdf(架构视图)


10. What are the risks, sensitivity points, and trade-off points? Give an example for each. (6 points)

概念 定义 示例
风险(Risk) 可能对所需质量属性产生负面影响的架构决策 使用分层模式可能带来性能损耗,因为每层调用会增加额外开销
敏感点(Sensitivity Point) 特定质量属性对其敏感的架构决策——微小变化导致质量属性显著变化 在性能敏感的系统中,决定在某处使用缓存中间件——缓存命中率的微小变化会显著影响系统响应时间
权衡点(Trade-off Point) 影响多个质量属性的架构决策,改善一个可能损害另一个 使用分层模式带来性能损耗(性能↓),但同时解耦增加系统可修改性(可修改性↑)

参考课件slides/Lecture 01 - Attributes Driven Design.pdf(ATAM),slides/Lecture 02 - Attributes Driven Design - Case Study.pdf


二、设计题(共40分)

1. Multi-instance Pattern 设计 (10 points)

要求:设计 Multi-instance 模式,确保系统中某个类的对象数量不超过给定的常数 n。

设计思路

Multi-instance 模式是单例模式的推广——允许创建固定数量(n 个)的实例。核心机制: - 使用对象池(Object Pool) 管理有限数量的实例 - 通过静态计数器跟踪已创建的实例数 - 提供静态工厂方法获取实例

类图

┌──────────────────────────────┐
│      MultiInstance           │
├──────────────────────────────┤
│ - static instances:          │
│   List<MultiInstance>        │
│ - static MAX_INSTANCES: int  │
│ - static count: int          │
│ - id: int                    │
├──────────────────────────────┤
│ - MultiInstance() // private │
│ + static getInstance():      │
│   MultiInstance              │
│ + static releaseInstance(    │
│   instance): void            │
│ + getId(): int               │
└──────────────────────────────┘

代码实现

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

public class MultiInstance {
    private static final int MAX_INSTANCES = 5; // n
    private static List<MultiInstance> instances = new ArrayList<>();
    private static List<Boolean> available = new ArrayList<>();
    private static int currentCount = 0;
    private int id;

    // 私有构造函数
    private MultiInstance() {
        this.id = currentCount;
    }

    // 获取一个可用实例
    public static synchronized MultiInstance getInstance() {
        // 查找是否有已释放的可用实例
        for (int i = 0; i < instances.size(); i++) {
            if (available.get(i)) {
                available.set(i, false);
                return instances.get(i);
            }
        }
        // 如果还有配额,创建新实例
        if (currentCount < MAX_INSTANCES) {
            MultiInstance instance = new MultiInstance();
            instances.add(instance);
            available.add(false);
            currentCount++;
            return instance;
        }
        // 达到上限,返回 null 或抛出异常
        throw new IllegalStateException(
            "Maximum instances (" + MAX_INSTANCES + ") exceeded");
    }

    // 释放实例,使其可被重用
    public static synchronized void releaseInstance(MultiInstance instance) {
        int index = instances.indexOf(instance);
        if (index >= 0) {
            available.set(index, true);
        }
    }

    public int getId() { return id; }
}

参考课件slides/04创建型模式.pdf(单例模式),slides/补充-单例模式.pdf


2. OA 通知系统设计 (10 points)

要求:设计 OA 系统,存储公司部门和人,形成树状结构。发通知时可以通知整个部门的所有人或指定个别人员。

设计模式:组合模式(Composite Pattern)

选择理由:部门和人员形成部分-整体层次(树状结构),客户端需要统一对待单个对象和组合对象

类图

┌──────────────────────────┐
│    <<interface>>          │
│    Notifiable             │
├──────────────────────────┤
│ + sendNotice(msg: String) │
│ + add(child: Notifiable)  │
│ + remove(child:           │
│   Notifiable)             │
│ + getName(): String       │
└──────┬───────────────────┘
  ┌────┴──────────────┐
  │                   │
┌─┴──────────────┐  ┌─┴──────────────┐
│  Department    │  │    Person      │
│  (部门-组合节点)│  │   (人员-叶节点) │
├────────────────┤  ├────────────────┤
│ - name: String  │  │ - name: String  │
│ - children:     │  │ - title: String │
│   List<Notifiable>│ ├────────────────┤
├────────────────┤  │+ sendNotice()   │
│+ sendNotice(): │  │+ add() -        │
│  递归发送给所有子 │  │  Unsupported    │
│  节点            │  │+ remove() -     │
│+ add()          │  │  Unsupported    │
│+ remove()       │  │+ getName()      │
│+ getName()      │  └────────────────┘
└────────────────┘

┌──────────────────────────────────────┐
│              OASystem                │
├──────────────────────────────────────┤
│ - root: Notifiable                   │
│ - targetList: List<Notifiable>       │
├──────────────────────────────────────┤
│ + issueNotice(targets, msg): void    │
│ + displayNotices(person): void       │
└──────────────────────────────────────┘

代码实现

// 接口
interface Notifiable {
    void sendNotice(String message);
    default void add(Notifiable child) {
        throw new UnsupportedOperationException();
    }
    default void remove(Notifiable child) {
        throw new UnsupportedOperationException();
    }
    String getName();
}

// 部门(组合节点)
class Department implements Notifiable {
    private String name;
    private List<Notifiable> children = new ArrayList<>();

    public Department(String name) { this.name = name; }

    public void sendNotice(String message) {
        System.out.println("[部门] " + name + " 收到通知: " + message);
        for (Notifiable child : children) {
            child.sendNotice(message);
        }
    }

    public void add(Notifiable child) { children.add(child); }
    public void remove(Notifiable child) { children.remove(child); }
    public String getName() { return name; }
}

// 人员(叶节点)
class Person implements Notifiable {
    private String name;
    private String title;
    private List<String> notices = new ArrayList<>();

    public Person(String name, String title) {
        this.name = name;
        this.title = title;
    }

    public void sendNotice(String message) {
        notices.add(message);
        System.out.println("[" + title + "] " + name + " 收到通知: " + message);
    }

    public String getName() { return name; }
    public List<String> getNotices() { return notices; }
}

// OA 系统
class OASystem {
    private Department root;

    public OASystem() {
        root = new Department("公司总部");
    }

    public void issueNotice(List<Notifiable> targets, String message) {
        for (Notifiable target : targets) {
            target.sendNotice(message);
        }
    }

    public Department getRoot() { return root; }
}

// 使用示例
// Department tech = new Department("技术部");
// Person alice = new Person("Alice", "工程师");
// tech.add(alice);
// oa.issueNotice(Arrays.asList(tech, bob), "系统升级通知");

参考课件slides/07适配器与组合.pdf(组合模式)


3. Distributed Cache Updates 设计题 (20 points)

场景:在线客服系统,N 个服务器集群,每个服务器有缓存。要求: - REQ1:缓存变更提交到数据库 - REQ2:所有其他缓存状态必须失效并从数据库重新加载

(a) 识别额外需要的组件 (5 points)

额外组件:Invalidation Coordinator(失效协调器)

组件名称 主要功能
InvalidationCoordinator(失效协调器) ① 接收缓存变更通知 ② 向所有其他缓存广播失效消息 ③ 管理分布式缓存的一致状态
替代方案:可用 Message Broker(消息代理)Event Bus(事件总线) 使用发布-订阅模式,当一个缓存变更时发布事件,其他缓存订阅失效事件

(b) 将 Observer 模式角色映射到组件 (5 points)

Observer 模式角色 映射到的组件
Subject(主题/被观察者) 被修改的 Cache(缓存)+ InvalidationCoordinator
Observer(观察者) 所有其他 Server 的 Cache
ConcreteSubject 发生变更的具体 Cache 实例
ConcreteObserver 其他需要失效并重新加载的 Cache 实例
notifyObserver() InvalidationCoordinator 广播失效消息
update() 各个 Cache 的 invalidate() + reload()

(c) 序列图 (5 points)

Client    Server1    Cache1    Invalidation    Cache2    Database
  │          │          │       Coordinator       │          │
  │ 请求修改  │          │            │            │          │
  │─────────▶│          │            │            │          │
  │          │ 更新数据  │            │            │          │
  │          │─────────▶│            │            │          │
  │          │          │ 提交变更   │            │          │
  │          │          │─────────────────────────│─────────▶│
  │          │          │            │            │  write   │
  │          │          │◀─────────────────────────│──────────│
  │          │          │ notify(invalidation)     │          │
  │          │          │───────────▶│            │          │
  │          │          │            │ broadcast  │          │
  │          │          │            │ invalidate │          │
  │          │          │            │───────────▶│          │
  │          │          │            │            │ invalidate│
  │          │          │            │            │ reload   │
  │          │          │            │            │─────────▶│
  │          │          │            │            │◀─────────│
  │◀─────────│──────────│            │            │          │
  │ 返回结果  │          │            │            │          │

(d) Connectors/Web Services 解决异构缓存访问 (5 points)

问题:不同服务器使用不同的协议(Java RMI、HTTP JSP、SOAP),缓存接口不一致。

解决方案:使用 Connector 模式 + Web Service 封装

方案 描述
Connector 模式 为每种协议创建对应的 Connector 适配器(如 RmiConnectorHttpConnectorSoapConnector),每个 Connector 实现统一的 CacheConnector 接口,封装协议差异
Web Service 方案 将每个 Cache 封装为标准的 RESTful Web Service,使用统一的 HTTP + JSON 接口。所有服务器通过 HTTP 协议调用缓存操作(GET /cache/{key}POST /cache/invalidate),屏蔽底层实现差异

Connector 模式设计

// 统一接口
interface CacheConnector {
    void invalidate(String key);
    Object load(String key);
    void update(String key, Object value);
}

// 适配不同协议
class RmiCacheConnector implements CacheConnector { /* Java RMI */ }
class HttpCacheConnector implements CacheConnector { /* HTTP/JSP */ }
class SoapCacheConnector implements CacheConnector { /* SOAP */ }

// InvalidationCoordinator 使用统一接口
class InvalidationCoordinator {
    private Map<String, CacheConnector> connectors;

    public void broadcastInvalidation(String key) {
        for (CacheConnector conn : connectors.values()) {
            conn.invalidate(key);
        }
    }
}

关键思想:通过统一的接口抽象(Connector 模式或 Web Service 封装),使得 InvalidationCoordinator 无需关心每个缓存的底层协议差异。

参考课件slides/软件架构模式_update_2.pdf(Broker/Connector),slides/06行为型模式.pdf(观察者模式),slides/Lecture 01 - Attributes Driven Design.pdf(架构战术)


综合参考exams/软件系统设计-复习-EagleBear.pdf(综合复习资料)