设计模式与软件架构学习笔记

从设计原则到经典模式,再到架构分层与微服务。每节讲清"什么场景用、怎么实现、有什么坑"。


1 · 设计原则

1.1 SOLID

S — 单一职责(Single Responsibility Principle)

一个类或函数应该只负责一件事。判断标准:如果这个类因为两个不同的原因需要修改,就违反了 SRP。

反面例子:一个 UserService 既处理登录,又处理订单,又发邮件。应该拆成 AuthService、OrderService、EmailService。

O — 开闭原则(Open-Closed Principle)

对扩展开放,对修改关闭。

例如支付系统,不要写成:

if (type == "alipay") { ... }
else if (type == "wechat") { ... }

应该抽象出 PaymentStrategy 接口,新增支付方式时只需新增实现类。

L — 里氏替换(Liskov Substitution Principle)

子类必须能替换父类而不影响程序正确性。

反面例子:Square extends Rectangle,但正方形的 setWidth 和 setHeight 会互相影响,破坏矩形约定。

I — 接口隔离(Interface Segregation Principle)

不要强迫客户端依赖它们不需要的方法。

反面例子:一个 Worker 接口有 work() 和 eat(),但机器人只需要 work(),却不得不实现 eat()。

D — 依赖倒置(Dependency Inversion Principle)

高层模块不应该依赖低层模块,二者都应依赖抽象。

例如:服务层依赖 UserRepository 接口,而不是直接依赖 MySQLUserRepository。

1.2 其他原则

  • DRY:Don't Repeat Yourself,消除重复代码
  • KISS:Keep It Simple, Stupid,简单即美
  • YAGNI:You Aren't Gonna Need It,不要为可能用不到的功能提前设计
  • 组合优于继承:优先用组合实现复用,继承是强耦合

2 · 创建型模式

2.1 单例模式(Singleton)

保证一个类只有一个实例。

饿汉式(线程安全,类加载时创建):

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return INSTANCE; }
}

懒汉式 + 双重检查锁:

public class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

为什么用 volatile? 防止指令重排序导致返回未初始化完成的对象。

注意点:反射和序列化会破坏单例,生产环境可用枚举单例或静态内部类。

2.2 工厂方法(Factory Method)

定义创建对象的接口,让子类决定实例化哪个类。

interface LoggerFactory {
    Logger createLogger();
}
class FileLoggerFactory implements LoggerFactory {
    public Logger createLogger() { return new FileLogger(); }
}
class DBLoggerFactory implements LoggerFactory {
    public Logger createLogger() { return new DBLogger(); }
}

2.3 抽象工厂(Abstract Factory)

创建一系列相关对象(产品族)。

例如 UI 组件:Windows 风格的 Button + TextBox,Mac 风格的 Button + TextBox。

interface UIFactory {
    Button createButton();
    TextBox createTextBox();
}

2.4 建造者模式(Builder)

当对象构造过程复杂、参数多时,用 Builder 一步步构建。

HttpRequest request = new HttpRequest.Builder()
    .url("https://api.example.com")
    .method("POST")
    .header("Content-Type", "application/json")
    .body("{}")
    .build();

2.5 原型模式(Prototype)

通过复制已有对象创建新对象。

class Document implements Cloneable {
    public Document clone() throws CloneNotSupportedException {
        return (Document) super.clone();
    }
}

注意深拷贝 vs 浅拷贝:浅拷贝只复制引用,深拷贝会递归复制对象内部。


3 · 结构型模式

3.1 适配器模式(Adapter)

把不兼容的接口转成兼容接口。

类比:Type-C 转 HDMI 转接头。

// 旧接口
class OldPrinter {
    void oldPrint(String s) { ... }
}

// 目标接口
interface Printer {
    void print(String s);
}

// 适配器
class PrinterAdapter implements Printer {
    private OldPrinter old;
    public void print(String s) { old.oldPrint(s); }
}

3.2 装饰器模式(Decorator)

动态给对象加功能,比继承灵活。

interface Coffee {
    double cost();
}

class SimpleCoffee implements Coffee {
    public double cost() { return 10; }
}

class MilkDecorator implements Coffee {
    private Coffee coffee;
    public double cost() { return coffee.cost() + 2; }
}

Java IO 流大量用了装饰器:BufferedInputStream(new FileInputStream("a.txt"))。

3.3 代理模式(Proxy)

控制对真实对象的访问。

类型:

  • 静态代理:编译期确定代理类
  • 动态代理:运行期生成代理(JDK 动态代理、CGLIB)
  • 远程代理:RPC 调用
  • 虚拟代理:懒加载
  • 保护代理:权限控制

Spring AOP 底层就是动态代理。

3.4 外观模式(Facade)

为一组复杂子系统提供简单统一的接口。

例如:

class OrderFacade {
    void placeOrder(Order order) {
        inventoryService.check(order);
        paymentService.pay(order);
        shippingService.ship(order);
    }
}

3.5 桥接模式(Bridge)

分离抽象和实现,让它们独立变化。

例如:图形 Shape 和颜色 Color 是两个维度。不用为每种形状 × 颜色都建一个类,而是让 Shape 持有 Color 引用。

3.6 组合模式(Composite)

统一处理单个对象和对象组合。

典型场景:文件系统(文件和文件夹都有 getSize())。

3.7 享元模式(Flyweight)

共享细粒度对象,减少内存占用。

典型场景:

  • 围棋棋子:只有黑白两色,坐标由外部传入
  • Java 字符串常量池
  • 线程池、连接池

4 · 行为型模式

4.1 观察者模式(Observer)

一对多依赖:主题状态变化时通知所有观察者。

interface Observer {
    void update(String msg);
}

class Subject {
    private List<Observer> observers = new ArrayList<>();
    void attach(Observer o) { observers.add(o); }
    void notify(String msg) {
        for (Observer o : observers) o.update(msg);
    }
}

应用:事件总线、消息订阅、MVVM 数据绑定、消息队列。

图 1 \xb7 观察者模式

4.2 策略模式(Strategy)

把算法族分别封装,让算法可以互相替换。

interface DiscountStrategy {
    double applyDiscount(double price);
}
class NormalDiscount implements DiscountStrategy {
    public double applyDiscount(double price) { return price; }
}
class VipDiscount implements DiscountStrategy {
    public double applyDiscount(double price) { return price * 0.8; }
}

配合简单工厂,可以消除大量 if-else。

4.3 模板方法(Template Method)

父类定义流程骨架,子类实现具体步骤。

abstract class Game {
    final void play() { init(); start(); end(); }
    abstract void init();
    abstract void start();
    abstract void end();
}

4.4 责任链模式(Chain of Responsibility)

请求沿链传递,每个节点决定处理或传递。

应用:中间件管道(Express、Koa)、审批流程、异常处理链。

4.5 命令模式(Command)

把请求封装成对象,支持撤销/重做、日志、队列。

应用:编辑器撤销/重做、线程池任务、遥控器按钮。

4.6 状态模式(State)

对象行为随状态改变。

例如自动售货机:空闲 → 投币 → 选择商品 → 出货 → 空闲。

4.7 迭代器模式(Iterator)

提供统一遍历接口,隐藏底层实现。

Java Iterator、C++ std::iterator。

4.8 中介者模式(Mediator)

集中管理多个对象之间的交互。

应用:聊天室(所有人发消息给聊天室,由聊天室转发)。

4.9 备忘录模式(Memento)

保存对象状态,支持恢复。

应用:游戏存档、编辑器历史状态。

4.10 访问者模式(Visitor)

在不修改类的前提下,为类添加新操作。

应用:AST 遍历、编译器 Visitor 模式。


5 · 架构分层

5.1 MVC

  • Model:数据和业务逻辑
  • View:界面展示
  • Controller:接收输入、调用 Model、更新 View

缺点:Controller 容易臃肿。

5.2 MVP

Presenter 替代 Controller,View 通过接口与 Presenter 交互。

适合:Android、Qt 客户端开发。

5.3 MVVM

ViewModel 通过数据绑定自动同步 View。

适合:Vue、Angular、WPF。

5.4 分层架构原则

  • 依赖方向:上层依赖下层
  • 同层之间不直接依赖
  • 通过接口防腐(Anti-Corruption Layer)
  • 避免跨层调用和环形依赖

6 · DDD 领域驱动设计

6.1 核心概念

  • 领域(Domain):业务问题空间
  • 限界上下文(Bounded Context):一个模型适用的边界
  • 聚合根(Aggregate Root):一致性边界,外部只能通过聚合根修改内部对象
  • 实体(Entity):有唯一标识,状态可变
  • 值对象(Value Object):无身份,不可变
  • 领域事件(Domain Event):用事件表达业务发生的事情

6.2 分层架构

接口层(Controller / API) ↓ 应用层(Application Service,编排领域对象) ↓ 领域层(Domain,核心业务逻辑) ↓ 基础设施层(Repository / Message / RPC 实现)

6.3 什么时候用 DDD

  • 业务复杂、规则多
  • 团队规模大,需要统一语言
  • 不适合简单 CRUD 系统

7 · 微服务架构

7.1 单体 vs 微服务

特性 单体 微服务
部署 一起部署 独立部署
扩展 整体扩 按服务扩
技术栈 统一 可异构
故障隔离 差 好
复杂度 低 高
数据一致性 容易 困难

7.2 微服务拆分原则

  • 按业务能力拆分,不按技术层拆分
  • 服务之间有清晰的边界和责任
  • 每个服务独立数据库
  • 避免分布式单体(只是把代码拆成多个 jar,还是一起部署)

7.3 关键组件

  • API 网关:统一入口、路由、鉴权、限流
  • 服务注册与发现:Consul、Nacos、Eureka
  • 配置中心:Apollo、Nacos、Spring Cloud Config
  • 服务通信:REST、gRPC、消息队列
  • 链路追踪:Jaeger、SkyWalking、Zipkin
  • 日志与监控:ELK、Prometheus + Grafana

7.4 分布式事务

  • 两阶段提交(2PC):强一致,性能差
  • TCC(Try-Confirm-Cancel):业务补偿
  • Saga:长事务,按正向/逆向流程执行
  • 本地消息表:最终一致,适合异步场景

7.5 微服务不是银弹

如果你的团队小于 10 人、业务不复杂,单体 + 模块化可能更合适。微服务带来的是运维复杂度的提升。