23种设计模式-示例讲解

64 阅读42分钟

前言

设计模式是解决特定问题的成熟方案。分为三大类:创建型(怎么造对象)、结构型(怎么组合对象)、行为型(对象间怎么协作)。


一、创建型模式(5种)

1. 单例模式 (Singleton)

需求场景:系统需要一个全局的配置管理器,要求:

  • 整个应用只有一个配置实例
  • 任何地方都能访问到同一份配置
  • 避免重复创建造成资源浪费

为什么用单例而不是其他方案?

  • ❌ 静态类:无法实现接口,难以扩展和测试
  • ❌ 全局变量:不安全,可能被意外修改
  • ✅ 单例模式:既保证唯一性,又保持面向对象特性
class ConfigManager {
    private static ConfigManager instance;
    private Properties config;
    
    private ConfigManager() {
        config = new Properties();
        // 加载配置文件
    }
    
    public static ConfigManager getInstance() {
        if (instance == null) {
            instance = new ConfigManager();
        }
        return instance;
    }
    
    public String getProperty(String key) {
        return config.getProperty(key);
    }
}

// 使用 - 全局访问同一实例
ConfigManager.getInstance().getProperty("database.url");
ConfigManager.getInstance().getProperty("app.name");

核心价值:保证全局唯一实例,统一访问点


2. 工厂方法模式 (Factory Method)

需求场景:开发一个日志系统,需要支持多种日志输出方式(文件、数据库、控制台),要求:

  • 根据配置动态选择日志输出方式
  • 新增日志类型时不修改现有代码
  • 客户端不需要知道具体日志类的实现

为什么用工厂方法而不是直接new?

  • ❌ 直接new:客户端与具体类耦合,难以扩展
  • ❌ 简单if-else:新增类型需要修改现有代码
  • ✅ 工厂方法:每种产品有专门工厂,符合开闭原则
// 产品接口
interface Logger {
    void log(String message);
}

// 具体产品
class FileLogger implements Logger {
    public void log(String message) {
        System.out.println("[文件] " + message);
    }
}

class DatabaseLogger implements Logger {
    public void log(String message) {
        System.out.println("[数据库] " + message);
    }
}

// 工厂接口
abstract class LoggerFactory {
    abstract Logger createLogger();
}

// 具体工厂 - 每个工厂专门创建一种产品
class FileLoggerFactory extends LoggerFactory {
    Logger createLogger() { return new FileLogger(); }
}

class DatabaseLoggerFactory extends LoggerFactory {
    Logger createLogger() { return new DatabaseLogger(); }
}

// 客户端 - 根据配置选择工厂
String logType = "file"; // 来自配置文件
LoggerFactory factory = "file".equals(logType) ? 
    new FileLoggerFactory() : new DatabaseLoggerFactory();

Logger logger = factory.createLogger();
logger.log("系统启动");

核心价值:每种产品有专门工厂,便于扩展新产品


3. 抽象工厂模式 (Abstract Factory)

需求场景:开发跨平台UI应用,需要为不同操作系统创建一套风格一致的UI组件,要求:

  • 确保同一平台的组件风格统一
  • 切换平台时整套组件都要切换
  • 新增平台支持时不影响现有代码

为什么用抽象工厂而不是工厂方法?

  • ❌ 工厂方法:只能保证单个产品的创建,无法保证产品间的一致性
  • ❌ 直接创建:容易出现Windows按钮配Mac输入框的情况
  • ✅ 抽象工厂:确保创建的是同一系列的产品
// 产品接口
interface Button { void render(); }
interface TextBox { void display(); }

// Windows系列产品
class WinButton implements Button {
    public void render() { System.out.println("Windows风格按钮"); }
}
class WinTextBox implements TextBox {
    public void display() { System.out.println("Windows风格输入框"); }
}

// Mac系列产品
class MacButton implements Button {
    public void render() { System.out.println("Mac风格按钮"); }
}
class MacTextBox implements TextBox {
    public void display() { System.out.println("Mac风格输入框"); }
}

// 抽象工厂 - 创建一系列相关产品
interface UIFactory {
    Button createButton();
    TextBox createTextBox();
}

// 具体工厂 - 保证创建的产品风格一致
class WindowsFactory implements UIFactory {
    public Button createButton() { return new WinButton(); }
    public TextBox createTextBox() { return new WinTextBox(); }
}

class MacFactory implements UIFactory {
    public Button createButton() { return new MacButton(); }
    public TextBox createTextBox() { return new MacTextBox(); }
}

// 使用 - 一次性创建整套组件
String os = System.getProperty("os.name");
UIFactory factory = os.contains("Windows") ? 
    new WindowsFactory() : new MacFactory();

Button btn = factory.createButton();
TextBox text = factory.createTextBox();
// 确保风格一致

核心价值:创建一系列相关产品,保证产品间的兼容性


4. 建造者模式 (Builder)

需求场景:构建SQL查询语句,需要支持复杂的查询条件组合,要求:

  • 参数很多且大部分可选
  • 构建过程要清晰易读
  • 支持链式调用提高代码可读性

为什么用建造者而不是构造函数?

  • ❌ 多个构造函数:参数过多时容易出错,可读性差
  • ❌ setXxx方法:对象可能处于不完整状态
  • ✅ 建造者模式:分步构建,参数含义清晰
class SqlQuery {
    private String select;
    private String from;
    private String where;
    private String orderBy;
    private int limit;
    
    private SqlQuery() {}
    
    public static class Builder {
        private SqlQuery query = new SqlQuery();
        
        public Builder select(String fields) {
            query.select = fields;
            return this;
        }
        
        public Builder from(String table) {
            query.from = table;
            return this;
        }
        
        public Builder where(String condition) {
            query.where = condition;
            return this;
        }
        
        public Builder orderBy(String field) {
            query.orderBy = field;
            return this;
        }
        
        public Builder limit(int count) {
            query.limit = count;
            return this;
        }
        
        public SqlQuery build() {
            if (query.select == null || query.from == null) {
                throw new IllegalStateException("SELECT和FROM是必需的");
            }
            return query;
        }
    }
    
    @Override
    public String toString() {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT ").append(select)
           .append(" FROM ").append(from);
        if (where != null) sql.append(" WHERE ").append(where);
        if (orderBy != null) sql.append(" ORDER BY ").append(orderBy);
        if (limit > 0) sql.append(" LIMIT ").append(limit);
        return sql.toString();
    }
}

// 使用 - 清晰的链式构建
SqlQuery query = new SqlQuery.Builder()
    .select("name, age")
    .from("users")
    .where("age > 18")
    .orderBy("name")
    .limit(10)
    .build();

System.out.println(query); // SELECT name, age FROM users WHERE age > 18 ORDER BY name LIMIT 10

核心价值:分步构建复杂对象,提高代码可读性


5. 原型模式 (Prototype)

需求场景:游戏中需要创建大量相似的敌人,每个敌人的基础属性相同,但位置不同,要求:

  • 避免重复的复杂初始化过程
  • 快速创建大量相似对象
  • 支持深拷贝避免属性共享问题

为什么用原型而不是直接new?

  • ❌ 直接new:每次都要重复复杂的初始化逻辑
  • ❌ 工厂方法:仍然需要重复创建过程
  • ✅ 原型模式:通过复制避免重复初始化,性能更好
class Enemy implements Cloneable {
    private String type;
    private int health;
    private Position position;
    private List<String> skills;
    
    public Enemy(String type) {
        this.type = type;
        // 复杂的初始化过程
        initializeStats();
        loadSkills();
    }
    
    private void initializeStats() {
        // 从数据库或配置文件加载属性
        this.health = 100;
    }
    
    private void loadSkills() {
        // 加载技能列表
        this.skills = Arrays.asList("攻击", "防御");
    }
    
    @Override
    public Enemy clone() {
        try {
            Enemy cloned = (Enemy) super.clone();
            // 深拷贝
            cloned.position = new Position(position.x, position.y);
            cloned.skills = new ArrayList<>(skills);
            return cloned;
        } catch (CloneNotSupportedException e) {
            return null;
        }
    }
    
    public void setPosition(int x, int y) {
        this.position = new Position(x, y);
    }
}

// 使用 - 快速创建多个敌人
Enemy zombiePrototype = new Enemy("僵尸"); // 复杂初始化只执行一次

// 通过克隆快速创建
Enemy zombie1 = zombiePrototype.clone();
zombie1.setPosition(10, 20);

Enemy zombie2 = zombiePrototype.clone(); 
zombie2.setPosition(30, 40);

核心价值:通过复制避免重复的复杂对象创建过程


二、结构型模式(7种)

6. 适配器模式 (Adapter)

需求场景:系统需要集成第三方支付SDK,但第三方接口与系统现有接口不兼容,要求:

  • 不修改第三方SDK代码
  • 不修改现有系统接口
  • 让不兼容的接口能够协同工作

为什么用适配器而不是直接修改?

  • ❌ 修改第三方代码:版本升级时会丢失修改
  • ❌ 修改现有接口:影响其他已有实现
  • ✅ 适配器模式:在中间做转换,不影响双方
// 系统现有的支付接口
interface PaymentProcessor {
    boolean processPayment(String userId, double amount);
}

// 第三方支付SDK(不能修改)
class ThirdPartyPayment {
    public String makePayment(String account, String money, String currency) {
        System.out.println("第三方支付:账户" + account + " 金额" + money + currency);
        return "SUCCESS";
    }
}

// 适配器 - 转换接口调用
class PaymentAdapter implements PaymentProcessor {
    private ThirdPartyPayment thirdPartyPayment;
    
    public PaymentAdapter(ThirdPartyPayment thirdPartyPayment) {
        this.thirdPartyPayment = thirdPartyPayment;
    }
    
    @Override
    public boolean processPayment(String userId, double amount) {
        // 接口转换:系统接口 -> 第三方接口
        String account = "USER_" + userId;
        String money = String.valueOf(amount);
        String result = thirdPartyPayment.makePayment(account, money, "CNY");
        return "SUCCESS".equals(result);
    }
}

// 使用 - 统一的接口调用
PaymentProcessor processor = new PaymentAdapter(new ThirdPartyPayment());
boolean success = processor.processPayment("12345", 99.9);

核心价值:让不兼容的接口能够协同工作


7. 装饰器模式 (Decorator)

需求场景:开发一个文本处理系统,需要对文本进行多种格式化处理(加密、压缩、编码),要求:

  • 可以任意组合不同的处理方式
  • 动态添加或移除处理功能
  • 不修改原始文本类的代码

为什么用装饰器而不是继承?

  • ❌ 继承:需要为每种组合创建子类(类爆炸问题)
  • ❌ 修改原类:违反开闭原则,影响现有功能
  • ✅ 装饰器模式:像穿衣服一样层层包装,灵活组合
// 核心接口
interface TextProcessor {
    String process(String text);
}

// 基础实现
class PlainText implements TextProcessor {
    public String process(String text) {
        return text;
    }
}

// 装饰器基类
abstract class TextDecorator implements TextProcessor {
    protected TextProcessor processor;
    
    public TextDecorator(TextProcessor processor) {
        this.processor = processor;
    }
}

// 具体装饰器
class EncryptDecorator extends TextDecorator {
    public EncryptDecorator(TextProcessor processor) {
        super(processor);
    }
    
    public String process(String text) {
        String result = processor.process(text);
        return "[加密]" + new StringBuilder(result).reverse();
    }
}

class CompressDecorator extends TextDecorator {
    public CompressDecorator(TextProcessor processor) {
        super(processor);
    }
    
    public String process(String text) {
        String result = processor.process(text);
        return "[压缩]" + result.replaceAll("\\s", "");
    }
}

// 使用 - 灵活组合功能
TextProcessor processor = new PlainText();
System.out.println(processor.process("Hello World"));

// 加密装饰
processor = new EncryptDecorator(processor);
System.out.println(processor.process("Hello World"));

// 再加压缩装饰
processor = new CompressDecorator(processor);
System.out.println(processor.process("Hello World"));

核心价值:动态组合对象功能,避免类爆炸


8. 代理模式 (Proxy)

需求场景:开发一个图片查看器,需要显示大量高分辨率图片,要求:

  • 提高加载性能,避免一次性加载所有图片
  • 支持权限控制,只有VIP用户能查看高清图
  • 添加缓存机制,避免重复加载

为什么用代理而不是直接调用?

  • ❌ 直接调用:无法控制访问,无法添加额外功能
  • ❌ 修改原类:违反开闭原则,增加类的复杂度
  • ✅ 代理模式:在不修改原对象的情况下控制访问
interface ImageViewer {
    void displayImage(String filename);
}

// 真实对象 - 实际的图片加载
class HighResolutionImage implements ImageViewer {
    private String filename;
    
    public HighResolutionImage(String filename) {
        this.filename = filename;
        loadImage(); // 耗时操作
    }
    
    private void loadImage() {
        System.out.println("从磁盘加载高分辨率图片: " + filename);
    }
    
    public void displayImage(String filename) {
        System.out.println("显示高分辨率图片: " + filename);
    }
}

// 代理对象 - 控制访问
class ImageProxy implements ImageViewer {
    private HighResolutionImage realImage;
    private String filename;
    private String userType;
    private static Set<String> cache = new HashSet<>();
    
    public ImageProxy(String filename, String userType) {
        this.filename = filename;
        this.userType = userType;
    }
    
    public void displayImage(String filename) {
        // 权限检查
        if (!"VIP".equals(userType)) {
            System.out.println("显示低分辨率预览图: " + filename);
            return;
        }
        
        // 缓存检查
        if (cache.contains(filename)) {
            System.out.println("从缓存显示: " + filename);
            return;
        }
        
        // 延迟加载
        if (realImage == null) {
            realImage = new HighResolutionImage(filename);
        }
        
        realImage.displayImage(filename);
        cache.add(filename);
    }
}

// 使用 - 通过代理控制访问
ImageViewer vipImage = new ImageProxy("photo.jpg", "VIP");
ImageViewer normalImage = new ImageProxy("photo.jpg", "NORMAL");

vipImage.displayImage("photo.jpg");    // 加载高清图
normalImage.displayImage("photo.jpg"); // 显示预览图
vipImage.displayImage("photo.jpg");    // 从缓存显示

核心价值:在不修改对象的前提下控制访问


9. 外观模式 (Facade)

需求场景:开发一个智能家居控制系统,用户希望通过简单的语音命令控制多个设备,要求:

  • 一个命令能控制多个相关设备
  • 隐藏复杂的设备控制细节
  • 提供简单易用的高层接口

为什么用外观而不是让客户端直接调用?

  • ❌ 客户端直接调用:需要了解所有子系统的接口,代码复杂
  • ❌ 把逻辑放在客户端:重复代码,难以维护
  • ✅ 外观模式:提供统一的简化接口,隐藏复杂性
// 复杂的子系统
class Light {
    public void turnOn() { System.out.println("灯光打开"); }
    public void turnOff() { System.out.println("灯光关闭"); }
    public void dim(int level) { System.out.println("灯光调至" + level + "%"); }
}

class AirConditioner {
    public void turnOn() { System.out.println("空调启动"); }
    public void setTemperature(int temp) { System.out.println("温度设为" + temp + "°C"); }
}

class Television {
    public void turnOn() { System.out.println("电视打开"); }
    public void setChannel(int channel) { System.out.println("切换到" + channel + "频道"); }
}

class SoundSystem {
    public void turnOn() { System.out.println("音响开启"); }
    public void setVolume(int volume) { System.out.println("音量设为" + volume); }
}

// 外观类 - 简化复杂操作
class SmartHomeFacade {
    private Light light;
    private AirConditioner ac;
    private Television tv;
    private SoundSystem sound;
    
    public SmartHomeFacade() {
        light = new Light();
        ac = new AirConditioner();
        tv = new Television();
        sound = new SoundSystem();
    }
    
    // 一键回家模式
    public void arriveHome() {
        System.out.println("=== 回家模式启动 ===");
        light.turnOn();
        light.dim(70);
        ac.turnOn();
        ac.setTemperature(26);
        System.out.println("=== 欢迎回家 ===");
    }
    
    // 一键观影模式
    public void watchMovie() {
        System.out.println("=== 观影模式启动 ===");
        light.dim(20);
        tv.turnOn();
        tv.setChannel(5);
        sound.turnOn();
        sound.setVolume(30);
        System.out.println("=== 享受观影时光 ===");
    }
    
    // 一键睡眠模式
    public void sleep() {
        System.out.println("=== 睡眠模式启动 ===");
        light.turnOff();
        tv.turnOff();
        sound.turnOff();
        ac.setTemperature(24);
        System.out.println("=== 晚安 ===");
    }
}

// 使用 - 简单的高层接口
SmartHomeFacade home = new SmartHomeFacade();
home.arriveHome();  // 一键操作多个设备
home.watchMovie();  // 一键切换场景
home.sleep();       // 一键关闭

核心价值:为复杂子系统提供简单统一的接口


10. 组合模式 (Composite)

需求场景:开发一个文件管理器,需要统一处理文件和文件夹的操作,要求:

  • 文件夹可以包含文件和子文件夹
  • 对文件夹的操作要递归应用到所有子项
  • 客户端不需要区分处理文件还是文件夹

为什么用组合而不是分别处理?

  • ❌ 分别处理:客户端需要判断类型,代码复杂
  • ❌ 用继承:无法表达文件夹包含关系
  • ✅ 组合模式:统一接口处理,支持递归结构
// 统一接口
abstract class FileSystemItem {
    protected String name;
    
    public FileSystemItem(String name) {
        this.name = name;
    }
    
    public abstract void display(String indent);
    public abstract long getSize();
}

// 叶子节点 - 文件
class File extends FileSystemItem {
    private long size;
    
    public File(String name, long size) {
        super(name);
        this.size = size;
    }
    
    public void display(String indent) {
        System.out.println(indent + "📄 " + name + " (" + size + " bytes)");
    }
    
    public long getSize() {
        return size;
    }
}

// 组合节点 - 文件夹
class Folder extends FileSystemItem {
    private List<FileSystemItem> children = new ArrayList<>();
    
    public Folder(String name) {
        super(name);
    }
    
    public void add(FileSystemItem item) {
        children.add(item);
    }
    
    public void display(String indent) {
        System.out.println(indent + "📁 " + name + "/");
        for (FileSystemItem child : children) {
            child.display(indent + "  ");
        }
    }
    
    public long getSize() {
        long totalSize = 0;
        for (FileSystemItem child : children) {
            totalSize += child.getSize();
        }
        return totalSize;
    }
}

// 使用 - 统一处理文件和文件夹
Folder root = new Folder("项目根目录");
root.add(new File("README.md", 1024));
root.add(new File("pom.xml", 2048));

Folder src = new Folder("src");
src.add(new File("Main.java", 5120));
src.add(new File("Utils.java", 3072));

root.add(src);

// 统一操作 - 不区分文件还是文件夹
root.display("");
System.out.println("总大小: " + root.getSize() + " bytes");

核心价值:统一处理单个对象和组合对象


11. 桥接模式 (Bridge)

需求场景:开发一个图形绘制程序,需要支持多种形状在不同平台上绘制,要求:

  • 形状类型可以独立扩展(圆形、矩形、三角形等)
  • 绘制平台可以独立扩展(Windows、Linux、Web等)
  • 避免形状数量×平台数量的类爆炸问题

为什么用桥接而不是继承?

  • ❌ 继承:N种形状×M个平台=N×M个类
  • ❌ 接口继承:仍然存在组合爆炸
  • ✅ 桥接模式:抽象和实现分离,各自独立扩展
// 实现接口 - 绘制平台
interface DrawingAPI {
    void drawLine(int x1, int y1, int x2, int y2);
    void drawCircle(int x, int y, int radius);
}

// 具体实现 - Windows平台
class WindowsDrawingAPI implements DrawingAPI {
    public void drawLine(int x1, int y1, int x2, int y2) {
        System.out.println("Windows绘制直线: (" + x1 + "," + y1 + ") to (" + x2 + "," + y2 + ")");
    }
    
    public void drawCircle(int x, int y, int radius) {
        System.out.println("Windows绘制圆形: 中心(" + x + "," + y + ") 半径:" + radius);
    }
}

// 具体实现 - Linux平台  
class LinuxDrawingAPI implements DrawingAPI {
    public void drawLine(int x1, int y1, int x2, int y2) {
        System.out.println("Linux绘制直线: [" + x1 + "," + y1 + "]->[" + x2 + "," + y2 + "]");
    }
    
    public void drawCircle(int x, int y, int radius) {
        System.out.println("Linux绘制圆形: center(" + x + "," + y + ") r=" + radius);
    }
}

// 抽象 - 形状
abstract class Shape {
    protected DrawingAPI drawingAPI;
    
    protected Shape(DrawingAPI drawingAPI) {
        this.drawingAPI = drawingAPI;
    }
    
    public abstract void draw();
}

// 扩展抽象 - 具体形状
class Circle extends Shape {
    private int x, y, radius;
    
    public Circle(int x, int y, int radius, DrawingAPI drawingAPI) {
        super(drawingAPI);
        this.x = x;
        this.y = y;
        this.radius = radius;
    }
    
    public void draw() {
        drawingAPI.drawCircle(x, y, radius);
    }
}

class Rectangle extends Shape {
    private int x, y, width, height;
    
    public Rectangle(int x, int y, int width, int height, DrawingAPI drawingAPI) {
        super(drawingAPI);
        this.x = x; this.y = y; this.width = width; this.height = height;
    }
    
    public void draw() {
        drawingAPI.drawLine(x, y, x + width, y);
        drawingAPI.drawLine(x + width, y, x + width, y + height);
        drawingAPI.drawLine(x + width, y + height, x, y + height);
        drawingAPI.drawLine(x, y + height, x, y);
    }
}

// 使用 - 抽象和实现独立变化
Shape[] shapes = {
    new Circle(10, 10, 5, new WindowsDrawingAPI()),
    new Circle(20, 20, 8, new LinuxDrawingAPI()),
    new Rectangle(0, 0, 10, 5, new WindowsDrawingAPI())
};

for (Shape shape : shapes) {
    shape.draw();
}

核心价值:抽象与实现分离,避免类爆炸


12. 享元模式 (Flyweight)

需求场景:开发一个在线文档编辑器,需要处理大量文字字符,每个字符都有字体、大小、颜色等属性,要求:

  • 支持百万级字符的文档
  • 节约内存占用
  • 保持良好的性能

为什么用享元而不是每个字符一个对象?

  • ❌ 每字符一对象:百万字符=百万对象,内存爆炸
  • ❌ 静态缓存:无法处理不同字体样式
  • ✅ 享元模式:共享相同字符,只存储位置等外在状态
// 享元接口
interface CharacterFlyweight {
    void display(int row, int col, String color);
}

// 具体享元 - 只存储内在状态(字符和字体)
class ConcreteCharacter implements CharacterFlyweight {
    private final char character; // 内在状态,不变的
    private final String font;    // 内在状态,不变的
    
    public ConcreteCharacter(char character, String font) {
        this.character = character;
        this.font = font;
    }
    
    // 外在状态(位置、颜色)作为参数传入
    public void display(int row, int col, String color) {
        System.out.println("显示字符 '" + character + "' " + font + 
                          " 位置:(" + row + "," + col + ") 颜色:" + color);
    }
}

// 享元工厂 - 管理和复用享元对象
class CharacterFactory {
    private static Map<String, CharacterFlyweight> flyweights = new HashMap<>();
    
    public static CharacterFlyweight getCharacter(char c, String font) {
        String key = c + ":" + font;
        CharacterFlyweight flyweight = flyweights.get(key);
        
        if (flyweight == null) {
            flyweight = new ConcreteCharacter(c, font);
            flyweights.put(key, flyweight);
            System.out.println("创建享元: " + key);
        }
        
        return flyweight;
    }
    
    public static int getCreatedCount() {
        return flyweights.size();
    }
}

// 文档类 - 存储外在状态
class Document {
    private List<CharacterContext> characters = new ArrayList<>();
    
    private static class CharacterContext {
        CharacterFlyweight flyweight;
        int row, col;
        String color;
        
        CharacterContext(CharacterFlyweight flyweight, int row, int col, String color) {
            this.flyweight = flyweight;
            this.row = row;
            this.col = col;
            this.color = color;
        }
    }
    
    public void addCharacter(char c, String font, int row, int col, String color) {
        CharacterFlyweight flyweight = CharacterFactory.getCharacter(c, font);
        characters.add(new CharacterContext(flyweight, row, col, color));
    }
    
    public void display() {
        for (CharacterContext context : characters) {
            context.flyweight.display(context.row, context.col, context.color);
        }
    }
}

// 使用 - 大量字符共享少量对象
Document doc = new Document();

// 添加文本 "Hello World",相同字符共享享元
doc.addCharacter('H', "Arial", 0, 0, "black");
doc.addCharacter('e', "Arial", 0, 1, "black");
doc.addCharacter('l', "Arial", 0, 2, "black");
doc.addCharacter('l', "Arial", 0, 3, "black"); // 复用'l'
doc.addCharacter('o', "Arial", 0, 4, "black");
doc.addCharacter(' ', "Arial", 0, 5, "black");
doc.addCharacter('W', "Arial", 0, 6, "red");
doc.addCharacter('o', "Arial", 0, 7, "red");   // 复用'o'但颜色不同
doc.addCharacter('r', "Arial", 0, 8, "red");
doc.addCharacter('l', "Arial", 0, 9, "red");   // 复用'l'
doc.addCharacter('d', "Arial", 0, 10, "red");

doc.display();
System.out.println("创建的享元对象数量: " + CharacterFactory.getCreatedCount());

核心价值:通过共享减少大量相似对象的内存占用


三、行为型模式(11种)

13. 观察者模式 (Observer)

需求场景:开发一个股票监控系统,多个投资者需要实时关注股票价格变动,要求:

  • 股票价格变动时自动通知所有关注者
  • 投资者可以随时取消关注
  • 新增投资者不影响现有系统

为什么用观察者而不是定时查询?

  • ❌ 定时查询:延迟高,资源浪费
  • ❌ 硬编码通知:添加新观察者需要修改代码
  • ✅ 观察者模式:价格变动时主动通知,松耦合
// 观察者接口
interface StockObserver {
    void update(String stockName, double price);
}

// 具体观察者 - 投资者
class Investor implements StockObserver {
    private String name;
    
    public Investor(String name) {
        this.name = name;
    }
    
    public void update(String stockName, double price) {
        System.out.println(name + " 收到通知: " + stockName + " 价格 ¥" + price);
        
        // 根据价格做出投资决策
        if (price > 100) {
            System.out.println(name + " -> 价格过高,考虑卖出");
        } else if (price < 80) {
            System.out.println(name + " -> 价格较低,考虑买入");
        }
    }
}

// 被观察者 - 股票
class Stock {
    private List<StockObserver> observers = new ArrayList<>();
    private String name;
    private double price;
    
    public Stock(String name) {
        this.name = name;
    }
    
    public void addObserver(StockObserver observer) {
        observers.add(observer);
    }
    
    public void removeObserver(StockObserver observer) {
        observers.remove(observer);
    }
    
    public void setPrice(double price) {
        System.out.println("\n=== " + name + " 价格变动: ¥" + price + " ===");
        this.price = price;
        notifyObservers();
    }
    
    private void notifyObservers() {
        for (StockObserver observer : observers) {
            observer.update(name, price);
        }
    }
}

// 使用 - 一对多依赖关系
Stock appleStock = new Stock("苹果股票");

Investor zhang = new Investor("张三");
Investor li = new Investor("李四");

appleStock.addObserver(zhang);
appleStock.addObserver(li);

appleStock.setPrice(120.0); // 自动通知所有投资者
appleStock.setPrice(75.0);  // 再次通知

核心价值:一对多依赖,状态变化时自动通知


14. 策略模式 (Strategy)

需求场景:电商系统需要支持多种促销策略(满减、打折、积分抵扣),要求:

  • 根据用户类型和活动规则选择不同策略
  • 新增促销策略不修改现有代码
  • 策略可以在运行时动态切换

为什么用策略而不是if-else?

  • ❌ if-else:新增策略需要修改现有代码,违反开闭原则
  • ❌ 继承:客户端需要知道具体子类
  • ✅ 策略模式:算法封装成独立类,可以互换
// 策略接口
interface PricingStrategy {
    double calculatePrice(double originalPrice);
    String getDescription();
}

// 具体策略
class RegularPricing implements PricingStrategy {
    public double calculatePrice(double originalPrice) {
        return originalPrice;
    }
    
    public String getDescription() {
        return "正常价格";
    }
}

class DiscountPricing implements PricingStrategy {
    private double discount;
    
    public DiscountPricing(double discount) {
        this.discount = discount;
    }
    
    public double calculatePrice(double originalPrice) {
        return originalPrice * (1 - discount);
    }
    
    public String getDescription() {
        return (int)(discount * 100) + "% 折扣";
    }
}

class VipPricing implements PricingStrategy {
    public double calculatePrice(double originalPrice) {
        // VIP享受8.5折,满100减10
        double vipPrice = originalPrice * 0.85;
        if (vipPrice >= 100) {
            vipPrice -= 10;
        }
        return vipPrice;
    }
    
    public String getDescription() {
        return "VIP价格(8.5折+满100减10)";
    }
}

// 环境类 - 购物车
class ShoppingCart {
    private PricingStrategy strategy;
    private double originalPrice;
    
    public ShoppingCart(double originalPrice) {
        this.originalPrice = originalPrice;
        this.strategy = new RegularPricing(); // 默认策略
    }
    
    public void setPricingStrategy(PricingStrategy strategy) {
        this.strategy = strategy;
    }
    
    public void checkout() {
        double finalPrice = strategy.calculatePrice(originalPrice);
        System.out.println("原价: ¥" + originalPrice);
        System.out.println("策略: " + strategy.getDescription());
        System.out.println("实付: ¥" + finalPrice);
        System.out.println("节省: ¥" + (originalPrice - finalPrice));
        System.out.println("---");
    }
}

// 使用 - 根据用户类型选择策略
ShoppingCart cart = new ShoppingCart(200.0);

// 普通用户
cart.checkout();

// 活动期间 - 7折优惠
cart.setPricingStrategy(new DiscountPricing(0.3));
cart.checkout();

// VIP用户
cart.setPricingStrategy(new VipPricing());
cart.checkout();

核心价值:将算法封装成可互换的策略类


15. 命令模式 (Command)

需求场景:开发一个智能家居遥控器,需要控制多种设备,要求:

  • 支持撤销操作(如误按了关灯)
  • 支持宏命令(一键执行多个操作)
  • 可以记录操作历史

为什么用命令而不是直接调用?

  • ❌ 直接调用:无法撤销,无法记录历史
  • ❌ 保存参数:撤销逻辑复杂,难以管理
  • ✅ 命令模式:将请求封装成对象,支持撤销和队列
// 命令接口
interface Command {
    void execute();
    void undo();
}

// 接收者 - 灯泡
class Light {
    private boolean isOn = false;
    
    public void turnOn() {
        isOn = true;
        System.out.println("灯已打开");
    }
    
    public void turnOff() {
        isOn = false;
        System.out.println("灯已关闭");
    }
    
    public boolean isOn() {
        return isOn;
    }
}

// 具体命令
class LightOnCommand implements Command {
    private Light light;
    
    public LightOnCommand(Light light) {
        this.light = light;
    }
    
    public void execute() {
        light.turnOn();
    }
    
    public void undo() {
        light.turnOff();
    }
}

class LightOffCommand implements Command {
    private Light light;
    
    public LightOffCommand(Light light) {
        this.light = light;
    }
    
    public void execute() {
        light.turnOff();
    }
    
    public void undo() {
        light.turnOn();
    }
}

// 宏命令 - 组合多个命令
class MacroCommand implements Command {
    private Command[] commands;
    
    public MacroCommand(Command[] commands) {
        this.commands = commands;
    }
    
    public void execute() {
        System.out.println("执行宏命令:");
        for (Command command : commands) {
            command.execute();
        }
    }
    
    public void undo() {
        System.out.println("撤销宏命令:");
        // 逆序撤销
        for (int i = commands.length - 1; i >= 0; i--) {
            commands[i].undo();
        }
    }
}

// 调用者 - 遥控器
class RemoteControl {
    private Command lastCommand;
    
    public void setCommand(Command command) {
        command.execute();
        lastCommand = command;
    }
    
    public void pressUndo() {
        if (lastCommand != null) {
            System.out.println("撤销上一个操作:");
            lastCommand.undo();
        }
    }
}

// 使用 - 支持撤销的遥控器
Light livingRoomLight = new Light();
Light bedRoomLight = new Light();

RemoteControl remote = new RemoteControl();

// 单个命令
remote.setCommand(new LightOnCommand(livingRoomLight));
remote.pressUndo(); // 撤销

// 宏命令 - 一键关闭所有灯
Command[] allOffCommands = {
    new LightOffCommand(livingRoomLight),
    new LightOffCommand(bedRoomLight)
};
MacroCommand allOff = new MacroCommand(allOffCommands);

remote.setCommand(allOff);
remote.pressUndo(); // 撤销宏命令

核心价值:将请求封装成对象,支持撤销和宏操作


16. 状态模式 (State)

需求场景:开发一个电梯控制系统,电梯有多种状态(停止、上行、下行、维护),不同状态下的操作行为不同,要求:

  • 状态改变时行为自动改变
  • 新增状态不影响现有代码
  • 避免复杂的状态判断逻辑

为什么用状态而不是if-else?

  • ❌ if-else:状态多了代码复杂,难以维护
  • ❌ 枚举+switch:仍然是集中式判断,违反开闭原则
  • ✅ 状态模式:每种状态独立处理,易于扩展
// 状态接口
interface ElevatorState {
    void openDoor(ElevatorContext context);
    void closeDoor(ElevatorContext context);
    void moveUp(ElevatorContext context);
    void moveDown(ElevatorContext context);
}

// 环境类 - 电梯
class ElevatorContext {
    private ElevatorState state;
    private int currentFloor = 1;
    
    public ElevatorContext() {
        state = new StoppedState(); // 初始状态
    }
    
    public void setState(ElevatorState state) {
        System.out.println("电梯状态切换为: " + state.getClass().getSimpleName());
        this.state = state;
    }
    
    public void openDoor() { state.openDoor(this); }
    public void closeDoor() { state.closeDoor(this); }
    public void moveUp() { state.moveUp(this); }
    public void moveDown() { state.moveDown(this); }
    
    public int getCurrentFloor() { return currentFloor; }
    public void setCurrentFloor(int floor) { this.currentFloor = floor; }
}

// 具体状态 - 停止状态
class StoppedState implements ElevatorState {
    public void openDoor(ElevatorContext context) {
        System.out.println("电梯门打开");
    }
    
    public void closeDoor(ElevatorContext context) {
        System.out.println("电梯门关闭");
    }
    
    public void moveUp(ElevatorContext context) {
        System.out.println("电梯开始上行");
        context.setState(new MovingUpState());
    }
    
    public void moveDown(ElevatorContext context) {
        System.out.println("电梯开始下行");  
        context.setState(new MovingDownState());
    }
}

// 具体状态 - 上行状态
class MovingUpState implements ElevatorState {
    public void openDoor(ElevatorContext context) {
        System.out.println("电梯运行中,无法开门");
    }
    
    public void closeDoor(ElevatorContext context) {
        System.out.println("电梯门已关闭,正在上行");
    }
    
    public void moveUp(ElevatorContext context) {
        System.out.println("电梯继续上行到" + (context.getCurrentFloor() + 1) + "楼");
        context.setCurrentFloor(context.getCurrentFloor() + 1);
        
        // 到达目标楼层,停止
        System.out.println("到达目标楼层,电梯停止");
        context.setState(new StoppedState());
    }
    
    public void moveDown(ElevatorContext context) {
        System.out.println("电梯正在上行,先停止再下行");
        context.setState(new StoppedState());
    }
}

class MovingDownState implements ElevatorState {
    public void openDoor(ElevatorContext context) {
        System.out.println("电梯运行中,无法开门");
    }
    
    public void closeDoor(ElevatorContext context) {
        System.out.println("电梯门已关闭,正在下行");
    }
    
    public void moveUp(ElevatorContext context) {
        System.out.println("电梯正在下行,先停止再上行");
        context.setState(new StoppedState());
    }
    
    public void moveDown(ElevatorContext context) {
        System.out.println("电梯继续下行到" + (context.getCurrentFloor() - 1) + "楼");
        context.setCurrentFloor(context.getCurrentFloor() - 1);
        
        System.out.println("到达目标楼层,电梯停止");
        context.setState(new StoppedState());
    }
}

// 使用 - 状态决定行为
ElevatorContext elevator = new ElevatorContext();

elevator.openDoor();  // 停止状态可以开门
elevator.moveUp();    // 切换到上行状态
elevator.openDoor();  // 上行状态无法开门
elevator.moveUp();    // 继续上行并到达
elevator.openDoor();  // 停止状态又可以开门了

核心价值:状态改变时行为自动改变,避免复杂判断


17. 责任链模式 (Chain of Responsibility)

需求场景:开发一个客服系统,不同类型的问题需要不同级别的客服处理,要求:

  • 按级别逐层处理问题
  • 可以灵活调整处理链结构
  • 发送者不需要知道具体哪个处理者会处理

为什么用责任链而不是直接分派?

  • ❌ 直接分派:客户端需要知道所有处理者,耦合度高
  • ❌ 集中判断:处理逻辑集中,难以扩展
  • ✅ 责任链模式:松散耦合,处理者可以动态组合
// 问题类型
enum IssueType {
    BASIC, TECHNICAL, BILLING, VIP
}

// 问题实体
class CustomerIssue {
    private IssueType type;
    private String description;
    private String customerLevel;
    
    public CustomerIssue(IssueType type, String description, String customerLevel) {
        this.type = type;
        this.description = description;
        this.customerLevel = customerLevel;
    }
    
    public IssueType getType() { return type; }
    public String getDescription() { return description; }
    public String getCustomerLevel() { return customerLevel; }
}

// 抽象处理者
abstract class SupportHandler {
    protected SupportHandler nextHandler;
    protected String handlerName;
    
    public SupportHandler(String name) {
        this.handlerName = name;
    }
    
    public void setNext(SupportHandler handler) {
        this.nextHandler = handler;
    }
    
    public void handleIssue(CustomerIssue issue) {
        if (canHandle(issue)) {
            System.out.println(handlerName + " 处理问题: " + issue.getDescription());
            solve(issue);
        } else if (nextHandler != null) {
            System.out.println(handlerName + " 无法处理,转交给上级");
            nextHandler.handleIssue(issue);
        } else {
            System.out.println("所有客服都无法处理此问题");
        }
    }
    
    protected abstract boolean canHandle(CustomerIssue issue);
    protected abstract void solve(CustomerIssue issue);
}

// 具体处理者
class BasicSupportHandler extends SupportHandler {
    public BasicSupportHandler() {
        super("初级客服");
    }
    
    protected boolean canHandle(CustomerIssue issue) {
        return issue.getType() == IssueType.BASIC;
    }
    
    protected void solve(CustomerIssue issue) {
        System.out.println("提供基础帮助和FAQ解答");
    }
}

class TechnicalSupportHandler extends SupportHandler {
    public TechnicalSupportHandler() {
        super("技术客服");
    }
    
    protected boolean canHandle(CustomerIssue issue) {
        return issue.getType() == IssueType.TECHNICAL;
    }
    
    protected void solve(CustomerIssue issue) {
        System.out.println("提供技术支持和解决方案");
    }
}

class ManagerHandler extends SupportHandler {
    public ManagerHandler() {
        super("客服经理");
    }
    
    protected boolean canHandle(CustomerIssue issue) {
        return issue.getType() == IssueType.BILLING || 
               issue.getType() == IssueType.VIP ||
               "VIP".equals(issue.getCustomerLevel());
    }
    
    protected void solve(CustomerIssue issue) {
        System.out.println("经理亲自处理重要问题");
    }
}

// 使用 - 构建处理链
SupportHandler basicSupport = new BasicSupportHandler();
SupportHandler techSupport = new TechnicalSupportHandler();
SupportHandler manager = new ManagerHandler();

// 构建责任链
basicSupport.setNext(techSupport);
techSupport.setNext(manager);

// 处理不同问题
CustomerIssue[] issues = {
    new CustomerIssue(IssueType.BASIC, "如何修改密码", "普通"),
    new CustomerIssue(IssueType.TECHNICAL, "软件崩溃", "普通"), 
    new CustomerIssue(IssueType.BILLING, "账单错误", "普通"),
    new CustomerIssue(IssueType.BASIC, "账户咨询", "VIP")
};

for (CustomerIssue issue : issues) {
    System.out.println("\n=== 处理问题: " + issue.getDescription() + " ===");
    basicSupport.handleIssue(issue);
}

核心价值:沿链传递请求,直到有对象处理它


18. 模板方法模式 (Template Method)

需求场景:开发一个数据处理框架,需要处理不同格式的数据文件(CSV、XML、JSON),处理流程固定但具体步骤不同,要求:

  • 统一处理流程,确保步骤不会遗漏
  • 各种格式的具体处理逻辑可以定制
  • 公共逻辑可以复用

为什么用模板方法而不是让子类完全实现?

  • ❌ 完全实现:容易遗漏步骤,公共逻辑重复
  • ❌ 接口定义:无法强制执行处理流程
  • ✅ 模板方法:在父类定义骨架,子类实现具体步骤
// 抽象模板类
abstract class DataProcessor {
    // 模板方法 - 定义处理流程
    public final void processData(String filename) {
        System.out.println("=== 开始处理数据文件: " + filename + " ===");
        
        validateFile(filename);
        String data = readData(filename);
        String processedData = parseData(data);
        String result = transformData(processedData);
        saveResult(result);
        
        if (needsCleanup()) { // 钩子方法
            cleanup();
        }
        
        System.out.println("=== 数据处理完成 ===\n");
    }
    
    // 具体方法 - 公共逻辑
    private void validateFile(String filename) {
        System.out.println("验证文件存在性和权限");
    }
    
    private void saveResult(String result) {
        System.out.println("保存处理结果到数据库");
    }
    
    // 抽象方法 - 子类必须实现
    protected abstract String readData(String filename);
    protected abstract String parseData(String data);
    protected abstract String transformData(String data);
    
    // 钩子方法 - 子类可以选择重写
    protected boolean needsCleanup() {
        return true;
    }
    
    protected void cleanup() {
        System.out.println("清理临时文件");
    }
}

// 具体类 - CSV处理器
class CsvDataProcessor extends DataProcessor {
    protected String readData(String filename) {
        System.out.println("读取CSV文件内容");
        return "csv_raw_data";
    }
    
    protected String parseData(String data) {
        System.out.println("解析CSV格式,按逗号分割");
        return "csv_parsed_data";
    }
    
    protected String transformData(String data) {
        System.out.println("转换CSV数据为标准格式");
        return "csv_transformed_data";
    }
}

// 具体类 - JSON处理器
class JsonDataProcessor extends DataProcessor {
    protected String readData(String filename) {
        System.out.println("读取JSON文件内容");
        return "json_raw_data";
    }
    
    protected String parseData(String data) {
        System.out.println("解析JSON格式");
        return "json_parsed_data";
    }
    
    protected String transformData(String data) {
        System.out.println("转换JSON数据为标准格式");
        return "json_transformed_data";
    }
    
    // 重写钩子方法
    protected boolean needsCleanup() {
        return false; // JSON处理不需要清理
    }
}

// 使用 - 相同流程,不同实现
DataProcessor csvProcessor = new CsvDataProcessor();
DataProcessor jsonProcessor = new JsonDataProcessor();

csvProcessor.processData("data.csv");
jsonProcessor.processData("data.json");

核心价值:在父类定义算法骨架,子类实现具体步骤

19. 迭代器模式 (Iterator)

需求场景:开发一个播放列表系统,需要支持多种遍历方式(顺序播放、随机播放、循环播放),要求:

  • 提供统一的遍历接口,不暴露内部存储结构
  • 支持多种遍历算法
  • 可以同时进行多个遍历操作

为什么用迭代器而不是直接访问?

  • ❌ 直接访问:暴露内部结构,客户端依赖具体实现
  • ❌ 返回数组/列表:无法支持不同遍历方式
  • ✅ 迭代器模式:统一接口,支持多种遍历算法
// 迭代器接口
interface Iterator<T> {
    boolean hasNext();
    T next();
}

// 容器接口
interface PlayList<T> {
    Iterator<T> getIterator();
    Iterator<T> getRandomIterator();
    Iterator<T> getLoopIterator();
}

// 具体容器 - 音乐播放列表
class MusicPlayList implements PlayList<String> {
    private String[] songs;
    private int count = 0;
    
    public MusicPlayList(int capacity) {
        songs = new String[capacity];
    }
    
    public void addSong(String song) {
        if (count < songs.length) {
            songs[count++] = song;
        }
    }
    
    // 顺序迭代器
    public Iterator<String> getIterator() {
        return new SequentialIterator();
    }
    
    // 随机迭代器
    public Iterator<String> getRandomIterator() {
        return new RandomIterator();
    }
    
    // 循环迭代器
    public Iterator<String> getLoopIterator() {
        return new LoopIterator();
    }
    
    // 顺序迭代器实现
    private class SequentialIterator implements Iterator<String> {
        private int position = 0;
        
        public boolean hasNext() {
            return position < count;
        }
        
        public String next() {
            return hasNext() ? songs[position++] : null;
        }
    }
    
    // 随机迭代器实现
    private class RandomIterator implements Iterator<String> {
        private List<Integer> indices;
        private int position = 0;
        
        public RandomIterator() {
            indices = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                indices.add(i);
            }
            Collections.shuffle(indices);
        }
        
        public boolean hasNext() {
            return position < indices.size();
        }
        
        public String next() {
            return hasNext() ? songs[indices.get(position++)] : null;
        }
    }
    
    // 循环迭代器实现
    private class LoopIterator implements Iterator<String> {
        private int position = 0;
        private int playCount = 0;
        private final int maxLoop = 3; // 最多循环3次
        
        public boolean hasNext() {
            return playCount < maxLoop;
        }
        
        public String next() {
            if (!hasNext()) return null;
            
            String song = songs[position];
            position = (position + 1) % count;
            
            if (position == 0) { // 一轮结束
                playCount++;
                System.out.println("--- 第" + playCount + "轮播放完成 ---");
            }
            
            return song;
        }
    }
}

// 使用 - 统一的遍历接口,不同的遍历行为
MusicPlayList playList = new MusicPlayList(5);
playList.addSong("夜曲");
playList.addSong("青花瓷");
playList.addSong("稻香");

System.out.println("=== 顺序播放 ===");
Iterator<String> sequential = playList.getIterator();
while (sequential.hasNext()) {
    System.out.println("播放: " + sequential.next());
}

System.out.println("\n=== 随机播放 ===");
Iterator<String> random = playList.getRandomIterator();
while (random.hasNext()) {
    System.out.println("播放: " + random.next());
}

System.out.println("\n=== 循环播放 ===");
Iterator<String> loop = playList.getLoopIterator();
int count = 0;
while (loop.hasNext() && count < 8) { // 只播放8首演示
    System.out.println("播放: " + loop.next());
    count++;
}

核心价值:统一的遍历接口,支持多种遍历算法


20. 中介者模式 (Mediator)

需求场景:开发一个在线聊天室系统,用户可以加入不同房间聊天,要求:

  • 用户之间不直接通信,通过聊天室转发
  • 支持私聊和群聊
  • 可以方便地添加新功能(如禁言、踢出等)

为什么用中介者而不是用户直接通信?

  • ❌ 直接通信:用户间相互引用,耦合度高,难以管理
  • ❌ 全局管理类:职责不清,违反单一职责原则
  • ✅ 中介者模式:集中管理交互,降低对象间耦合
// 中介者接口
interface ChatMediator {
    void sendMessage(String message, User sender, User receiver);
    void sendGroupMessage(String message, User sender);
    void addUser(User user);
    void removeUser(User user);
}

// 抽象用户
abstract class User {
    protected ChatMediator mediator;
    protected String name;
    protected boolean isMuted = false;
    
    public User(String name, ChatMediator mediator) {
        this.name = name;
        this.mediator = mediator;
    }
    
    public abstract void sendMessage(String message, User receiver);
    public abstract void sendGroupMessage(String message);
    public abstract void receiveMessage(String message, User sender);
    
    public String getName() { return name; }
    public boolean isMuted() { return isMuted; }
    public void setMuted(boolean muted) { this.isMuted = muted; }
}

// 具体中介者 - 聊天室
class ChatRoom implements ChatMediator {
    private List<User> users = new ArrayList<>();
    private String roomName;
    
    public ChatRoom(String roomName) {
        this.roomName = roomName;
    }
    
    public void addUser(User user) {
        users.add(user);
        System.out.println(user.getName() + " 加入聊天室: " + roomName);
        sendSystemMessage(user.getName() + " 进入了聊天室");
    }
    
    public void removeUser(User user) {
        users.remove(user);
        System.out.println(user.getName() + " 离开聊天室: " + roomName);
        sendSystemMessage(user.getName() + " 离开了聊天室");
    }
    
    public void sendMessage(String message, User sender, User receiver) {
        if (sender.isMuted()) {
            System.out.println("[系统] " + sender.getName() + " 被禁言,无法发送消息");
            return;
        }
        
        System.out.println("[私聊] " + sender.getName() + " -> " + receiver.getName());
        receiver.receiveMessage(message, sender);
    }
    
    public void sendGroupMessage(String message, User sender) {
        if (sender.isMuted()) {
            System.out.println("[系统] " + sender.getName() + " 被禁言,无法发送消息");
            return;
        }
        
        System.out.println("[群聊] " + sender.getName() + " 在 " + roomName + " 说:");
        for (User user : users) {
            if (user != sender) { // 不发送给自己
                user.receiveMessage(message, sender);
            }
        }
    }
    
    // 系统消息
    private void sendSystemMessage(String message) {
        System.out.println("[系统消息] " + message);
    }
    
    // 管理员功能 - 禁言用户
    public void muteUser(String userName) {
        for (User user : users) {
            if (user.getName().equals(userName)) {
                user.setMuted(true);
                sendSystemMessage(userName + " 被禁言");
                break;
            }
        }
    }
}

// 具体用户
class ChatUser extends User {
    public ChatUser(String name, ChatMediator mediator) {
        super(name, mediator);
    }
    
    public void sendMessage(String message, User receiver) {
        mediator.sendMessage(message, this, receiver);
    }
    
    public void sendGroupMessage(String message) {
        mediator.sendGroupMessage(message, this);
    }
    
    public void receiveMessage(String message, User sender) {
        System.out.println(name + " 收到消息: " + message);
    }
}

// 使用 - 通过中介者管理复杂交互
ChatRoom gameRoom = new ChatRoom("游戏讨论室");

User alice = new ChatUser("Alice", gameRoom);
User bob = new ChatUser("Bob", gameRoom);
User charlie = new ChatUser("Charlie", gameRoom);

gameRoom.addUser(alice);
gameRoom.addUser(bob);
gameRoom.addUser(charlie);

// 群聊
alice.sendGroupMessage("大家好!有人一起玩游戏吗?");
bob.sendGroupMessage("我可以!");

// 私聊
bob.sendMessage("我们组队吧", alice);

// 管理员功能
gameRoom.muteUser("Charlie");
charlie.sendGroupMessage("我也想参加"); // 被禁言,无法发送

gameRoom.removeUser(bob);

核心价值:集中管理对象间复杂交互,降低耦合度


21. 备忘录模式 (Memento)

需求场景:开发一个绘图软件,需要支持撤销和重做功能,要求:

  • 可以撤销多步操作
  • 支持重做被撤销的操作
  • 不破坏绘图对象的封装性
  • 内存占用要合理

为什么用备忘录而不是直接保存状态?

  • ❌ 公开所有属性:破坏封装性,不安全
  • ❌ 深拷贝整个对象:内存占用大,性能差
  • ✅ 备忘录模式:只保存必要状态,保持封装性
// 备忘录 - 保存绘图状态
class DrawingMemento {
    private final List<String> shapes;
    private final String background;
    private final String timestamp;
    
    public DrawingMemento(List<String> shapes, String background) {
        this.shapes = new ArrayList<>(shapes); // 深拷贝
        this.background = background;
        this.timestamp = new Date().toString();
    }
    
    // 包访问权限,只有DrawingBoard可以访问
    List<String> getShapes() { return new ArrayList<>(shapes); }
    String getBackground() { return background; }
    String getTimestamp() { return timestamp; }
}

// 原发器 - 绘图板
class DrawingBoard {
    private List<String> shapes = new ArrayList<>();
    private String background = "白色";
    
    // 绘制操作
    public void addShape(String shape) {
        shapes.add(shape);
        System.out.println("添加图形: " + shape);
        displayBoard();
    }
    
    public void setBackground(String background) {
        this.background = background;
        System.out.println("设置背景: " + background);
        displayBoard();
    }
    
    public void clear() {
        shapes.clear();
        System.out.println("清空画板");
        displayBoard();
    }
    
    // 创建备忘录
    public DrawingMemento saveState() {
        System.out.println("保存状态: " + shapes.size() + " 个图形");
        return new DrawingMemento(shapes, background);
    }
    
    // 恢复状态
    public void restoreState(DrawingMemento memento) {
        this.shapes = memento.getShapes();
        this.background = memento.getBackground();
        System.out.println("恢复状态到: " + memento.getTimestamp());
        displayBoard();
    }
    
    private void displayBoard() {
        System.out.println("当前画板: 背景=" + background + 
                          ", 图形=" + shapes + "\n");
    }
}

// 管理者 - 历史记录管理器
class HistoryManager {
    private Stack<DrawingMemento> undoStack = new Stack<>();
    private Stack<DrawingMemento> redoStack = new Stack<>();
    private final int maxHistorySize = 10;
    
    // 保存状态
    public void saveState(DrawingBoard board) {
        DrawingMemento memento = board.saveState();
        
        // 保存到撤销栈
        if (undoStack.size() >= maxHistorySize) {
            undoStack.remove(0); // 移除最老的状态
        }
        undoStack.push(memento);
        
        // 清空重做栈(新操作后不能重做之前的撤销)
        redoStack.clear();
    }
    
    // 撤销
    public void undo(DrawingBoard board) {
        if (!undoStack.isEmpty()) {
            // 先保存当前状态到重做栈
            redoStack.push(board.saveState());
            
            // 恢复到上一个状态
            DrawingMemento memento = undoStack.pop();
            board.restoreState(memento);
            
            System.out.println("撤销操作完成");
        } else {
            System.out.println("没有可撤销的操作");
        }
    }
    
    // 重做
    public void redo(DrawingBoard board) {
        if (!redoStack.isEmpty()) {
            // 先保存当前状态到撤销栈
            undoStack.push(board.saveState());
            
            // 恢复到重做状态
            DrawingMemento memento = redoStack.pop();
            board.restoreState(memento);
            
            System.out.println("重做操作完成");
        } else {
            System.out.println("没有可重做的操作");
        }
    }
    
    public void showHistory() {
        System.out.println("撤销栈大小: " + undoStack.size());
        System.out.println("重做栈大小: " + redoStack.size());
    }
}

// 使用 - 支持撤销重做的绘图软件
DrawingBoard board = new DrawingBoard();
HistoryManager history = new HistoryManager();

// 绘制操作序列
board.addShape("圆形");
history.saveState(board);

board.addShape("矩形");
history.saveState(board);

board.setBackground("蓝色");
history.saveState(board);

board.addShape("三角形");
history.saveState(board);

System.out.println("=== 开始撤销操作 ===");
history.undo(board); // 撤销三角形
history.undo(board); // 撤销背景色
history.undo(board); // 撤销矩形

System.out.println("=== 开始重做操作 ===");
history.redo(board); // 重做矩形
history.redo(board); // 重做背景色

history.showHistory();

核心价值:保存对象状态快照,支持撤销重做功能


22. 解释器模式 (Interpreter)

需求场景:开发一个配置文件解析器,需要解析类似"if price > 100 then discount = 0.1"的规则表达式,要求:

  • 支持条件判断、赋值操作
  • 语法可扩展,容易添加新的操作符
  • 表达式可以嵌套组合

为什么用解释器而不是字符串解析?

  • ❌ 硬编码解析:每种语法都要写解析代码,难以扩展
  • ❌ 正则表达式:复杂语法难以处理,不支持嵌套
  • ✅ 解释器模式:每种语法规则独立实现,易于扩展
// 上下文 - 存储变量值
class Context {
    private Map<String, Double> variables = new HashMap<>();
    
    public void setVariable(String name, double value) {
        variables.put(name, value);
        System.out.println("设置变量: " + name + " = " + value);
    }
    
    public double getVariable(String name) {
        return variables.getOrDefault(name, 0.0);
    }
    
    public void showVariables() {
        System.out.println("当前变量: " + variables);
    }
}

// 抽象表达式
interface Expression {
    double interpret(Context context);
}

// 终结符表达式 - 数字
class NumberExpression implements Expression {
    private double number;
    
    public NumberExpression(double number) {
        this.number = number;
    }
    
    public double interpret(Context context) {
        return number;
    }
    
    @Override
    public String toString() {
        return String.valueOf(number);
    }
}

// 终结符表达式 - 变量
class VariableExpression implements Expression {
    private String variableName;
    
    public VariableExpression(String variableName) {
        this.variableName = variableName;
    }
    
    public double interpret(Context context) {
        return context.getVariable(variableName);
    }
    
    @Override
    public String toString() {
        return variableName;
    }
}

// 非终结符表达式 - 加法
class AddExpression implements Expression {
    private Expression left, right;
    
    public AddExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
    
    public double interpret(Context context) {
        return left.interpret(context) + right.interpret(context);
    }
    
    @Override
    public String toString() {
        return "(" + left + " + " + right + ")";
    }
}

// 非终结符表达式 - 比较(大于)
class GreaterThanExpression implements Expression {
    private Expression left, right;
    
    public GreaterThanExpression(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }
    
    public double interpret(Context context) {
        return left.interpret(context) > right.interpret(context) ? 1.0 : 0.0;
    }
    
    @Override
    public String toString() {
        return "(" + left + " > " + right + ")";
    }
}

// 非终结符表达式 - 条件执行
class IfThenExpression implements Expression {
    private Expression condition;
    private Expression thenExpression;
    
    public IfThenExpression(Expression condition, Expression thenExpression) {
        this.condition = condition;
        this.thenExpression = thenExpression;
    }
    
    public double interpret(Context context) {
        if (condition.interpret(context) > 0) { // 大于0表示true
            System.out.println("条件成立,执行then部分");
            return thenExpression.interpret(context);
        } else {
            System.out.println("条件不成立,跳过then部分");
            return 0;
        }
    }
    
    @Override
    public String toString() {
        return "if " + condition + " then " + thenExpression;
    }
}

// 赋值表达式
class AssignExpression implements Expression {
    private String variableName;
    private Expression valueExpression;
    
    public AssignExpression(String variableName, Expression valueExpression) {
        this.variableName = variableName;
        this.valueExpression = valueExpression;
    }
    
    public double interpret(Context context) {
        double value = valueExpression.interpret(context);
        context.setVariable(variableName, value);
        return value;
    }
    
    @Override
    public String toString() {
        return variableName + " = " + valueExpression;
    }
}

// 简单的表达式构建器
class RuleBuilder {
    public static Expression buildDiscountRule() {
        // 构建规则: if price > 100 then discount = 0.1
        Expression price = new VariableExpression("price");
        Expression hundred = new NumberExpression(100);
        Expression condition = new GreaterThanExpression(price, hundred);
        
        Expression discountValue = new NumberExpression(0.1);
        Expression assignment = new AssignExpression("discount", discountValue);
        
        return new IfThenExpression(condition, assignment);
    }
    
    public static Expression buildVipRule() {
        // 构建规则: if (price > 200 and vip = 1) then discount = 0.2
        Expression price = new VariableExpression("price");
        Expression twoHundred = new NumberExpression(200);
        Expression priceCondition = new GreaterThanExpression(price, twoHundred);
        
        Expression vip = new VariableExpression("vip");
        Expression one = new NumberExpression(1);
        Expression vipCondition = new GreaterThanExpression(vip, new NumberExpression(0.5)); // 简化and逻辑
        
        Expression discountValue = new NumberExpression(0.2);
        Expression assignment = new AssignExpression("discount", discountValue);
        
        return new IfThenExpression(priceCondition, assignment);
    }
}

// 使用 - 解释执行规则表达式
Context context = new Context();

// 测试普通用户规则
System.out.println("=== 测试普通用户折扣规则 ===");
context.setVariable("price", 150);
context.setVariable("discount", 0);

Expression discountRule = RuleBuilder.buildDiscountRule();
System.out.println("规则: " + discountRule);
discountRule.interpret(context);
context.showVariables();

// 测试VIP用户规则
System.out.println("\n=== 测试VIP用户折扣规则 ===");
context.setVariable("price", 250);
context.setVariable("vip", 1);
context.setVariable("discount", 0);

Expression vipRule = RuleBuilder.buildVipRule();
System.out.println("规则: " + vipRule);
vipRule.interpret(context);
context.showVariables();

// 测试条件不满足的情况
System.out.println("\n=== 测试条件不满足 ===");
context.setVariable("price", 50);
context.setVariable("discount", 0);

discountRule.interpret(context);
context.showVariables();

核心价值:为特定语言定义语法规则并解释执行


23. 访问者模式 (Visitor)

需求场景:开发一个电商系统的订单分析工具,需要对不同类型的商品进行多种分析(价格统计、重量计算、税费计算),要求:

  • 在不修改商品类的情况下添加新的分析功能
  • 支持对不同类型商品的专门处理
  • 分析逻辑可以独立变化

为什么用访问者而不是在商品类中添加方法?

  • ❌ 在商品类中添加:每次新增分析功能都要修改所有商品类
  • ❌ 用instanceof判断:违反开闭原则,代码丑陋
  • ✅ 访问者模式:分析逻辑与商品分离,易于扩展
// 访问者接口
interface ProductVisitor {
    void visit(Book book);
    void visit(Electronics electronics);
    void visit(Clothing clothing);
}

// 商品接口
interface Product {
    void accept(ProductVisitor visitor);
    String getName();
    double getPrice();
}

// 具体商品 - 书籍
class Book implements Product {
    private String name;
    private double price;
    private int pages;
    private String author;
    
    public Book(String name, double price, int pages, String author) {
        this.name = name;
        this.price = price;
        this.pages = pages;
        this.author = author;
    }
    
    public void accept(ProductVisitor visitor) {
        visitor.visit(this);
    }
    
    public String getName() { return name; }
    public double getPrice() { return price; }
    public int getPages() { return pages; }
    public String getAuthor() { return author; }
}

// 具体商品 - 电子产品
class Electronics implements Product {
    private String name;
    private double price;
    private double weight;
    private int warrantyYears;
    
    public Electronics(String name, double price, double weight, int warrantyYears) {
        this.name = name;
        this.price = price;
        this.weight = weight;
        this.warrantyYears = warrantyYears;
    }
    
    public void accept(ProductVisitor visitor) {
        visitor.visit(this);
    }
    
    public String getName() { return name; }
    public double getPrice() { return price; }
    public double getWeight() { return weight; }
    public int getWarrantyYears() { return warrantyYears; }
}

// 具体商品 - 服装
class Clothing implements Product {
    private String name;
    private double price;
    private String size;
    private String material;
    
    public Clothing(String name, double price, String size, String material) {
        this.name = name;
        this.price = price;
        this.size = size;
        this.material = material;
    }
    
    public void accept(ProductVisitor visitor) {
        visitor.visit(this);
    }
    
    public String getName() { return name; }
    public double getPrice() { return price; }
    public String getSize() { return size; }
    public String getMaterial() { return material; }
}

// 具体访问者 - 价格统计
class PriceCalculatorVisitor implements ProductVisitor {
    private double totalPrice = 0;
    private int itemCount = 0;
    
    public void visit(Book book) {
        double bookPrice = book.getPrice();
        // 书籍超过500页有折扣
        if (book.getPages() > 500) {
            bookPrice *= 0.9;
            System.out.println("厚书折扣: " + book.getName() + " 优惠后价格: ¥" + bookPrice);
        }
        addToTotal(bookPrice);
    }
    
    public void visit(Electronics electronics) {
        double electronicsPrice = electronics.getPrice();
        // 电子产品有3年以上保修的加价
        if (electronics.getWarrantyYears() > 3) {
            electronicsPrice *= 1.1;
            System.out.println("长保修加价: " + electronics.getName() + " 调整后价格: ¥" + electronicsPrice);
        }
        addToTotal(electronicsPrice);
    }
    
    public void visit(Clothing clothing) {
        double clothingPrice = clothing.getPrice();
        // 真丝材料的服装有额外税费
        if ("真丝".equals(clothing.getMaterial())) {
            clothingPrice *= 1.15;
            System.out.println("真丝税费: " + clothing.getName() + " 含税价格: ¥" + clothingPrice);
        }
        addToTotal(clothingPrice);
    }
    
    private void addToTotal(double price) {
        totalPrice += price;
        itemCount++;
    }
    
    public void showResults() {
        System.out.println("价格统计结果:");
        System.out.println("总商品数: " + itemCount);
        System.out.println("总价格: ¥" + String.format("%.2f", totalPrice));
        System.out.println("平均价格: ¥" + String.format("%.2f", totalPrice / itemCount));
        System.out.println();
    }
}

// 具体访问者 - 重量计算
class WeightCalculatorVisitor implements ProductVisitor {
    private double totalWeight = 0;
    
    public void visit(Book book) {
        // 书籍重量按页数估算
        double bookWeight = book.getPages() * 0.005; // 每页5克
        System.out.println("书籍重量: " + book.getName() + " 约" + bookWeight + "kg");
        totalWeight += bookWeight;
    }
    
    public void visit(Electronics electronics) {
        double weight = electronics.getWeight();
        System.out.println("电子产品重量: " + electronics.getName() + " " + weight + "kg");
        totalWeight += weight;
    }
    
    public void visit(Clothing clothing) {
        // 服装重量按尺码估算
        double clothingWeight;
        switch (clothing.getSize().toUpperCase()) {
            case "S": clothingWeight = 0.3; break;
            case "M": clothingWeight = 0.4; break;
            case "L": clothingWeight = 0.5; break;
            case "XL": clothingWeight = 0.6; break;
            default: clothingWeight = 0.4;
        }
        System.out.println("服装重量: " + clothing.getName() + " (" + clothing.getSize() + ") " + clothingWeight + "kg");
        totalWeight += clothingWeight;
    }
    
    public void showResults() {
        System.out.println("重量统计结果:");
        System.out.println("总重量: " + String.format("%.2f", totalWeight) + "kg");
        
        // 根据重量推荐物流方式
        if (totalWeight < 2) {
            System.out.println("推荐: 快递配送");
        } else if (totalWeight < 10) {
            System.out.println("推荐: 物流配送");
        } else {
            System.out.println("推荐: 货运配送");
        }
        System.out.println();
    }
}

// 具体访问者 - 税费计算
class TaxCalculatorVisitor implements ProductVisitor {
    private double totalTax = 0;
    
    public void visit(Book book) {
        // 书籍免税
        System.out.println("书籍免税: " + book.getName() + " 税费: ¥0");
    }
    
    public void visit(Electronics electronics) {
        // 电子产品13%增值税
        double tax = electronics.getPrice() * 0.13;
        System.out.println("电子产品税费: " + electronics.getName() + " 税费: ¥" + String.format("%.2f", tax));
        totalTax += tax;
    }
    
    public void visit(Clothing clothing) {
        // 服装17%增值税
        double tax = clothing.getPrice() * 0.17;
        System.out.println("服装税费: " + clothing.getName() + " 税费: ¥" + String.format("%.2f", tax));
        totalTax += tax;
    }
    
    public void showResults() {
        System.out.println("税费统计结果:");
        System.out.println("总税费: ¥" + String.format("%.2f", totalTax));
        System.out.println();
    }
}

// 购物车 - 对象结构
class ShoppingCart {
    private List<Product> products = new ArrayList<>();
    
    public void addProduct(Product product) {
        products.add(product);
        System.out.println("添加商品: " + product.getName() + " ¥" + product.getPrice());
    }
    
    public void accept(ProductVisitor visitor) {
        System.out.println("=== 开始" + visitor.getClass().getSimpleName() + "分析 ===");
        for (Product product : products) {
            product.accept(visitor);
        }
    }
    
    public void showProducts() {
        System.out.println("\n购物车商品清单:");
        for (Product product : products) {
            System.out.println("- " + product.getName() + " ¥" + product.getPrice());
        }
        System.out.println();
    }
}

// 使用 - 不同分析访问同一批商品
ShoppingCart cart = new ShoppingCart();

// 添加不同类型的商品
cart.addProduct(new Book("设计模式", 89.0, 600, "GoF"));
cart.addProduct(new Book("Java编程", 59.0, 400, "张三"));
cart.addProduct(new Electronics("笔记本电脑", 5999.0, 2.1, 5));
cart.addProduct(new Electronics("手机", 3999.0, 0.2, 2));
cart.addProduct(new Clothing("真丝衬衫", 299.0, "M", "真丝"));
cart.addProduct(new Clothing("牛仔裤", 199.0, "L", "棉"));

cart.showProducts();

// 使用不同访问者进行分析
PriceCalculatorVisitor priceVisitor = new PriceCalculatorVisitor();
cart.accept(priceVisitor);
priceVisitor.showResults();

WeightCalculatorVisitor weightVisitor = new WeightCalculatorVisitor();
cart.accept(weightVisitor);
weightVisitor.showResults();

TaxCalculatorVisitor taxVisitor = new TaxCalculatorVisitor();
cart.accept(taxVisitor);
taxVisitor.showResults();

核心价值:在不修改元素类的前提下,定义作用于元素的新操作


总结:如何选择设计模式

🎯 问题导向的选择方法

当你遇到以下问题时:

  1. 需要全局唯一对象 → 单例模式
  2. 对象创建逻辑复杂 → 工厂/建造者模式
  3. 接口不兼容需要转换 → 适配器模式
  4. 需要动态添加功能 → 装饰器模式
  5. 需要控制对象访问 → 代理模式
  6. 子系统太复杂 → 外观模式
  7. 需要统一处理单个和组合对象 → 组合模式
  8. 一个对象变化需要通知多个对象 → 观察者模式
  9. 需要在运行时切换算法 → 策略模式
  10. 需要撤销操作 → 命令模式
  11. 对象行为随状态变化 → 状态模式
  12. 请求需要链式处理 → 责任链模式
  13. 流程固定但步骤可变 → 模板方法模式
  14. 需要统一遍历方式 → 迭代器模式
  15. 对象间交互复杂 → 中介者模式
  16. 需要保存对象状态 → 备忘录模式
  17. 需要解析特定语言 → 解释器模式
  18. 需要在不修改类的前提下添加操作 → 访问者模式

📊 使用频率与学习优先级

🥇 必须掌握(高频使用)

  • 单例模式:配置管理、缓存、线程池
  • 工厂模式:对象创建、框架设计
  • 观察者模式:事件系统、MVC架构
  • 策略模式:算法切换、业务规则
  • 装饰器模式:功能扩展、中间件

🥈 重点理解(中频使用)

  • 适配器模式:系统集成、遗留代码改造
  • 代理模式:权限控制、缓存、懒加载
  • 外观模式:API设计、子系统封装
  • 命令模式:撤销重做、宏操作
  • 模板方法模式:框架设计、流程控制

🥉 了解概念(低频使用)

  • 抽象工厂、建造者、原型:特定场景使用
  • 桥接、组合、享元:架构设计时考虑
  • 状态、责任链:复杂业务逻辑
  • 迭代器、中介者、备忘录:框架级开发
  • 解释器、访问者:编译器、DSL设计

⚡ 快速决策树

遇到问题时的思考路径:

创建对象有问题?
├── 需要全局唯一 → 单例
├── 创建逻辑复杂 → 工厂方法
├── 需要一套产品 → 抽象工厂
├── 参数很多很复杂 → 建造者
└── 需要快速复制 → 原型

对象结构有问题?
├── 接口不匹配 → 适配器
├── 需要加功能 → 装饰器
├── 需要控制访问 → 代理
├── 系统太复杂 → 外观
├── 树形结构 → 组合
├── 抽象实现分离 → 桥接
└── 对象太多占内存 → 享元

对象交互有问题?
├── 一对多通知 → 观察者
├── 算法需要切换 → 策略
├── 需要撤销 → 命令
├── 状态影响行为 → 状态
├── 链式处理 → 责任链
├── 流程固定步骤可变 → 模板方法
├── 需要统一遍历 → 迭代器
├── 对象间交互复杂 → 中介者
├── 需要保存状态 → 备忘录
├── 需要解析语言 → 解释器
└── 需要新操作不改类 → 访问者

💡 设计模式使用原则

  1. 不要为了模式而模式 - 先有问题,再找模式
  2. 简单问题简单解决 - 复杂度要匹配问题复杂度
  3. 优先组合而不是继承 - 大多数模式都体现这个原则
  4. 面向接口编程 - 依赖抽象而不是具体实现
  5. 单一职责 - 一个类只做一件事
  6. 开闭原则 - 对扩展开放,对修改关闭

🚀 实际项目应用建议

小项目(个人项目、练手项目)

  • 主要使用:单例、工厂、观察者、策略
  • 重点练习模式的基本用法

中型项目(企业应用、团队开发)

  • 增加使用:适配器、装饰器、代理、外观、命令
  • 注重模式间的配合使用

大型项目(复杂系统、框架设计)

  • 综合运用:所有模式都可能用到
  • 注重架构层面的模式应用

**记住:设计模式是经验的总结,不是银弹。理解问题本质,选择合适方案,代码简洁可维护才是目标。

附录:23种设计模式使用频率排行

根据实际生产环境的使用情况,我将23种设计模式按使用频率从高到低进行排列:

🔥 极高频率使用(必须掌握)

1. 单例模式 (Singleton)

使用频率:⭐⭐⭐⭐⭐

  • 数据库连接池管理
  • 配置文件管理器(读取application.properties/yml)
  • 日志记录器(Logger)
  • Spring中的Bean默认作用域
  • 缓存管理器(Redis连接管理)
  • 线程池(ExecutorService)

2. 工厂模式 (Factory Method)

使用频率:⭐⭐⭐⭐⭐

  • Spring的BeanFactory创建Bean
  • 数据库连接工厂(不同数据库类型)
  • 支付方式工厂(微信、支付宝、银联)
  • 日志框架(SLF4J的LoggerFactory)
  • 消息队列生产者创建(Kafka、RabbitMQ)

3. 代理模式 (Proxy)

使用频率:⭐⭐⭐⭐⭐

  • Spring AOP实现(动态代理)
  • MyBatis的Mapper接口代理
  • RPC远程调用(Dubbo、Feign)
  • 权限控制和安全检查
  • 事务管理
  • 缓存代理(二级缓存)

4. 策略模式 (Strategy)

使用频率:⭐⭐⭐⭐⭐

  • 支付策略(不同支付方式的处理)
  • 促销活动计算(满减、折扣、买赠)
  • 文件上传策略(本地、OSS、S3)
  • 登录方式策略(账号密码、手机验证码、第三方登录)
  • 排序算法选择
  • 数据导出格式(Excel、PDF、CSV)

5. 观察者模式 (Observer)

使用频率:⭐⭐⭐⭐⭐

  • Spring的事件监听机制(ApplicationEvent)
  • MQ消息订阅(订单创建后触发多个操作)
  • 前端响应式框架(Vue、React的数据绑定)
  • 系统监控告警
  • 用户行为埋点
  • 库存变动通知

🔥 高频率使用(重点掌握)

6. 装饰器模式 (Decorator)

使用频率:⭐⭐⭐⭐

  • Java I/O流(BufferedReader装饰FileReader)
  • Spring中的BeanWrapper
  • 数据加密装饰
  • 日志增强(添加追踪ID)
  • HTTP请求/响应包装
  • 缓存装饰

7. 模板方法模式 (Template Method)

使用频率:⭐⭐⭐⭐

  • Spring的JdbcTemplate、RestTemplate
  • Servlet的service方法
  • 抽象类定义算法骨架(数据导入流程)
  • 单元测试框架(JUnit的setUp/tearDown)
  • 爬虫框架(通用爬取流程)
  • 报表生成流程

8. 适配器模式 (Adapter)

使用频率:⭐⭐⭐⭐

  • Spring MVC的HandlerAdapter
  • 第三方接口适配(统一不同供应商接口)
  • 老系统与新系统对接
  • 日志框架适配(SLF4J适配Log4j)
  • 数据库驱动适配
  • 消息格式转换

9. 责任链模式 (Chain of Responsibility)

使用频率:⭐⭐⭐⭐

  • Servlet的Filter链
  • Spring Security的过滤器链
  • 请求参数校验(多级校验)
  • 审批流程(多级审批)
  • 异常处理链
  • 网关鉴权链路

10. 建造者模式 (Builder)

使用频率:⭐⭐⭐⭐

  • Lombok的@Builder注解
  • StringBuilder/StringBuffer
  • HTTP客户端构建(OkHttpClient)
  • 复杂对象创建(订单对象)
  • SQL构建器(MyBatis的SQL Builder)
  • 邮件对象构建

🔶 中等频率使用

11. 抽象工厂模式 (Abstract Factory)

使用频率:⭐⭐⭐

  • 数据库访问层工厂(DAO工厂族)
  • UI主题工厂(不同皮肤)
  • 跨平台组件工厂
  • 多数据源配置

12. 外观模式 (Facade)

使用频率:⭐⭐⭐

  • 微服务聚合API(BFF层)
  • 第三方SDK封装
  • 复杂子系统的统一接口
  • Service层聚合多个Manager

13. 组合模式 (Composite)

使用频率:⭐⭐⭐

  • 树形菜单结构
  • 组织架构树
  • 文件目录结构
  • 权限树管理
  • 商品类目树

14. 迭代器模式 (Iterator)

使用频率:⭐⭐⭐

  • Java集合框架(Iterator接口)
  • 数据库结果集遍历(ResultSet)
  • 分页查询迭代
  • 自定义集合遍历

15. 命令模式 (Command)

使用频率:⭐⭐⭐

  • 撤销/重做功能
  • 任务队列调度
  • 线程池任务提交(Runnable)
  • 数据库事务回滚
  • GUI按钮操作

16. 状态模式 (State)

使用频率:⭐⭐⭐

  • 订单状态流转
  • 工作流引擎
  • TCP连接状态
  • 游戏角色状态
  • 审批流状态

🔷 低频率使用

17. 原型模式 (Prototype)

使用频率:⭐⭐

  • 对象深拷贝(BeanUtils.copyProperties)
  • Spring的prototype作用域
  • 大对象复制优化
  • 缓存对象克隆

18. 桥接模式 (Bridge)

使用频率:⭐⭐

  • JDBC驱动(DriverManager)
  • 消息发送桥接(短信、邮件)
  • 跨平台图形库
  • 多维度变化场景

19. 享元模式 (Flyweight)

使用频率:⭐⭐

  • String常量池
  • Integer缓存池(-128~127)
  • 线程池
  • 数据库连接池
  • 对象池技术

20. 中介者模式 (Mediator)

使用频率:⭐⭐

  • MVC中的Controller
  • 聊天室服务器
  • 航空管制系统
  • 智能家居中控

21. 备忘录模式 (Memento)

使用频率:⭐

  • 编辑器撤销功能
  • 游戏存档
  • 数据库事务快照
  • 表单草稿保存

22. 访问者模式 (Visitor)

使用频率:⭐

  • 编译器AST遍历
  • 报表数据处理
  • 对象结构稳定但操作多变
  • XML文档解析

23. 解释器模式 (Interpreter)

使用频率:⭐

  • 正则表达式引擎
  • SQL解析器
  • 表达式计算器(SpEL)
  • 规则引擎
  • 自定义DSL

💡 总结建议

优先掌握前10个模式,它们覆盖了90%以上的实际开发场景。特别是:

  • 单例、工厂、代理、策略、观察者是最核心的5个模式
  • Spring框架大量使用了这些设计模式,理解它们有助于深入学习框架源码
  • 实际开发中往往组合使用多种设计模式,而不是单独使用
  • 不要为了用设计模式而用,要根据实际场景选择合适的模式