Skip to content

第一章 软件设计基础

软件设计不是让代码跑起来,而是让它能活下来。

良好的软件设计是长期项目成功的基石。它决定了系统能否适应变化、团队能否高效协作、成本能否持续可控。

1 什么是良好的软件设计?

一个优秀的软件设计应具备以下五个核心特征:

特征说明
可维护性代码结构清晰,逻辑易懂,新人能快速上手,Bug 定位迅速。
可扩展性新功能可独立添加,不影响现有模块,避免“牵一发而动全身”。
可复用性模块或组件可在不同上下文中复用,减少重复造轮子。
可测试性代码解耦良好,易于编写单元测试与集成测试,保障质量。
性能在资源约束下满足响应时间、吞吐量等非功能性需求。

为什么重要?

良好的设计不是“锦上添花”,而是生存必需品

  • 可维护性 → 减少修复 Bug 的时间成本
  • 可扩展性 → 让产品能跟上市场变化
  • 可复用性 → 提升团队整体开发效率
  • 可测试性 → 构建对变更的“信心”
  • 性能 → 直接影响用户留存与体验

💡 一个设计良好的系统,即使最初功能简单,也能优雅地演化为复杂系统。

不良设计的代价

  • 单一巨型文件,职责混乱
  • 模块间强耦合,修改一处崩溃一片
  • 无接口抽象,替换实现成本极高
  • 缺乏测试,上线即“开盲盒”

这类系统最终会演变为“技术债务黑洞”,团队在恐惧中维护,效率持续下滑。

作为开发者,我们应始终保持对软件设计的好奇心,并努力提高设计质量。而遵从良好设计,我们才能更好的实现我们希望的软件目标。

2 设计原则与设计模式的关系

为了创建一个好的软件系统,设计原则和设计模式是两个重要的概念。

原则是“道”,模式是“术”。

设计原则包含5 大维度:

  1. 单一职责原则:一个类或模块只负责一个功能。
  2. 开放封闭原则:一个软件实体(类、模块、函数)应该对扩展开放,对修改关闭。
  3. 里氏替换原则:子类必须能够替换掉父类。
  4. 依赖倒置原则:抽象公共行为,将细节封装在子类中。
  5. 单一接口原则:一个接口只负责一个功能。

设计模式分为三大类:

  • 创建型模式:关注如何创建对象实例。
  • 结构型模式:关注如何组织对象之间的关系。
  • 行为型模式:关注如何协调对象之间的交互。
代码示例
java
import java.util.*;

// 行为型模式:策略模式
interface SortStrategy {
    void sort(List<Integer> list);
}
class BubbleSortStrategy implements SortStrategy { 
    // 这里继承父类,实现子类接口
    public void sort(List<Integer> list) {
        Collections.sort(list);
    }
}

// 结构型模式:外观模式
class SubSystemA { void operationA() { System.out.println("子系统A"); } }
class SubSystemB { void operationB() { System.out.println("子系统B"); } }
class Facade {
    // 组合对象, 组合对象的关系,这里通过组合,将多个对象组合在一起,从而实现具体的功能
    private SubSystemA a = new SubSystemA();
    private SubSystemB b = new SubSystemB();
    public void operation() { 
        a.operationA();
        b.operationB();
    }
}

public class DesignPattern {
    public static void main(String[] args) {
        // 创建型模式:创建对象
        List<Integer> list = new ArrayList<>(Arrays.asList(3, 1, 2));
        System.out.println("创建的列表: " + list);

        // 结构型模式:组织对象
        Facade facade = new Facade();
        facade.operation();

        // 行为型模式:对象交互
        SortStrategy strategy = new BubbleSortStrategy();
        strategy.sort(list);
        System.out.println("排序后的列表: " + list);
    }
}

本系列将只会介绍其中的 12 种经典设计模式,如果你还分不清这三类模式也不用担心,后续章节会结合实例详细讲解每种设计模式及其对应的设计原则。

将设计原则与设计模式联系起来,可以理解为:

维度设计原则设计模式
层级抽象、高层指导具体、可复用的解决方案
目的定义“什么是对的”解决“如何做”
灵活性普适,不依赖语言依赖语言特性与上下文

设计原则是思想,设计模式是满足设计原则的具体解决方案 —— 原则指导模式

例如:SOLID 原则催生了工厂、策略、观察者等模式

实例:单一职责原则(SRP) × 策略模式

java
// 违反 SRP:一个类处理多种支付逻辑
class PaymentService {
    public void pay(String method, double amount) {
        if (method.equals("alipay")) { ... }
        else if (method.equals("wechat")) { ... }
        else if (...) { ... }
    }
    // 可以发现这里存在多个 if-else 分支,对未来扩展造成了困难
    // 如果我们要添加新的支付方式,那么就要回到这里,既不利于扩展,也容易影响之前的逻辑
}

// 遵循 SRP + 策略模式
interface PaymentStrategy {
    void pay(double amount);
}

class AlipayStrategy implements PaymentStrategy { ... }
class WechatStrategy implements PaymentStrategy { ... }

class PaymentService {
    private PaymentStrategy strategy;
    public void setStrategy(PaymentStrategy s) { this.strategy = s; }
    public void pay(double amount) { strategy.pay(amount); }
}

策略模式不是为了“多态”而存在,而是为了满足 SRP 和开闭原则。

我们的目的不是增大类与类之间的复杂度,而通过将不同的支付方式封装成策略,根据使用的策略,选择执行对应的支付方式。这样新增支付方式时,只需添加新策略类,无需修改原有代码。


3 面向对象设计的核心思想

上面的无论是设计原则还是设计模式,最终都离不开面向对象设计这一核心思想。 面向对象(OOP)不是“类和对象”的语法糖,而是一种组织复杂性的思维范式

核心四要素

概念作用价值
抽象提取共性,隐藏细节降低认知负荷,聚焦核心逻辑
封装隐藏内部状态,暴露受控接口保护数据完整性,减少副作用
继承共享公共行为实现代码复用
多态同一接口,不同行为提升扩展性,支持运行时动态替换

示例:动物系统

java
// 抽象:定义公共契约
abstract class Animal {
    abstract void makeSound();
    void eat() { System.out.println("Eating..."); } // 共享实现
}

// 封装:私有数据 + 公共接口
class Dog extends Animal {
    private String name;
    public Dog(String name) { this.name = name; }
    @Override void makeSound() { System.out.println(name + " says: Woof!"); }
}

// 多态:同一接口,不同表现
Animal a1 = new Dog("Buddy");
Animal a2 = new Cat("Whiskers");

a1.makeSound(); // Woof!
a2.makeSound(); // Meow!

OOP 的真正收益

  • 模块化开发:团队可并行开发不同的模块,并独立测试
  • 高内聚低耦合:修改一个类,影响最小 ———— 在你写完一个功能后,就测试这个功能,并确保所有测试通过,这样才算一个功能完成。后面继续开发,就避免修改已经通过测试的功能。
  • 可插拔架构:轻松替换实现(如换支付方式、换数据库)

4 软件演化与可维护性的重要性

90% 的软件生命周期成本发生在维护阶段。

软件不是一次性的作品,而是一个持续进化的有机体。需求会变、用户会变、技术会变,而且在日新月异的网络环境下,新的漏洞会不断被发现,软件必须不断适应这些变化才能生存下去。

为什么可维护性是核心?

项目阶段成本占比目标
开发10–20%满足开发目标,实现用户需求
维护60–80%持续扩展和修改,满足新需求,同时保持原有功能的持续可用
重构10–15%对旧代码进行重构,提高可读性,提高可测试性以及可拓展性

要习惯于重构

多数情况下,重构需要的精力是远大于普通开发。因为你要确保重构后的代码功能不变,且能更好的进行后续的扩展。但长期来看,重构能显著提升代码质量,降低未来维护成本。这需要我们有良好的设计,才能在重构时避免引入新的问题。

良好设计如何支持演化?

场景不良设计良好设计
添加新支付方式修改 PaymentService 中的 if-else,风险高实现新 PaymentStrategy,注入即可,零改动旧代码
修复一个 Bug影响 5 个模块,需回归测试 30 个用例仅影响 1 个模块,单元测试覆盖,1 分钟验证
新人入职花 2 周读懂代码1 天看懂架构图 + 模块职责
graph LR
    A[开发 Development<br/>- 新功能实现<br/>- 遵循设计原则] --> B[维护 Maintenance<br/>- Bug修复<br/>- 性能优化<br/>- 功能增强]
    B --> C{代码质量下降?<br/>- 重复代码<br/>- 耦合度高<br/>- 难以扩展}
    C -->|是| D[重构 Refactoring<br/>- 优化设计<br/>- 应用设计模式<br/>- 提升可读性]
    D --> A
    C -->|否| B
    style A fill:#e1f5fe
    style B fill:#f3e5f5
    style D fill:#e8f5e8
    style C fill:#fff3e0

软件开发往往是在 开发 → 维护 → 重构 的循环中不断演进,满足不断变化的需求。设计良好的系统才能可持续发展

小结

良好设计不是“更复杂”,而是“更可演化”。 它让变化 局部化、可预测、低成本,从而在需求不断变化的现实中,保持系统长期可维护性和团队高效交付能力。