一文搞懂常用设计模式原理(附Java实现)
设计模式是设计原则的“落地形态”,是前人总结的、解决特定场景问题的通用代码结构。掌握常用设计模式,能让你用成熟的方案解决开发中的高频问题,写出更优雅、易维护的代码。
本文聚焦23种设计模式中最常用的10种,按“创建型、结构型、行为型”三大类划分,每类讲解核心原理、适用场景、Java实现代码,全程贴合实际开发,新手也能快速上手。
一、设计模式总览:分类与核心目标
设计模式按“解决的问题类型”分为三类,核心目标都是解耦、复用、扩展:
| 类型 | 核心目标 | 常用模式 |
|---|---|---|
| 创建型 | 封装对象的创建逻辑 | 单例、工厂方法、抽象工厂、建造者、原型 |
| 结构型 | 优化类/对象的组合结构 | 代理、装饰器、适配器、桥接、组合、享元 |
| 行为型 | 优化对象间的交互/职责分配 | 观察者、策略、模板方法、迭代器、命令 |
二、高频设计模式详解(原理+Java实现)
(一)创建型模式:搞定对象创建
1. 单例模式(Singleton)
- 原理:保证一个类在程序中只有一个实例,并提供全局访问点。
- 适用场景:配置管理、连接池、日志对象等(避免重复创建消耗资源)。
- 核心实现:私有构造器 + 静态方法/静态变量控制实例创建。
- Java实现(懒汉式-线程安全版,开发常用):
/**
* 懒汉式单例:按需创建,加锁保证线程安全
* 优点:节省内存,线程安全;缺点:加锁有轻微性能损耗
*/
public class Singleton {
// volatile保证多线程下可见性,防止指令重排
private static volatile Singleton instance;
// 私有构造器:禁止外部new
private Singleton() {}
// 双重检查锁:减少锁竞争,提升性能
public static Singleton getInstance() {
if (instance == null) { // 第一层检查:避免每次加锁
synchronized (Singleton.class) { // 加锁保证线程安全
if (instance == null) { // 第二层检查:防止多线程同时进入第一层
instance = new Singleton();
}
}
}
return instance;
}
// 测试方法
public void doSomething() {
System.out.println("单例对象执行操作");
}
}
// 调用示例
class SingletonTest {
public static void main(String[] args) {
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // true,只有一个实例
s1.doSomething();
}
}
2. 工厂方法模式(Factory Method)
- 原理:定义创建对象的接口,让子类决定创建哪种对象,将对象创建延迟到子类。
- 适用场景:产品类型较多,且需要扩展新类型(符合开闭原则)。
- 核心角色:抽象工厂(创建接口)、具体工厂(实现创建逻辑)、抽象产品、具体产品。
- Java实现(支付场景):
// 1. 抽象产品:支付接口
public interface Pay {
void pay(double amount);
}
// 2. 具体产品:支付宝支付
public class Alipay implements Pay {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount + "元");
}
}
// 3. 具体产品:微信支付
public class WechatPay implements Pay {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount + "元");
}
}
// 4. 抽象工厂:支付工厂接口
public interface PayFactory {
Pay createPay();
}
// 5. 具体工厂:支付宝工厂
public class AlipayFactory implements PayFactory {
@Override
public Pay createPay() {
return new Alipay();
}
}
// 6. 具体工厂:微信支付工厂
public class WechatPayFactory implements PayFactory {
@Override
public Pay createPay() {
return new WechatPay();
}
}
// 调用示例
class FactoryMethodTest {
public static void main(String[] args) {
// 创建支付宝支付
PayFactory alipayFactory = new AlipayFactory();
Pay alipay = alipayFactory.createPay();
alipay.pay(100);
// 创建微信支付(新增类型无需修改原有代码,符合开闭原则)
PayFactory wechatFactory = new WechatPayFactory();
Pay wechat = wechatFactory.createPay();
wechat.pay(200);
}
}
3. 建造者模式(Builder)
- 原理:将复杂对象的构建与表示分离,分步构建对象,且构建过程可复用。
- 适用场景:对象属性多、可选参数多(如订单、用户、汽车对象)。
- Java实现(用户对象构建):
/**
* 复杂对象:用户(含必填+可选属性)
*/
public class User {
// 必填属性
private final String username;
private final String password;
// 可选属性
private final String phone;
private final String email;
private final int age;
// 私有构造器:仅Builder能调用
private User(Builder builder) {
this.username = builder.username;
this.password = builder.password;
this.phone = builder.phone;
this.email = builder.email;
this.age = builder.age;
}
// 静态内部建造者类
public static class Builder {
// 必填属性(无默认值)
private final String username;
private final String password;
// 可选属性(默认值)
private String phone = "";
private String email = "";
private int age = 0;
// Builder构造器:强制传入必填属性
public Builder(String username, String password) {
this.username = username;
this.password = password;
}
// 链式设置可选属性
public Builder phone(String phone) {
this.phone = phone;
return this;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder age(int age) {
this.age = age;
return this;
}
// 构建最终对象
public User build() {
return new User(this);
}
}
// 重写toString方便测试
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", phone='" + phone + '\'' +
", email='" + email + '\'' +
", age=" + age +
'}';
}
}
// 调用示例
class BuilderTest {
public static void main(String[] args) {
// 分步构建用户:必填+部分可选
User user = new User.Builder("zhangsan", "123456")
.phone("13800138000")
.age(25)
.build();
System.out.println(user);
// 输出:User{username='zhangsan', phone='13800138000', email='', age=25}
}
}
(二)结构型模式:优化对象组合
1. 代理模式(Proxy)
- 原理:为目标对象提供一个代理对象,控制对目标对象的访问,可在访问前后增加额外逻辑。
- 适用场景:权限控制、日志记录、缓存、远程调用(如RPC)。
- 核心分类:静态代理(编译期确定)、动态代理(运行时生成)。
- Java实现(静态代理-日志增强):
// 1. 抽象主题:业务接口
public interface OrderService {
void createOrder(String orderId);
}
// 2. 真实主题:目标业务类
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder(String orderId) {
System.out.println("创建订单:" + orderId);
}
}
// 3. 代理类:增强目标对象逻辑
public class OrderServiceProxy implements OrderService {
// 持有目标对象
private final OrderService target;
public OrderServiceProxy(OrderService target) {
this.target = target;
}
@Override
public void createOrder(String orderId) {
// 前置增强:日志记录
System.out.println("【日志】创建订单开始,订单ID:" + orderId);
// 调用目标对象方法
target.createOrder(orderId);
// 后置增强:日志记录
System.out.println("【日志】创建订单结束,订单ID:" + orderId);
}
}
// 调用示例
class ProxyTest {
public static void main(String[] args) {
// 目标对象
OrderService target = new OrderServiceImpl();
// 代理对象
OrderService proxy = new OrderServiceProxy(target);
// 通过代理调用方法(自动增强)
proxy.createOrder("001");
}
}
2. 装饰器模式(Decorator)
- 原理:动态给对象添加额外职责,不改变原有类结构,比继承更灵活。
- 适用场景:给对象动态增加功能,且功能可组合(如IO流、咖啡加配料)。
- 核心区别(与代理):装饰器关注增强对象功能,代理关注控制对象访问。
- Java实现(咖啡加配料):
// 1. 抽象组件:咖啡
public interface Coffee {
double cost(); // 计算价格
String getName(); // 获取名称
}
// 2. 具体组件:基础咖啡(美式咖啡)
public class Americano implements Coffee {
@Override
public double cost() {
return 10; // 基础价格10元
}
@Override
public String getName() {
return "美式咖啡";
}
}
// 3. 抽象装饰器:配料
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee; // 持有被装饰的咖啡对象
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}
// 4. 具体装饰器:牛奶
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return coffee.cost() + 2; // 基础价格+牛奶2元
}
@Override
public String getName() {
return coffee.getName() + "+牛奶";
}
}
// 5. 具体装饰器:糖
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return coffee.cost() + 1; // 基础价格+糖1元
}
@Override
public String getName() {
return coffee.getName() + "+糖";
}
}
// 调用示例
class DecoratorTest {
public static void main(String[] args) {
// 基础咖啡
Coffee coffee = new Americano();
System.out.println(coffee.getName() + ":" + coffee.cost() + "元"); // 美式咖啡:10.0元
// 加牛奶
coffee = new MilkDecorator(coffee);
System.out.println(coffee.getName() + ":" + coffee.cost() + "元"); // 美式咖啡+牛奶:12.0元
// 再加糖(组合增强)
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getName() + ":" + coffee.cost() + "元"); // 美式咖啡+牛奶+糖:13.0元
}
}
3. 适配器模式(Adapter)
- 原理:将一个类的接口转换成客户端期望的另一个接口,解决接口不兼容问题。
- 适用场景:集成第三方组件、复用已有类但接口不匹配(如老系统对接新接口)。
- 核心角色:目标接口(客户端期望)、适配者(原有接口)、适配器(转换接口)。
- Java实现(老支付接口适配新接口):
// 1. 目标接口:新系统期望的支付接口
public interface NewPayService {
void unifiedPay(double amount); // 统一支付方法
}
// 2. 适配者:老系统的支付接口(无法修改)
public class OldAlipayService {
// 老接口方法名/参数与新接口不一致
public void alipayPay(double money) {
System.out.println("老支付宝接口支付:" + money + "元");
}
}
// 3. 适配器:将老接口适配为新接口
public class PayAdapter implements NewPayService {
// 持有老接口对象
private OldAlipayService oldAlipayService;
public PayAdapter(OldAlipayService oldAlipayService) {
this.oldAlipayService = oldAlipayService;
}
@Override
public void unifiedPay(double amount) {
// 适配转换:调用老接口
oldAlipayService.alipayPay(amount);
}
}
// 调用示例
class AdapterTest {
public static void main(String[] args) {
// 老接口对象
OldAlipayService oldService = new OldAlipayService();
// 适配为新接口
NewPayService newService = new PayAdapter(oldService);
// 客户端按新接口调用(无需关心老接口细节)
newService.unifiedPay(100);
}
}
(三)行为型模式:优化对象交互
1. 观察者模式(Observer)
- 原理:定义对象间的一对多依赖,当一个对象(主题)状态变化时,所有依赖它的对象(观察者)自动收到通知并更新。
- 适用场景:消息通知、事件监听、发布订阅(如订单状态变更通知物流、库存)。
- Java实现(订单状态通知):
import java.util.ArrayList;
import java.util.List;
// 1. 抽象主题:订单主题
public interface OrderSubject {
// 注册观察者
void registerObserver(OrderObserver observer);
// 移除观察者
void removeObserver(OrderObserver observer);
// 通知所有观察者
void notifyObservers(String orderId, String status);
}
// 2. 具体主题:订单服务(被观察对象)
public class OrderService implements OrderSubject {
// 观察者列表
private List<OrderObserver> observers = new ArrayList<>();
@Override
public void registerObserver(OrderObserver observer) {
observers.add(observer);
}
@Override
public void removeObserver(OrderObserver observer) {
observers.remove(observer);
}
@Override
public void notifyObservers(String orderId, String status) {
// 遍历通知所有观察者
for (OrderObserver observer : observers) {
observer.update(orderId, status);
}
}
// 订单状态变更方法
public void updateOrderStatus(String orderId, String status) {
System.out.println("订单" + orderId + "状态变更为:" + status);
// 通知观察者
notifyObservers(orderId, status);
}
}
// 3. 抽象观察者
public interface OrderObserver {
void update(String orderId, String status);
}
// 4. 具体观察者1:物流系统
public class LogisticsObserver implements OrderObserver {
@Override
public void update(String orderId, String status) {
if ("已支付".equals(status)) {
System.out.println("物流系统:订单" + orderId + "已支付,开始备货发货");
}
}
}
// 5. 具体观察者2:库存系统
public class StockObserver implements OrderObserver {
@Override
public void update(String orderId, String status) {
if ("已支付".equals(status)) {
System.out.println("库存系统:订单" + orderId + "已支付,扣减库存");
}
}
}
// 调用示例
class ObserverTest {
public static void main(String[] args) {
// 创建主题(订单服务)
OrderService orderService = new OrderService();
// 注册观察者
orderService.registerObserver(new LogisticsObserver());
orderService.registerObserver(new StockObserver());
// 变更订单状态(自动通知观察者)
orderService.updateOrderStatus("001", "已支付");
}
}
2. 策略模式(Strategy)
- 原理:定义一系列算法,将每个算法封装成独立类,算法可互相替换,且算法变化不影响使用算法的客户端。
- 适用场景:多种算法/规则可选(如排序算法、支付方式、优惠策略)。
- Java实现(订单优惠策略):
// 1. 抽象策略:优惠接口
public interface DiscountStrategy {
double calculate(double amount); // 计算优惠后金额
}
// 2. 具体策略1:满减优惠(满100减20)
public class FullReduceStrategy implements DiscountStrategy {
@Override
public double calculate(double amount) {
if (amount >= 100) {
return amount - 20;
}
return amount;
}
}
// 3. 具体策略2:折扣优惠(9折)
public class DiscountStrategyImpl implements DiscountStrategy {
@Override
public double calculate(double amount) {
return amount * 0.9;
}
}
// 4. 上下文:订单(持有策略,调用策略)
public class OrderContext {
private DiscountStrategy strategy;
// 动态设置策略
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
// 计算最终金额
public double calculateFinalAmount(double amount) {
return strategy.calculate(amount);
}
}
// 调用示例
class StrategyTest {
public static void main(String[] args) {
OrderContext order = new OrderContext();
double originalAmount = 100;
// 使用满减策略
order.setStrategy(new FullReduceStrategy());
System.out.println("满减后金额:" + order.calculateFinalAmount(originalAmount)); // 80.0
// 切换为折扣策略(无需修改OrderContext)
order.setStrategy(new DiscountStrategyImpl());
System.out.println("折扣后金额:" + order.calculateFinalAmount(originalAmount)); // 90.0
}
}
3. 模板方法模式(Template Method)
- 原理:定义一个算法的骨架,将算法中可变的步骤延迟到子类实现,固定不变的步骤在父类中实现。
- 适用场景:多个子类有通用流程,仅部分步骤不同(如登录流程、支付流程)。
- Java实现(登录流程):
// 1. 抽象父类:定义登录流程骨架
public abstract class AbstractLoginTemplate {
// 模板方法:固定登录流程(final防止子类重写)
public final void login(String username, String password) {
// 步骤1:验证参数(固定)
if (!checkParams(username, password)) {
System.out.println("参数为空,登录失败");
return;
}
// 步骤2:验证账号密码(可变,子类实现)
if (verify(username, password)) {
// 步骤3:登录成功处理(固定)
success();
} else {
// 步骤4:登录失败处理(固定)
fail();
}
}
// 固定步骤:验证参数
private boolean checkParams(String username, String password) {
return username != null && !username.isEmpty()
&& password != null && !password.isEmpty();
}
// 可变步骤:验证账号密码(抽象方法,子类实现)
protected abstract boolean verify(String username, String password);
// 固定步骤:登录成功
private void success() {
System.out.println("登录成功,跳转到首页");
}
// 固定步骤:登录失败
private void fail() {
System.out.println("账号或密码错误,登录失败");
}
}
// 2. 具体子类:密码登录
public class PasswordLogin extends AbstractLoginTemplate {
@Override
protected boolean verify(String username, String password) {
// 模拟数据库验证
return "admin".equals(username) && "123456".equals(password);
}
}
// 3. 具体子类:短信验证码登录
public class SmsLogin extends AbstractLoginTemplate {
@Override
protected boolean verify(String username, String password) {
// 模拟短信验证码验证
return "admin".equals(username) && "666666".equals(password);
}
}
// 调用示例
class TemplateMethodTest {
public static void main(String[] args) {
// 密码登录
AbstractLoginTemplate passwordLogin = new PasswordLogin();
passwordLogin.login("admin", "123456"); // 登录成功
// 短信登录
AbstractLoginTemplate smsLogin = new SmsLogin();
smsLogin.login("admin", "666666"); // 登录成功
}
}
三、设计模式使用技巧与避坑
1. 核心使用原则
- 按需选择:不要为了用模式而用模式,简单场景直接写代码(如只有一个产品的工厂模式无意义);
- 结合设计原则:所有模式都遵循SOLID原则(如策略模式符合开闭原则,单例模式符合单一职责);
- 优先组合:设计模式中大量使用组合/聚合(如装饰器、观察者),而非继承。
2. 常见坑点
- 过度设计:用简单代码能解决的问题,不要强行套模式(如小项目用抽象工厂);
- 模式混淆:
- 代理vs装饰器:代理控制访问,装饰器增强功能;
- 适配器vs装饰器:适配器解决接口不兼容,装饰器增强功能;
- 忽略性能:如单例模式的懒汉式加锁、装饰器多层嵌套可能影响性能。
四、总结
核心要点回顾
- 三大分类核心:创建型封装对象创建,结构型优化对象组合,行为型优化对象交互;
- 高频模式重点:单例(唯一实例)、工厂方法(延迟创建)、代理(控制访问)、观察者(发布订阅)、策略(算法替换)是开发中最常用的5种模式;
- 使用核心:设计模式是“工具”,不是“规则”,结合场景选择,优先保证代码简洁、易维护。
落地建议
- 新手先掌握上述10种模式,再逐步学习抽象工厂、桥接、命令等进阶模式;
- 阅读JDK源码(如
java.util.Observer、java.io包),理解设计模式的实际应用; - 项目中重构时,用设计模式优化“臃肿、难扩展”的代码(如用策略模式替代大量if-else)。
如果需要某一种模式的进阶用法(如动态代理、抽象工厂)或源码分析(如Spring中的设计模式),可以告诉我,我会补充对应的代码和解析。
百流积聚,江河是也;文若化风,可以砾石。

浙公网安备 33010602011771号