Java最常用设计模式:流程图+源码全解析(干货收藏版)
一、设计模式是什么?为什么要学?
设计模式是软件开发人员在长期实践中总结出的可复用解决方案模板,它并非具体的代码实现,而是一种经过千锤百炼的设计思想。学会设计模式,能让你的代码更优雅、更灵活、更易于维护。
设计模式根据目的可分为三大类:
· 创建型模式:关注对象创建过程,解耦对象创建与使用(单例、工厂方法、抽象工厂、建造者、原型)
· 结构型模式:处理类或对象间的组合关系,让系统更灵活可复用(适配器、装饰器、代理、外观、组合)
· 行为型模式:关注对象间的责任划分与交互方式(观察者、策略、模板方法、命令、责任链)
💡 六大设计原则(SOLID) :开闭原则、里氏替换原则、依赖倒置原则、单一职责原则、接口隔离原则、迪米特法则。它们是设计模式的“底层哲学”,理解这些原则才能真正用好设计模式。
二、创建型模式
2.1 单例模式(Singleton)
核心思想:确保一个类只有一个实例,并提供全局访问点。
适用场景:数据库连接池、配置管理器、日志记录器等需要全局唯一的资源。
源码实现(双重检查锁 – 推荐) :
public class LazySingleton {
// volatile 防止指令重排序,保证内存可见性
private static volatile LazySingleton instance;
// 私有构造方法,防止外部创建实例
private LazySingleton() {}
public static LazySingleton getInstance() {
// 第一次检查:避免不必要的同步
if (instance == null) {
synchronized (LazySingleton.class) {
// 第二次检查:确保只有一个线程创建实例
if (instance == null) {
instance = new LazySingleton();
}
}
}
return instance;
}
}
🔑 关键点:volatile关键字必不可少!new LazySingleton()在JVM中实际分三步执行(分配内存→初始化对象→引用指向内存),如果没有volatile,指令重排可能导致线程拿到未初始化完成的对象,引发空指针异常。
2.2 工厂方法模式(Factory Method)
核心思想:定义创建对象的接口,由子类决定实例化哪个类,将对象实例化延迟到子类中完成。
适用场景:产品族固定但具体类型需延迟确定、一个类不知道它要创建的对象的类。
源码实现:
// 1. 产品接口
public interface Product {
void doSomething();
}
// 2. 具体产品
public class ConcreteProductA implements Product {
@Override
public void doSomething() {
System.out.println("产品A的功能实现");
}
}
public class ConcreteProductB implements Product {
@Override
public void doSomething() {
System.out.println("产品B的功能实现");
}
}
// 3. 工厂接口
public interface Factory {
Product createProduct();
}
// 4. 具体工厂
public class ConcreteFactoryA implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductA();
}
}
public class ConcreteFactoryB implements Factory {
@Override
public Product createProduct() {
return new ConcreteProductB();
}
}
// 5. 客户端使用
public class Client {
public static void main(String[] args) {
Factory factory = new ConcreteFactoryA();
Product product = factory.createProduct();
product.doSomething(); // 输出:产品A的功能实现
}
}
三、结构型模式
3.1 适配器模式(Adapter)
核心思想:将一个类的接口转换成客户期望的另一个接口,使原本因接口不兼容而无法一起工作的类能够协同工作。
适用场景:老系统接口改造、第三方库接口适配、不同格式数据转换。
源码实现(对象适配器 – 推荐) :
// 1. 目标接口(客户端期望的)
public interface DC5 {
int output5V();
}
// 2. 适配者(需要被适配的类)
public class AC220 {
public int outputAC220V() {
System.out.println("提供220V交流电");
return 220;
}
}
// 3. 适配器(桥接目标接口与适配者)
public class PowerAdapter implements DC5 {
private AC220 ac220; // 持有适配者引用
public PowerAdapter(AC220 ac220) {
this.ac220 = ac220;
}
@Override
public int output5V() {
int input = ac220.outputAC220V();
System.out.println("将220V转换成5V");
return input / 44; // 转换逻辑:220 ÷ 44 = 5
}
}
// 4. 客户端使用
public class Client {
public static void main(String[] args) {
DC5 powerAdapter = new PowerAdapter(new AC220());
System.out.println("输出电压:" + powerAdapter.output5V() + "V");
}
}
📌 适配器 vs 装饰器:适配器关注“接口转换”,解决不兼容问题;装饰器关注“功能增强”,在不改变原有对象基础上动态添加职责。
四、行为型模式
4.1 策略模式(Strategy)
核心思想:定义一系列算法,将它们封装起来并使它们可以互相替换,算法的变化不影响使用算法的客户端。
适用场景:支付方式选择、优惠计算、排序算法、数据校验规则等需要动态切换算法的场景。
源码实现:
// 1. 策略接口
public interface PayStrategy {
void pay(double amount);
}
// 2. 具体策略
public class AlipayStrategy implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("使用支付宝支付:" + amount + "元");
// 调用支付宝支付API...
}
}
public class WechatPayStrategy implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("使用微信支付:" + amount + "元");
// 调用微信支付API...
}
}
public class CreditCardStrategy implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("使用信用卡支付:" + amount + "元");
// 调用信用卡支付API...
}
}
// 3. 上下文(持有策略引用)
public class PaymentContext {
private PayStrategy payStrategy;
public void setPayStrategy(PayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
public void executePay(double amount) {
if (payStrategy == null) {
throw new IllegalStateException("请先选择支付方式");
}
payStrategy.pay(amount);
}
}
// 4. 客户端使用
public class Client {
public static void main(String[] args) {
PaymentContext context = new PaymentContext();
// 支付宝支付
context.setPayStrategy(new AlipayStrategy());
context.executePay(99.9);
// 切换到微信支付
context.setPayStrategy(new WechatPayStrategy());
context.executePay(199.9);
}
}
🎯 策略模式 vs if-else:当系统中有大量if-else判断来选择不同算法时,策略模式是绝佳的替代方案,符合“开闭原则”——新增策略无需修改已有代码。
4.2 观察者模式(Observer)
核心思想:定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖它的对象都会自动收到通知并更新。
适用场景:事件监听、消息推送、订阅通知、订单状态变更等。
源码实现:
import java.util.ArrayList;
import java.util.List;
// 1. 观察者接口
public interface Observer {
void update(String message);
}
// 2. 主题(被观察者)
public class Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
notifyAllObservers(); // 状态变更时自动通知所有观察者
}
// 订阅
public void attach(Observer observer) {
observers.add(observer);
}
// 取消订阅
public void detach(Observer observer) {
observers.remove(observer);
}
// 通知所有观察者
private void notifyAllObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
}
// 3. 具体观察者
public class EmailObserver implements Observer {
@Override
public void update(String message) {
System.out.println("【邮件通知】状态已更新为:" + message);
// 实际项目中发送邮件...
}
}
public class SmsObserver implements Observer {
@Override
public void update(String message) {
System.out.println("【短信通知】状态已更新为:" + message);
// 实际项目中发送短信...
}
}
public class LogObserver implements Observer {
@Override
public void update(String message) {
System.out.println("【日志记录】状态变更为:" + message);
// 实际项目中记录日志...
}
}
// 4. 客户端使用
public class Client {
public static void main(String[] args) {
Subject subject = new Subject();
// 添加观察者
subject.attach(new EmailObserver());
subject.attach(new SmsObserver());
subject.attach(new LogObserver());
// 状态变更,所有观察者自动响应
subject.setState("订单已支付");
System.out.println("---");
subject.setState("订单已发货");
}
}
📱 Spring中的观察者模式:Spring的ApplicationEvent和ApplicationListener是观察者模式的优雅实现,通过事件驱动实现业务解耦。
4.3 模板方法模式(Template Method)
核心思想:在父类中定义一个算法的骨架,将一些具体步骤延迟到子类中实现。子类可以在不改变算法整体结构的前提下重新定义某些步骤。
适用场景:制作饮品(泡茶/冲咖啡)、数据读取、游戏流程等有固定步骤但细节可变的场景。
源码实现:
// 1. 抽象模板类
public abstract class DataParser {
// 模板方法:定义算法骨架,使用final防止子类修改
public final void parseData() {
openFile();
parseContent();
closeFile();
if (needNotify()) { // 钩子方法
notifyComplete();
}
}
// 具体方法:所有子类共享
private void openFile() {
System.out.println("打开文件");
}
// 抽象方法:由子类实现
protected abstract void parseContent();
// 具体方法
private void closeFile() {
System.out.println("关闭文件");
}
// 钩子方法:子类可选择重写
protected boolean needNotify() {
return false; // 默认不通知
}
private void notifyComplete() {
System.out.println("解析完成通知");
}
}
// 2. 具体子类 - XML解析器
public class XmlParser extends DataParser {
@Override
protected void parseContent() {
System.out.println("解析XML格式内容...");
}
@Override
protected boolean needNotify() {
return true; // XML解析完成后需要通知
}
}
// 3. 具体子类 - CSV解析器
public class CsvParser extends DataParser {
@Override
protected void parseContent() {
System.out.println("解析CSV格式内容...");
}
}
// 4. 客户端使用
public class Client {
public static void main(String[] args) {
DataParser xmlParser = new XmlParser();
xmlParser.parseData();
System.out.println("---");
DataParser csvParser = new CsvParser();
csvParser.parseData();
}
}
🌟 框架中的模板方法:Spring的JdbcTemplate、HibernateTemplate都是模板方法模式的经典应用——把重复的流程(打开连接、执行SQL、关闭连接)封装在模板中,把具体业务逻辑留给开发者实现。
五、总结与建议
本文介绍的7种设计模式各有侧重,没有“最好”,只有“最适合”。下面是根据实践经验整理的选型指南:
模式 类别 核心价值 推荐场景
单例模式 创建型 确保全局唯一 配置管理、连接池、日志器
工厂方法 创建型 解耦对象创建 产品族固定、类型延迟确定
适配器模式 结构型 接口兼容 系统集成、老代码改造
策略模式 行为型 算法动态切换 支付方式、排序算法
观察者模式 行为型 事件驱动解耦 消息推送、状态监听
模板方法 行为型 复用算法骨架 工作流、数据处理框架
🚀 学习建议:先理解每个模式的核心思想,再结合项目中的实际场景尝试应用。记住——过度设计比没有设计更糟糕,在简单场景中强行套用设计模式反而会增加代码复杂度。从单例、策略、观察者这3个高频模式入手,慢慢扩展到其他模式,是最稳妥的学习路径。
如果觉得这篇文章对你有帮助,欢迎点赞、转发、收藏!你的支持是我持续创作的最大动力。
夜雨聆风