设计模式是前辈们对软件开发中反复出现的问题的成熟解决方案。掌握它们,让你的代码更具弹性、可复用性,与可维护性。
前言
设计模式是软件工程的“词汇表”,它让开发者能够用简洁的术语描述复杂的架构思想。Java 作为经典的面向对象语言,天然支持大部分 GoF 设计模式。但在实际项目中,很多同学要么过度设计(模式滥用),要么对模式一知半解(实现僵硬)。
本文将系统讲解23 种 GoF 设计模式在 Java 中的实现,涵盖创建型、结构型、行为型三大类,每个模式都会提供:
解决的核心问题
UML 角色与结构说明
完整可运行的 Java 代码示例(含现代 Lambda 优化)
JDK/Spring 等框架中的实际应用
使用场景、优缺点与常见误区
读前提示:本文较长,建议收藏。可以先阅读创建型模式,结合项目中的实际场景逐步消化。
目录
设计模式概述与分类
创建型模式
2.1 单例模式(Singleton)
2.2 工厂方法模式(Factory Method)
2.3 抽象工厂模式(Abstract Factory)
2.4 建造者模式(Builder)
2.5 原型模式(Prototype)
结构型模式
3.1 适配器模式(Adapter)
3.2 装饰器模式(Decorator)
3.3 代理模式(Proxy)
3.4 外观模式(Facade)
3.5 桥接模式(Bridge)
3.6 组合模式(Composite)
3.7 享元模式(Flyweight)
行为型模式
4.1 策略模式(Strategy)
4.2 模板方法模式(Template Method)
4.3 观察者模式(Observer)
4.4 责任链模式(Chain of Responsibility)
4.5 命令模式(Command)
4.6 状态模式(State)
4.7 迭代器模式(Iterator)
4.8 访问者模式(Visitor)
设计模式综合对比与选型建议
总结
1. 设计模式概述与分类
设计模式分为三大类:
| 类型 | 关注点 | 包含模式 |
|---|---|---|
| 创建型 | 对象创建机制,隐藏实例化逻辑 | 单例、工厂方法、抽象工厂、建造者、原型 |
| 结构型 | 类与对象的组合,形成更大的结构 | 适配器、装饰器、代理、外观、桥接、组合、享元 |
| 行为型 | 对象之间的职责分配与通信 | 策略、模板方法、观察者、责任链、命令、状态、迭代器、访问者、中介者、备忘录、解释器 |
本文会覆盖最常用的15种模式,对于解释器等日常开发较少使用的模式仅作简要介绍。
2. 创建型模式
2.1 单例模式(Singleton)
确保一个类只有一个实例,并提供全局访问点。
实现方式(线程安全优先)
① 饿汉式(线程安全,推荐)
java
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }② 静态内部类(懒加载,线程安全)
java
public class Singleton { private Singleton() {} private static class Holder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; } }③ 双重检查锁(DCL,需注意 volatile)
java
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; } }④ 枚举(最简洁,天然防反射/序列化)
java
public enum SingletonEnum { INSTANCE; public void doSomething() { ... } }使用场景
配置管理类、日志类、数据库连接池。
需要严格控制全局唯一资源的场景。
注意事项
反射可以破坏私有构造器,枚举可以防御。
序列化需实现
readResolve()方法返回单例。
2.2 工厂方法模式(Factory Method)
定义一个创建对象的接口,让子类决定实例化哪一个类。将对象的实例化延迟到子类。
结构
Product:产品接口
ConcreteProduct:具体产品
Factory:工厂接口(声明工厂方法)
ConcreteFactory:具体工厂,实现工厂方法返回具体产品
代码示例
java
// 产品接口 interface Product { void doStuff(); } // 具体产品 A class ConcreteProductA implements Product { @Override public void doStuff() { System.out.println("Product A"); } } // 具体产品 B class ConcreteProductB implements Product { @Override public void doStuff() { System.out.println("Product B"); } } // 工厂接口 interface Factory { Product createProduct(); } // 具体工厂 A class FactoryA implements Factory { @Override public Product createProduct() { return new ConcreteProductA(); } } class FactoryB implements Factory { @Override public Product createProduct() { return new ConcreteProductB(); } } // 使用 Factory factory = new FactoryA(); Product product = factory.createProduct(); product.doStuff();实际应用
java.util.Collection中的iterator()方法可以看作工厂方法(不同集合返回不同迭代器)。JDBC 中
Connection.createStatement()返回Statement对象。
优点
单一职责:产品创建与使用分离。
开闭原则:新增产品只需新增工厂类。
2.3 抽象工厂模式(Abstract Factory)
创建一系列相关或相互依赖的对象族,而无需指定它们具体的类。
场景
需要生产不同品牌(如 Windows、Mac)的 UI 组件(Button、Checkbox)。
代码示例
java
// 产品族1:按钮 interface Button { void paint(); } class WinButton implements Button { public void paint() { System.out.println("Win Button"); } } class MacButton implements Button { public void paint() { System.out.println("Mac Button"); } } // 产品族2:复选框 interface Checkbox { void paint(); } class WinCheckbox implements Checkbox { public void paint() { System.out.println("Win Checkbox"); } } class MacCheckbox implements Checkbox { public void paint() { System.out.println("Mac Checkbox"); } } // 抽象工厂 interface GUIFactory { Button createButton(); Checkbox createCheckbox(); } // 具体工厂:Windows 风格 class WinFactory implements GUIFactory { public Button createButton() { return new WinButton(); } public Checkbox createCheckbox() { return new WinCheckbox(); } } // 具体工厂:Mac 风格 class MacFactory implements GUIFactory { public Button createButton() { return new MacButton(); } public Checkbox createCheckbox() { return new MacCheckbox(); } } // 客户端代码 class Application { private Button button; private Checkbox checkbox; public Application(GUIFactory factory) { button = factory.createButton(); checkbox = factory.createCheckbox(); } public void paint() { button.paint(); checkbox.paint(); } }与工厂方法的区别
工厂方法:一个工厂生产一种产品。
抽象工厂:一个工厂生产一系列相关联的产品。
实际应用
javax.xml.parsers.DocumentBuilderFactory返回DocumentBuilder和SAXParser等系列产品。
2.4 建造者模式(Builder)
将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。
适用于参数众多、部分可选、构造时需校验的对象(如消息体、HTTP 请求)。
经典写法(链式调用)
java
public class User { private final String name; // 必填 private final int age; // 必填 private final String phone; // 可选 private final String address; // 可选 private User(Builder builder) { this.name = builder.name; this.age = builder.age; this.phone = builder.phone; this.address = builder.address; } public static class Builder { // 必填参数 private final String name; private final int age; // 可选参数 private String phone = ""; private String address = ""; public Builder(String name, int age) { this.name = name; this.age = age; } public Builder phone(String phone) { this.phone = phone; return this; } public Builder address(String address) { this.address = address; return this; } public User build() { // 可添加校验逻辑 if (age < 0 || age > 150) throw new IllegalArgumentException("age invalid"); return new User(this); } } // getters 省略 } // 使用 User user = new User.Builder("张三", 25) .phone("13800000000") .address("北京") .build();实际应用
StringBuilder、StringBuffer 的
append链式调用。Lombok 的
@Builder注解自动生成。Spring 中的
UriComponentsBuilder。
2.5 原型模式(Prototype)
通过复制已有实例来创建新对象,而不是通过 new 关键字。
实现方式:实现Cloneable接口,重写clone()方法。
java
public class Sheep implements Cloneable { private String name; private Date birth; public Sheep(String name, Date birth) { ... } @Override protected Sheep clone() throws CloneNotSupportedException { Sheep clone = (Sheep) super.clone(); // 浅拷贝 // 如果需要深拷贝,对引用类型也进行克隆 clone.birth = (Date) this.birth.clone(); return clone; } }使用场景
对象创建成本高(如数据库连接、复杂计算)。
需要大量相似对象,且属性差异不大。
注意
默认
super.clone()是浅拷贝,需要深拷贝时需手动处理引用类型。也可使用序列化方式实现深拷贝。
3. 结构型模式
3.1 适配器模式(Adapter)
将一个类的接口转换为客户希望的另一个接口,使得原本不兼容的类可以一起工作。
类适配器(继承) vs 对象适配器(组合)
对象适配器示例(优先使用组合):
java
// 已有类,接口不匹配 class LegacyPrinter { public void printOld(String text) { System.out.println("Legacy: " + text); } } // 目标接口 interface Printer { void print(String message); } // 适配器 class PrinterAdapter implements Printer { private LegacyPrinter legacy; public PrinterAdapter(LegacyPrinter legacy) { this.legacy = legacy; } @Override public void print(String message) { legacy.printOld(message); } } // 使用 LegacyPrinter old = new LegacyPrinter(); Printer p = new PrinterAdapter(old); p.print("Hello");应用场景
将旧系统接口适配到新接口。
java.io.InputStreamReader将字节流适配为字符流。
3.2 装饰器模式(Decorator)
动态地给对象添加额外的职责,比继承更灵活。
经典:咖啡订单系统
java
// 抽象组件 interface Coffee { double cost(); String description(); } // 具体组件 class SimpleCoffee implements Coffee { public double cost() { return 2.0; } public String description() { return "Simple coffee"; } } // 装饰器抽象类 abstract class CoffeeDecorator implements Coffee { protected Coffee decoratedCoffee; public CoffeeDecorator(Coffee coffee) { this.decoratedCoffee = coffee; } } // 具体装饰器:加牛奶 class MilkDecorator extends CoffeeDecorator { public MilkDecorator(Coffee coffee) { super(coffee); } public double cost() { return decoratedCoffee.cost() + 0.5; } public String description() { return decoratedCoffee.description() + ", milk"; } } // 具体装饰器:加糖 class SugarDecorator extends CoffeeDecorator { public SugarDecorator(Coffee coffee) { super(coffee); } public double cost() { return decoratedCoffee.cost() + 0.2; } public String description() { return decoratedCoffee.description() + ", sugar"; } } // 使用 Coffee coffee = new SimpleCoffee(); coffee = new MilkDecorator(coffee); coffee = new SugarDecorator(coffee); System.out.println(coffee.description() + " -> $" + coffee.cost());对比代理模式
装饰器:增强原有功能,客户端可以嵌套多层。
代理:控制访问,通常不添加新功能。
Java I/O 经典应用
java
new BufferedInputStream(new FileInputStream("file.txt"));3.3 代理模式(Proxy)
为其他对象提供一种代理以控制对这个对象的访问。
静态代理
java
interface Image { void display(); } class RealImage implements Image { private String file; public RealImage(String file) { this.file = file; loadFromDisk(); } private void loadFromDisk() { System.out.println("Loading " + file); } public void display() { System.out.println("Displaying " + file); } } class ProxyImage implements Image { private RealImage realImage; private String file; public ProxyImage(String file) { this.file = file; } public void display() { if (realImage == null) realImage = new RealImage(file); realImage.display(); } }动态代理(基于 JDK Proxy)
java
// 需要接口 InvocationHandler handler = (proxy, method, args) -> { System.out.println("Before method"); Object result = method.invoke(target, args); System.out.println("After method"); return result; }; Image proxy = (Image) Proxy.newProxyInstance( Image.class.getClassLoader(), new Class[]{Image.class}, handler ); proxy.display();常见应用
Spring AOP(基于动态代理)。
延迟加载(Hibernate 懒加载)。
远程调用(RMI)。
3.4 外观模式(Facade)
为子系统中的一组接口提供一个一致的界面,简化调用。
java
// 复杂子系统 class CPU { public void start() {...} } class Memory { public void load() {...} } class HardDrive { public void read() {...} } // 外观类 class ComputerFacade { private CPU cpu; private Memory memory; private HardDrive disk; public ComputerFacade() { this.cpu = new CPU(); this.memory = new Memory(); this.disk = new HardDrive(); } public void start() { cpu.start(); memory.load(); disk.read(); } } // 客户端仅调用外观方法 ComputerFacade computer = new ComputerFacade(); computer.start();应用
java.nio.file.Files提供文件操作的简化方法。Spring JDBC 的
JdbcTemplate封装了数据库操作的细节。
3.5 桥接模式(Bridge)
将抽象部分与实现部分分离,使它们都可以独立变化。
场景:不同形状(圆形、方形)与不同颜色(红色、蓝色)的组合,避免类爆炸。
java
// 实现类接口(颜色) interface Color { void applyColor(); } class Red implements Color { public void applyColor() { System.out.println("red"); } } class Blue implements Color { public void applyColor() { System.out.println("blue"); } } // 抽象类(形状) abstract class Shape { protected Color color; public Shape(Color color) { this.color = color; } abstract void draw(); } class Circle extends Shape { public Circle(Color color) { super(color); } void draw() { System.out.print("Circle fill "); color.applyColor(); } } class Square extends Shape { public Square(Color color) { super(color); } void draw() { System.out.print("Square fill "); color.applyColor(); } }优点
避免继承爆炸(M×N 组合只需 M+N 个类)。
符合开闭原则。
3.6 组合模式(Composite)
将对象组合成树形结构以表示“部分-整体”层次结构,使客户端对单个对象和组合对象的使用具有一致性。
java
// 组件接口 interface FileSystemNode { void ls(); } // 叶子节点 class File implements FileSystemNode { private String name; public File(String name) { this.name = name; } public void ls() { System.out.println("File: " + name); } } // 容器节点 class Directory implements FileSystemNode { private String name; private List<FileSystemNode> children = new ArrayList<>(); public Directory(String name) { this.name = name; } public void add(FileSystemNode node) { children.add(node); } public void ls() { System.out.println("Directory: " + name); children.forEach(FileSystemNode::ls); } }应用
文件系统。
GUI 容器与组件(Swing 的
Container和Component)。
3.7 享元模式(Flyweight)
运用共享技术有效地支持大量细粒度对象,减少内存占用。
典型:围棋棋子(颜色相同可共享,位置不同为外部状态)
java
// 享元类(内部状态:颜色) class ChessPiece { private String color; public ChessPiece(String color) { this.color = color; } public void place(int x, int y) { System.out.println(color + " piece at (" + x + "," + y + ")"); } } // 工厂管理享元 class ChessPieceFactory { private static final Map<String, ChessPiece> pool = new HashMap<>(); public static ChessPiece getPiece(String color) { return pool.computeIfAbsent(color, ChessPiece::new); } } // 客户端使用 ChessPiece black1 = ChessPieceFactory.getPiece("black"); black1.place(1, 1); ChessPiece black2 = ChessPieceFactory.getPiece("black"); // 同一个对象应用
Integer 缓存(-128~127)。
String 常量池。
数据库连接池(虽然后者更接近对象池模式)。
4. 行为型模式
4.1 策略模式(Strategy)
定义一系列算法,将每个算法封装起来,并使它们可以互相替换。
示例:促销活动
java
// 策略接口 interface PromotionStrategy { double applyDiscount(double price); } // 具体策略 class NoDiscount implements PromotionStrategy { public double applyDiscount(double price) { return price; } } class PercentageDiscount implements PromotionStrategy { private double percent; public PercentageDiscount(double percent) { this.percent = percent; } public double applyDiscount(double price) { return price * (1 - percent / 100); } } // 上下文 class Order { private PromotionStrategy strategy; public void setStrategy(PromotionStrategy strategy) { this.strategy = strategy; } public double finalPrice(double original) { return strategy.applyDiscount(original); } } // 使用(也可结合Lambda简化) Order order = new Order(); order.setStrategy(new PercentageDiscount(20)); System.out.println(order.finalPrice(100.0)); // 80.0Java 8+ 简化策略
java
// 使用函数式接口 public class Order { private Function<Double, Double> strategy = Function.identity(); public void setStrategy(Function<Double, Double> strategy) { this.strategy = strategy; } public double finalPrice(double price) { return strategy.apply(price); } } order.setStrategy(price -> price * 0.8);应用
java.util.Comparator的不同排序策略。Spring 的
Resource加载策略(ClassPathResource,FileSystemResource)。
4.2 模板方法模式(Template Method)
定义一个算法的骨架,将一些步骤延迟到子类中实现,不改变算法结构的情况下重定义某些步骤。
java
abstract class DataProcessor { // 模板方法,声明为 final 防止子类修改算法顺序 public final void process() { readData(); processData(); writeData(); } protected abstract void readData(); protected abstract void processData(); protected void writeData() { System.out.println("Writing to default output"); } } class CSVProcessor extends DataProcessor { protected void readData() { System.out.println("Reading CSV"); } protected void processData() { System.out.println("Processing CSV"); } } class XMLProcessor extends DataProcessor { protected void readData() { System.out.println("Reading XML"); } protected void processData() { System.out.println("Processing XML"); } // 可覆盖 writeData }应用
Java 集合的
AbstractList中的addAll方法。Servlet 中的
doGet、doPost(HttpServlet 的 service 方法是模板方法)。
钩子方法
允许子类控制算法中的某些步骤,例如提供一个boolean方法供子类覆盖。
4.3 观察者模式(Observer)
定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象自动收到通知。
Java 内置实现(已过时,推荐自定义)
java
import java.util.ArrayList; import java.util.List; // 主题 class NewsAgency { private List<Channel> channels = new ArrayList<>(); public void addChannel(Channel channel) { channels.add(channel); } public void removeChannel(Channel channel) { channels.remove(channel); } public void sendNews(String news) { for (Channel c : channels) c.update(news); } } // 观察者接口 interface Channel { void update(String news); } // 具体观察者 class NewsChannel implements Channel { private String news; public void update(String news) { this.news = news; System.out.println("Received: " + news); } } // 使用 NewsAgency agency = new NewsAgency(); agency.addChannel(new NewsChannel()); agency.sendNews("Hello World!");Java 9+FlowAPI(响应式流)
java
// 参考 java.util.concurrent.Flow 实现发布订阅
框架应用
Spring 事件机制(
ApplicationEvent+@EventListener)。消息队列(MQ)本质也是观察者模式的分布式变体。
4.4 责任链模式(Chain of Responsibility)
将请求的发送者和接收者解耦,多个对象都有机会处理该请求,形成一条链。
java
// 抽象处理器 abstract class Logger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; protected Logger nextLogger; public void setNext(Logger next) { this.nextLogger = next; } public void logMessage(int level, String msg) { if (this.level <= level) { write(msg); } if (nextLogger != null) nextLogger.logMessage(level, msg); } protected abstract void write(String msg); } class ConsoleLogger extends Logger { public ConsoleLogger(int level) { this.level = level; } protected void write(String msg) { System.out.println("Console: " + msg); } } class FileLogger extends Logger { public FileLogger(int level) { this.level = level; } protected void write(String msg) { System.out.println("File: " + msg); } }应用
Servlet 的
FilterChain。Spring Security 的过滤器链。
日志框架(如 Log4j 的 Appender 链)。
4.5 命令模式(Command)
将请求封装为对象,从而支持请求的排队、记录日志、撤销等操作。
java
// 命令接口 interface Command { void execute(); void undo(); } // 接收者 class Light { public void on() { System.out.println("Light On"); } public void off() { System.out.println("Light Off"); } } // 具体命令 class LightOnCommand implements Command { private Light light; public LightOnCommand(Light light) { this.light = light; } public void execute() { light.on(); } public void undo() { light.off(); } } // 调用者(遥控器) class RemoteControl { private Command command; public void setCommand(Command command) { this.command = command; } public void pressButton() { command.execute(); } }应用
线程池中的
Runnable(本质是命令模式)。事务操作的回滚。
菜单项与按钮的操作。
4.6 状态模式(State)
允许对象在内部状态改变时改变它的行为,对象看起来像是修改了它的类。
java
// 状态接口 interface State { void handle(); } class ConcreteStateA implements State { public void handle() { System.out.println("State A handling"); } } class Context { private State state; public void setState(State state) { this.state = state; } public void request() { state.handle(); } }对比策略模式
策略模式:行为由外部传入,互不感知。
状态模式:状态之间可以转换,常由 Context 管理。
应用
工作流引擎(请假审批的不同状态)。
订单状态机。
4.7 迭代器模式(Iterator)
提供一种方法顺序访问聚合对象中的各个元素,而不暴露其内部表示。
Java 已经内置了java.util.Iterator,自定义示例:
java
class MyList<T> implements Iterable<T> { private T[] items; public MyList(T... items) { this.items = items; } @Override public Iterator<T> iterator() { return new Iterator<T>() { private int index = 0; public boolean hasNext() { return index < items.length; } public T next() { return items[index++]; } }; } }实际应用
所有 Java 集合框架的
iterator()方法。
4.8 访问者模式(Visitor)
将算法与数据结构分离,使得在不改变元素类的前提下增加作用于这些元素的新操作。
java
// 元素接口 interface Element { void accept(Visitor visitor); } // 具体元素 class Book implements Element { public void accept(Visitor visitor) { visitor.visit(this); } public String getTitle() { return "Design Patterns"; } } // 访问者接口 interface Visitor { void visit(Book book); void visit(Fruit fruit); } // 具体访问者:计算价格 class PriceVisitor implements Visitor { public void visit(Book book) { System.out.println("Book price: $30"); } public void visit(Fruit fruit) { System.out.println("Fruit price: $5"); } }优缺点
增加新操作容易(增加 Visitor 实现类)。
增加新元素困难(需修改所有 Visitor 接口)。
破坏了封装性。
应用
ASM、ANTLR 等编译器工具。
Spring 中
BeanDefinitionVisitor。
5. 设计模式综合对比与选型建议
| 需求/场景 | 推荐模式 |
|---|---|
| 只需要一个全局实例 | 单例 |
| 创建复杂对象,参数多且有可选字段 | 建造者 |
| 需要创建一系列相关产品 | 抽象工厂 |
| 动态给对象添加职责 | 装饰器 |
| 控制对象访问(权限、懒加载) | 代理 |
| 简化复杂子系统调用 | 外观 |
| 多种算法可互换 | 策略 |
| 算法固定,部分步骤可变 | 模板方法 |
| 一对多通知更新 | 观察者 |
| 请求逐级处理 | 责任链 |
| 对象状态变化导致行为变化 | 状态 |
| 扩展类的功能而不修改类 | 访问者(谨慎使用) |
6. 总结
设计模式是前人智慧的结晶,但切忌生搬硬套。遵循以下原则:
优先使用组合而非继承(如策略、装饰器模式)。
针对接口编程,不针对实现编程。
开闭原则:对扩展开放,对修改关闭。
单一职责原则:每个模式都体现了职责分离。
现代 Java 开发中,许多模式已被函数式编程(Lambda、Stream)简化(如策略模式可以用函数式接口替代)。建议结合 Spring 框架的源码学习,体会设计模式在实际框架中的落地。
最后送上一句:“过度设计是罪恶,没有设计是灾难。”
如果本文对你有帮助,欢迎点赞、收藏、转发!关于设计模式在实际项目中的应用,有任何疑问请留言讨论。