前言:设计模式不是银弹,但它是程序员之间的"通用语言"。本文以一个Java开发者的视角,用生活类比+实战代码,带你一次搞懂GoF的23种设计模式。
目录
一、设计模式是什么?为什么要学?
1.1 一句话理解
设计模式 = 前人总结的、反复出现的、特定场景下的最优代码结构。
就像做菜有菜谱、下棋有棋谱,写代码也有"码谱"。GoF(Gang of Four)四人在1994年把最常见的23种"码谱"整理成了《设计模式》一书。
1.2 为什么要学?
1.3 三大分类
23种设计模式
├── 创建型(5种):关注怎么创建对象
│ ├── 单例模式 Singleton
│ ├── 工厂方法模式 Factory Method
│ ├── 抽象工厂模式 Abstract Factory
│ ├── 建造者模式 Builder
│ └── 原型模式 Prototype
├── 结构型(7种):关注怎么组合对象
│ ├── 适配器模式 Adapter
│ ├── 桥接模式 Bridge
│ ├── 组合模式 Composite
│ ├── 装饰器模式 Decorator
│ ├── 外观模式 Facade
│ ├── 享元模式 Flyweight
│ └── 代理模式 Proxy
└── 行为型(11种):关注对象之间怎么通信
├── 责任链模式 Chain of Responsibility
├── 命令模式 Command
├── 解释器模式 Interpreter
├── 迭代器模式 Iterator
├── 中介者模式 Mediator
├── 备忘录模式 Memento
├── 观察者模式 Observer
├── 状态模式 State
├── 策略模式 Strategy
├── 模板方法模式 Template Method
└── 访问者模式 Visitor二、六大设计原则
在学模式之前,先理解这6条原则,它们是设计模式的"内功心法":
一句话总结:开闭原则是目标,其他5个原则是达到目标的手段。
三、创建型模式(5种)
核心问题:怎样创建对象? 把"创建逻辑"和"使用逻辑"分离。
1. 单例模式(Singleton)
生活类比:一个国家只能有一个总统。
意图:保证一个类只有一个实例,并提供全局访问点。
Java实现(推荐:静态内部类):
public class Singleton {
// 1. 私有构造,禁止外部 new
private Singleton() {}
// 2. 静态内部类,JVM保证线程安全 + 懒加载
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
// 3. 全局访问点
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}其他实现方式对比:
Spring中的应用:Bean默认就是单例(scope="singleton"),但Spring用ConcurrentHashMap管理,不是GoF单例。
2. 工厂方法模式(Factory Method)
生活类比:想吃汉堡,不用自己做,去麦当劳"工厂"拿。
意图:定义创建对象的接口,让子类决定实例化哪个类。
// 抽象产品
public interface Logger {
void log(String msg);
}
// 具体产品
public class FileLogger implements Logger {
public void log(String msg) { System.out.println("写文件: " + msg); }
}
public class ConsoleLogger implements Logger {
public void log(String msg) { System.out.println("控制台: " + msg); }
}
// 工厂接口
public interface LoggerFactory {
Logger createLogger();
}
// 具体工厂
public class FileLoggerFactory implements LoggerFactory {
public Logger createLogger() { return new FileLogger(); }
}
public class ConsoleLoggerFactory implements LoggerFactory {
public Logger createLogger() { return new ConsoleLogger(); }
}
// 使用
LoggerFactory factory = new FileLoggerFactory();
Logger logger = factory.createLogger();
logger.log("Hello");简单工厂 vs 工厂方法:
简单工厂:一个工厂类,里面一个switch(不是GoF模式,违反OCP)
工厂方法:每个产品对应一个工厂类(符合OCP,加新产品只需加新工厂)
Spring中的应用:BeanFactory就是工厂方法模式的经典实现。
3. 抽象工厂模式(Abstract Factory)
生活类比:小米生态链——小米工厂同时生产手机、电视、手环(一族产品)。
意图:创建一系列相关的产品对象,而无需指定具体类。
// 产品族:手机
public interface Phone { void call(); }
public class XiaomiPhone implements Phone { public void call(){System.out.println("小米打电话");} }
public class HuaweiPhone implements Phone { public void call(){System.out.println("华为打电话");} }
// 产品族:电视
public interface TV { void play(); }
public class XiaomiTV implements TV { public void play(){System.out.println("小米电视播放");} }
public class HuaweiTV implements TV { public void play(){System.out.println("华为电视播放");} }
// 抽象工厂
public interface DeviceFactory {
Phone createPhone();
TV createTV();
}
// 小米工厂:生产一整套小米产品
public class XiaomiFactory implements DeviceFactory {
public Phone createPhone() { return new XiaomiPhone(); }
public TV createTV() { return new XiaomiTV(); }
}工厂方法 vs 抽象工厂:
工厂方法:一个工厂生产一种产品
抽象工厂:一个工厂生产一族产品
4. 建造者模式(Builder)
生活类比:去赛百味点三明治——一步步选面包、肉、蔬菜、酱,最后组装。
意图:将一个复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示。
public class Computer {
private String cpu;
private String memory;
private String disk;
private String gpu;
// getter省略...
public static class Builder {
private Computer computer = new Computer();
public Builder cpu(String cpu) { computer.cpu = cpu; return this; }
public Builder memory(String m) { computer.memory = m; return this; }
public Builder disk(String d) { computer.disk = d; return this; }
public Builder gpu(String g) { computer.gpu = g; return this; }
public Computer build() {
// 可以在这里做参数校验
if (computer.cpu == null) throw new IllegalStateException("CPU必填");
return computer;
}
}
}
// 链式调用,清爽!
Computer pc = new Computer.Builder()
.cpu("i9-13900K")
.memory("64GB DDR5")
.disk("2TB NVMe SSD")
.gpu("RTX 4090")
.build();日常开发中处处可见:
StringBuilder.append().append().toString()OkHttpClient.BuilderLombok @BuilderStream API中的
Collectors
5. 原型模式(Prototype)
生活类比:细胞分裂——一个变两个,基因一模一样。
意图:通过复制已有对象来创建新对象,而不是new。
public class Resume implements Cloneable {
private String name;
private String company;
private List<String> skills;
@Override
protected Resume clone() {
try {
Resume copy = (Resume) super.clone(); // 浅拷贝
copy.skills = new ArrayList<>(this.skills); // 深拷贝引用类型
return copy;
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
}
// 使用:基于模板快速创建
Resume template = new Resume("张三", "阿里", Arrays.asList("Java","Spring"));
Resume copy1 = template.clone();
copy1.setName("李四"); // 改名字不影响原对象浅拷贝 vs 深拷贝:
浅拷贝:
super.clone(),基本类型复制值,引用类型复制地址(共享!)深拷贝:递归复制所有引用对象,或用序列化/反序列化实现
Spring中的应用:scope="prototype"的Bean每次getBean()都返回新实例。
四、结构型模式(7种)
核心问题:怎样把对象组合成更大的结构?
6. 适配器模式(Adapter)
生活类比:出国旅行的电源转换插头——让中国插头(不兼容)能插在外国插座上。
意图:将一个类的接口转换成客户期望的另一个接口。
// 老系统接口(不兼容)
public interface OldPrinter {
void printOld(String content);
}
// 新系统接口(期望的)
public interface NewPrinter {
void print(String content);
}
// 适配器:让老打印机适配新接口
public class PrinterAdapter implements NewPrinter {
private OldPrinter oldPrinter;
public PrinterAdapter(OldPrinter old) { this.oldPrinter = old; }
@Override
public void print(String content) {
oldPrinter.printOld(content); // 委托给老系统
}
}
// 使用
NewPrinter printer = new PrinterAdapter(new OldPrinterImpl());
printer.print("Hello"); // 调用新接口,内部走老系统JDK中的应用:InputStreamReader把InputStream(字节流)适配成Reader(字符流)。
7. 桥接模式(Bridge)
生活类比:咖啡店的"口味"和"杯型"是两个维度——大杯/小杯 × 加糖/加奶/原味,不需要2×3=6个类。
意图:将抽象部分与实现部分分离,使它们可以独立变化。
// 实现维度:咖啡口味
public interface CoffeeAddition {
void add();
}
public class Sugar implements CoffeeAddition {
public void add() { System.out.println("加糖"); }
}
public class Milk implements CoffeeAddition {
public void add() { System.out.println("加奶"); }
}
// 抽象维度:咖啡杯型(持有口味的引用 = "桥")
public abstract class Coffee {
protected CoffeeAddition addition; // 桥接点
public Coffee(CoffeeAddition addition) { this.addition = addition; }
public abstract void make();
}
public class LargeCoffee extends Coffee {
public LargeCoffee(CoffeeAddition a) { super(a); }
public void make() {
System.out.print("大杯咖啡: ");
addition.add();
}
}
// 自由组合
new LargeCoffee(new Sugar()).make(); // 大杯咖啡: 加糖
new LargeCoffee(new Milk()).make(); // 大杯咖啡: 加奶关键区别:适配器是"事后补救",桥接是"事前设计"。
8. 组合模式(Composite)
生活类比:文件系统——文件夹里面可以有文件,也可以有子文件夹,统一用"节点"表示。
意图:将对象组合成树形结构,使用户对单个对象和组合对象的使用具有一致性。
public abstract class FileSystemNode {
protected String name;
public FileSystemNode(String name) { this.name = name; }
public abstract int getSize();
public abstract void print(String indent);
}
public class File extends FileSystemNode {
private int size;
public File(String name, int size) { super(name); this.size = size; }
public int getSize() { return size; }
public void print(String indent) {
System.out.println(indent + "- " + name + " (" + size + "KB)");
}
}
public class Folder extends FileSystemNode {
private List<FileSystemNode> children = new ArrayList<>();
public Folder(String name) { super(name); }
public void add(FileSystemNode node) { children.add(node); }
public int getSize() {
return children.stream().mapToInt(FileSystemNode::getSize).sum();
}
public void print(String indent) {
System.out.println(indent + "+ " + name + "/");
children.forEach(c -> c.print(indent + " "));
}
}
// 构建树
Folder root = new Folder("项目");
Folder src = new Folder("src");
src.add(new File("Main.java", 10));
src.add(new File("Utils.java", 5));
root.add(src);
root.add(new File("README.md", 2));
root.print("");
// 输出:
// + 项目/
// + src/
// - Main.java (10KB)
// - Utils.java (5KB)
// - README.md (2KB)JDK中的应用:AWT/Swing的Container和Component。
9. 装饰器模式(Decorator)
生活类比:咖啡加料——基础咖啡 + 加糖 + 加奶 = 一杯定制咖啡,价格逐层叠加。
意图:动态地给对象添加额外职责,比继承更灵活。
// 组件接口
public interface Coffee {
double cost();
String description();
}
// 基础组件
public class SimpleCoffee implements Coffee {
public double cost() { return 10; }
public String description() { return "基础咖啡"; }
}
// 装饰器基类
public abstract class CoffeeDecorator implements Coffee {
protected Coffee wrapped; // 持有被装饰对象
public CoffeeDecorator(Coffee c) { this.wrapped = c; }
public double cost() { return wrapped.cost(); }
public String description() { return wrapped.description(); }
}
// 具体装饰器
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee c) { super(c); }
public double cost() { return super.cost() + 2; }
public String description() { return super.description() + " + 糖"; }
}
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee c) { super(c); }
public double cost() { return super.cost() + 3; }
public String description() { return super.description() + " + 奶"; }
}
// 层层包装
Coffee myCoffee = new MilkDecorator(new SugarDecorator(new SimpleCoffee()));
System.out.println(myCoffee.description()); // 基础咖啡 + 糖 + 奶
System.out.println("价格: " + myCoffee.cost()); // 15.0Java I/O就是装饰器模式的教科书:
BufferedReader reader = new BufferedReader(
new InputStreamReader(
new FileInputStream("data.txt"), "UTF-8"));10. 外观模式(Facade)
生活类比:酒店前台——你不用自己找厨师、保洁、行李员,跟前台说一声就行。
意图:为子系统提供一个统一的高层接口,使子系统更容易使用。
// 子系统(复杂)
public class OrderService { void createOrder() { /*...*/ } }
public class PaymentService { void pay() { /*...*/ } }
public class InventoryService { void deductStock() { /*...*/ } }
public class NotificationService { void sendSMS() { /*...*/ } }
// 外观类(简单)
public class OrderFacade {
private OrderService orderService = new OrderService();
private PaymentService payService = new PaymentService();
private InventoryService inventoryService = new InventoryService();
private NotificationService notifyService = new NotificationService();
public void placeOrder() {
orderService.createOrder();
inventoryService.deductStock();
payService.pay();
notifyService.sendSMS();
}
}
// 调用方只需要一行
new OrderFacade().placeOrder();Spring中的应用:JdbcTemplate就是JDBC操作的外观类,封装了Connection/Statement/ResultSet的繁琐操作。
11. 享元模式(Flyweight)
生活类比:围棋只有黑白两色棋子(享元),但棋盘上有361个位置,不需要造361颗棋子。
意图:运用共享技术有效地支持大量细粒度的对象。
// 享元对象:棋子(共享)
public class ChessPiece {
private final String color; // 内部状态(共享的)
private final String icon; // 内部状态
public ChessPiece(String color) {
this.color = color;
this.icon = loadIcon(color);
}
private String loadIcon(String color) { return color + "棋子图标"; }
public void draw(int x, int y) { // 外部状态(不共享,每次传入)
System.out.println("在(" + x + "," + y + ")画" + color + "棋子");
}
}
// 享元工厂
public class ChessPieceFactory {
private static final Map<String, ChessPiece> cache = new HashMap<>();
public static ChessPiece getPiece(String color) {
return cache.computeIfAbsent(color, ChessPiece::new);
}
}
// 使用:361个位置只创建2个棋子对象
ChessPieceFactory.getPiece("黑").draw(3, 5);
ChessPieceFactory.getPiece("白").draw(4, 6);
ChessPieceFactory.getPiece("黑").draw(5, 7); // 复用同一个"黑"对象JDK中的应用:Integer.valueOf()缓存了-128~127的对象;String.intern()字符串常量池。
12. 代理模式(Proxy)
生活类比:明星的经纪人——你想找明星拍戏,先跟经纪人谈。
意图:为其他对象提供一种代理以控制对这个对象的访问。
// 接口
public interface UserService {
void save(User user);
}
// 真实对象
public class UserServiceImpl implements UserService {
public void save(User user) { System.out.println("保存用户: " + user); }
}
// 静态代理
public class UserServiceProxy implements UserService {
private UserService target;
public UserServiceProxy(UserService target) { this.target = target; }
public void save(User user) {
System.out.println("[日志] 开始保存用户");
long start = System.currentTimeMillis();
target.save(user);
long cost = System.currentTimeMillis() - start;
System.out.println("[日志] 保存完成,耗时" + cost + "ms");
}
}三种代理对比:
Spring AOP的本质:有接口用JDK动态代理,没接口用CGLIB代理。
五、行为型模式(11种)
核心问题:对象之间怎么交互和分配职责?
13. 责任链模式(Chain of Responsibility)
生活类比:公司审批流程——项目经理→部门经理→总经理,谁能批就谁批。
意图:让多个对象都有机会处理请求,避免发送者和接收者耦合。
// 处理器抽象
public abstract class Handler {
protected Handler next;
public void setNext(Handler next) { this.next = next; }
public void handle(Request request) {
if (canHandle(request)) {
doHandle(request);
} else if (next != null) {
next.handle(request);
} else {
System.out.println("无人能处理: " + request);
}
}
protected abstract boolean canHandle(Request request);
protected abstract void doHandle(Request request);
}
public class ManagerHandler extends Handler {
protected boolean canHandle(Request r) { return r.getAmount() <= 5000; }
protected void doHandle(Request r) { System.out.println("经理审批: " + r); }
}
public class DirectorHandler extends Handler {
protected boolean canHandle(Request r) { return r.getAmount() <= 50000; }
protected void doHandle(Request r) { System.out.println("总监审批: " + r); }
}
// 组装链条
Handler chain = new ManagerHandler();
chain.setNext(new DirectorHandler());
chain.handle(new Request("采购", 3000)); // 经理审批
chain.handle(new Request("采购", 30000)); // 总监审批Servlet中的应用:FilterChain就是典型的责任链。
14. 命令模式(Command)
生活类比:餐厅点单——你(调用者)对服务员说"来一份牛排",服务员(命令)把订单交给厨师(接收者)。
意图:将请求封装为对象,从而可以用不同的请求参数化其他对象。
// 命令接口
public interface Command { void execute(); void undo(); }
// 接收者
public class Light {
public void on() { System.out.println("灯亮了"); }
public void off() { System.out.println("灯灭了"); }
}
// 具体命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) { this.light = light; }
public void execute() { light.on(); }
public void undo() { light.off(); }
}
// 调用者(遥控器)
public class RemoteControl {
private Command command;
public void setCommand(Command cmd) { this.command = cmd; }
public void pressButton() { command.execute(); }
public void pressUndo() { command.undo(); }
}
RemoteControl remote = new RemoteControl();
remote.setCommand(new LightOnCommand(new Light()));
remote.pressButton(); // 灯亮了
remote.pressUndo(); // 灯灭了核心好处:支持撤销、排队、日志记录(命令序列化后持久化)。
15. 解释器模式(Interpreter)
生活类比:乐谱——音乐家看到音符就能演奏,乐谱就是"语言",演奏就是"解释"。
意图:定义一种语言的语法,并建立一个解释器来解释该语言中的句子。
// 表达式接口
public interface Expression { int interpret(); }
public class NumberExpression implements Expression {
private int number;
public NumberExpression(int n) { this.number = n; }
public int interpret() { return number; }
}
public class AddExpression implements Expression {
private Expression left, right;
public AddExpression(Expression l, Expression r) { left = l; right = r; }
public int interpret() { return left.interpret() + right.interpret(); }
}
// 解释 "3 + 5"
Expression expr = new AddExpression(new NumberExpression(3), new NumberExpression(5));
System.out.println(expr.interpret()); // 8实际应用:Spring的SpEL表达式、MyBatis的动态SQL、正则表达式引擎。这个模式使用频率较低,适合简单语法解析场景。
16. 迭代器模式(Iterator)
生活类比:遥控器换台——你不需要知道电视内部怎么存频道的,按"下一个"就行。
意图:提供一种方法顺序访问聚合对象中的元素,而不暴露其内部表示。
// Java已经内置了:Iterable + Iterator
List<String> names = Arrays.asList("张三", "李四", "王五");
// for-each本质就是迭代器模式
for (String name : names) {
System.out.println(name);
}
// 展开后是这样:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}自定义迭代器示例:
public class MyList<T> implements Iterable<T> {
private Object[] data = new Object[10];
private int size = 0;
public void add(T item) { data[size++] = item; }
@Override
public Iterator<T> iterator() {
return new Iterator<T>() {
private int index = 0;
public boolean hasNext() { return index < size; }
@SuppressWarnings("unchecked")
public T next() { return (T) data[index++]; }
};
}
}17. 中介者模式(Mediator)
生活类比:机场塔台——飞机之间不直接通信,都通过塔台协调。
意图:用一个中介对象封装一系列对象的交互,使各对象不需要显式地相互引用。
// 中介者接口
public interface ChatMediator {
void sendMessage(String msg, User user);
void addUser(User user);
}
public class ChatRoom implements ChatMediator {
private List<User> users = new ArrayList<>();
public void addUser(User u) { users.add(u); }
public void sendMessage(String msg, User sender) {
users.stream()
.filter(u -> u != sender) // 不发给自己
.forEach(u -> u.receive(msg, sender.getName()));
}
}
public class User {
private String name;
private ChatMediator mediator;
public User(String name, ChatMediator m) { this.name = name; this.mediator = m; }
public String getName() { return name; }
public void send(String msg) { mediator.sendMessage(msg, this); }
public void receive(String msg, String from) {
System.out.println(name + " 收到 " + from + " 的消息: " + msg);
}
}
ChatRoom room = new ChatRoom();
User u1 = new User("张三", room);
User u2 = new User("李四", room);
room.addUser(u1); room.addUser(u2);
u1.send("大家好!"); // 李四 收到 张三 的消息: 大家好!Spring MVC中的DispatcherServlet就是中介者:协调Controller、ViewResolver、HandlerMapping等组件。
18. 备忘录模式(Memento)
生活类比:游戏存档——打Boss之前存个档,打输了读档重来。
意图:在不破坏封装的前提下,捕获并保存对象的内部状态,以便之后恢复。
// 备忘录(存档)
public class EditorMemento {
private final String content;
private final LocalDateTime saveTime;
public EditorMemento(String content) {
this.content = content;
this.saveTime = LocalDateTime.now();
}
public String getContent() { return content; }
}
// 发起人(编辑器)
public class Editor {
private String content = "";
public void write(String text) { this.content += text; }
public EditorMemento save() { return new EditorMemento(content); }
public void restore(EditorMemento m) { this.content = m.getContent(); }
public String getContent() { return content; }
}
// 管理者(版本历史)
public class History {
private Stack<EditorMemento> stack = new Stack<>();
public void push(EditorMemento m) { stack.push(m); }
public EditorMemento pop() { return stack.pop(); }
}
Editor editor = new Editor();
History history = new History();
editor.write("Hello ");
history.push(editor.save()); // 存档1
editor.write("World");
history.push(editor.save()); // 存档2
editor.write("!!!");
editor.restore(history.pop()); // 回退到存档2
System.out.println(editor.getContent()); // Hello World19. 观察者模式(Observer)
生活类比:微信公众号——你关注了某个号,有新文章就推送给你。
意图:定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖者都会收到通知。
// Java内置的(推荐自己实现,别用java.util.Observable,已废弃)
import java.util.List;
import java.util.ArrayList;
// 观察者接口
public interface Observer { void update(String event); }
// 被观察者(主题)
public class EventBus {
private List<Observer> observers = new ArrayList<>();
public void subscribe(Observer o) { observers.add(o); }
public void unsubscribe(Observer o) { observers.remove(o); }
public void publish(String event) {
observers.forEach(o -> o.update(event));
}
}
// 具体观察者
public class EmailNotifier implements Observer {
public void update(String event) { System.out.println("发邮件通知: " + event); }
}
public class SMSNotifier implements Observer {
public void update(String event) { System.out.println("发短信通知: " + event); }
}
EventBus bus = new EventBus();
bus.subscribe(new EmailNotifier());
bus.subscribe(new SMSNotifier());
bus.publish("订单已创建"); // 两个观察者都会收到Spring中的应用:ApplicationEvent + ApplicationListener + @EventListener。
20. 状态模式(State)
生活类比:自动售货机——投币前、投币后、出货中、售罄,不同状态按同一个按钮反应不同。
意图:允许对象在内部状态改变时改变其行为,看起来好像改变了它的类。
// 状态接口
public interface VendingState {
void insertCoin(VendingMachine machine);
void pressButton(VendingMachine machine);
}
public class NoCoinState implements VendingState {
public void insertCoin(VendingMachine m) {
System.out.println("投币成功");
m.setState(new HasCoinState());
}
public void pressButton(VendingMachine m) {
System.out.println("请先投币");
}
}
public class HasCoinState implements VendingState {
public void insertCoin(VendingMachine m) {
System.out.println("已经投过币了");
}
public void pressButton(VendingMachine m) {
System.out.println("出货中...");
m.setState(new NoCoinState());
}
}
// 上下文
public class VendingMachine {
private VendingState state = new NoCoinState();
public void setState(VendingState s) { this.state = s; }
public void insertCoin() { state.insertCoin(this); }
public void pressButton() { state.pressButton(this); }
}
VendingMachine vm = new VendingMachine();
vm.pressButton(); // 请先投币
vm.insertCoin(); // 投币成功
vm.pressButton(); // 出货中...状态模式 vs 策略模式:
状态模式:状态自动转换,调用者不感知
策略模式:调用者主动选择策略
21. 策略模式(Strategy)
生活类比:导航软件——去同一个地方可以选"最快路线"、"最短路线"、"不走高速"。
意图:定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。
// 策略接口
public interface PayStrategy {
void pay(double amount);
}
// 具体策略
public class AliPay implements PayStrategy {
public void pay(double amount) { System.out.println("支付宝支付: " + amount); }
}
public class WechatPay implements PayStrategy {
public void pay(double amount) { System.out.println("微信支付: " + amount); }
}
public class CreditCardPay implements PayStrategy {
public void pay(double amount) { System.out.println("信用卡支付: " + amount); }
}
// 上下文
public class Order {
private PayStrategy payStrategy;
public Order(PayStrategy strategy) { this.payStrategy = strategy; }
public void pay(double amount) { payStrategy.pay(amount); }
}
// 策略可自由切换
Order order = new Order(new AliPay());
order.pay(99.9);
order = new Order(new WechatPay());
order.pay(99.9);实际项目中的用法:策略 + 工厂 + 注解,消灭if-else:
// 定义策略标识
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface PayType { String value(); }
@PayType("alipay")
public class AliPay implements PayStrategy { /*...*/ }
// 策略工厂(Spring自动注入所有PayStrategy实现)
@Component
public class PayStrategyFactory {
private Map<String, PayStrategy> map = new HashMap<>();
@Autowired
public PayStrategyFactory(List<PayStrategy> strategies) {
strategies.forEach(s -> {
PayType annotation = s.getClass().getAnnotation(PayType.class);
map.put(annotation.value(), s);
});
}
public PayStrategy getStrategy(String type) { return map.get(type); }
}22. 模板方法模式(Template Method)
生活类比:炒菜的步骤——热锅→放油→炒菜→放盐→出锅。"炒菜"和"放盐"每道菜不一样,但整体流程一样。
意图:定义一个操作的算法骨架,把一些步骤延迟到子类。
// 抽象类(定义模板方法 + 钩子方法)
public abstract class DataExporter {
// 模板方法(final防止子类篡改流程)
public final void export() {
connect();
String data = queryData();
format(data);
disconnect();
}
protected void connect() { System.out.println("连接数据库"); }
protected abstract String queryData(); // 子类实现
protected abstract void format(String data); // 子类实现
protected void disconnect() { System.out.println("断开连接"); }
}
public class ExcelExporter extends DataExporter {
protected String queryData() { return "SELECT * FROM orders"; }
protected void format(String data) { System.out.println("导出为Excel: " + data); }
}
public class CsvExporter extends DataExporter {
protected String queryData() { return "SELECT * FROM users"; }
protected void format(String data) { System.out.println("导出为CSV: " + data); }
}
new ExcelExporter().export();
// 连接数据库 → SELECT * FROM orders → 导出为Excel → 断开连接Spring中的应用:JdbcTemplate、RestTemplate、AbstractApplicationContext.refresh()。
23. 访问者模式(Visitor)
生活类比:医院体检——你(元素)去体检,不同科室的医生(访问者)对你做不同的检查。
意图:在不修改元素类的前提下,定义作用于这些元素的新操作。
// 元素接口
public interface Shape {
void accept(ShapeVisitor visitor);
}
public class Circle implements Shape {
private double radius;
public Circle(double r) { this.radius = r; }
public double getRadius() { return radius; }
public void accept(ShapeVisitor v) { v.visit(this); }
}
public class Rectangle implements Shape {
private double width, height;
public Rectangle(double w, double h) { width = w; height = h; }
public double getWidth() { return width; }
public double getHeight() { return height; }
public void accept(ShapeVisitor v) { v.visit(this); }
}
// 访问者接口
public interface ShapeVisitor {
void visit(Circle c);
void visit(Rectangle r);
}
// 具体访问者:计算面积
public class AreaCalculator implements ShapeVisitor {
public void visit(Circle c) {
System.out.println("圆面积: " + Math.PI * c.getRadius() * c.getRadius());
}
public void visit(Rectangle r) {
System.out.println("矩形面积: " + r.getWidth() * r.getHeight());
}
}
// 具体访问者:计算周长
public class PerimeterCalculator implements ShapeVisitor {
public void visit(Circle c) {
System.out.println("圆周长: " + 2 * Math.PI * c.getRadius());
}
public void visit(Rectangle r) {
System.out.println("矩形周长: " + 2 * (r.getWidth() + r.getHeight()));
}
}
// 新增操作只需新建Visitor,不改Shape类
List<Shape> shapes = Arrays.asList(new Circle(5), new Rectangle(3, 4));
ShapeVisitor area = new AreaCalculator();
shapes.forEach(s -> s.accept(area));适用场景:元素类结构稳定、但经常需要新增操作的场景。缺点是增加新的元素类很麻烦。
六、23种模式速查表
七、总结
7.1 学习路线图
第1周:6大原则 + 单例 + 工厂方法 + 建造者 + 策略 + 观察者
第2周:代理 + 装饰器 + 适配器 + 外观 + 模板方法 + 责任链
第3周:组合 + 桥接 + 享元 + 原型 + 抽象工厂
第4周:命令 + 状态 + 迭代器 + 中介者 + 备忘录 + 访问者 + 解释器7.2 实际开发中最常用的Top 10
策略模式 —— 消灭if-else
模板方法 —— 固定流程抽取
观察者模式 —— 事件驱动
代理模式 —— AOP的基础
建造者模式 —— 复杂对象构建
工厂方法 —— 解耦对象创建
装饰器模式 —— 动态增强
责任链模式 —— 流水线处理
单例模式 —— 全局唯一
外观模式 —— 简化接口
7.3 最后的话
设计模式不是"用了就牛",而是"该用的时候知道用"。
初学时刻意练习,熟练后忘掉模式——就像学武术,先学招式,后悟心法,最终无招胜有招。
最好的代码不是"用了多少种设计模式",而是"恰到好处"。
本文所有代码基于Java 8+,如有问题欢迎指正。
评论