一、先明确:设计模式的核心原则(SOLID)
学习设计模式前,先掌握这 5 个基本原则,所有设计模式都是围绕这些原则设计的:
- 单一职责(SRP):一个类只做一件事,修改原因只有一个。
- 开闭原则(OCP):对扩展开放,对修改关闭(新增功能靠扩展,而非改原有代码)。
- 里氏替换(LSP):子类可以替换父类,且不影响程序运行。
- 接口隔离(ISP):不要强迫客户端依赖它不需要的接口(拆分大接口为小接口)。
- 依赖倒置(DIP):依赖抽象,而非具体实现(面向接口编程)。
二、创建型模式(5 种:解决 "对象创建" 问题)
创建型模式的核心是封装对象的创建过程,降低创建逻辑和业务逻辑的耦合。
1. 单例模式(Singleton)
-
核心思想:保证一个类在程序中只有一个实例,且提供全局访问点。
-
适用场景:
- 资源密集型对象(数据库连接池、线程池、Redis 客户端),避免重复创建消耗资源;
- 全局配置类、日志工具类、缓存管理器。
-
常见实现方式对比:
|------------|-------------|-------------------|------|-----|
| 实现方式 | 优点 | 缺点 | 线程安全 | 懒加载 |
| 饿汉式 | 简单、无线程安全问题 | 类加载时就创建,可能浪费内存 | 是 | 否 |
| 懒汉式(双重检查锁) | 懒加载、性能优 | 代码稍复杂(需 volatile) | 是 | 是 |
| 静态内部类 | 懒加载、线程安全、简洁 | 无法传参 | 是 | 是 |
| 枚举 | 绝对线程安全、防反射 | 灵活性低(无法懒加载) | 是 | 否 |
代码示例(静态内部类,推荐):
java
public class ConfigManager {
// 私有构造方法,禁止外部实例化
private ConfigManager() {}
// 静态内部类,类加载时不会初始化,实现懒加载
private static class SingletonHolder {
private static final ConfigManager INSTANCE = new ConfigManager();
}
// 全局访问点
public static ConfigManager getInstance() {
return SingletonHolder.INSTANCE;
}
// 示例方法:获取配置
public String getConfig(String key) {
return "配置值:" + key;
}
}
// 测试
public class Test {
public static void main(String[] args) {
ConfigManager cm1 = ConfigManager.getInstance();
ConfigManager cm2 = ConfigManager.getInstance();
System.out.println(cm1 == cm2); // 输出:true(同一实例)
}
}
- 注意事项:单例模式会导致类的耦合度升高,且不利于单元测试(无法 mock),避免滥用。
2. 工厂模式(Factory)
分为简单工厂 、工厂方法 、抽象工厂,逐层抽象:
(1)简单工厂(不属于 GoF,但最常用)
- 核心:一个工厂类根据参数创建不同的产品实例,隐藏创建细节。
- 场景:产品类型少、创建逻辑简单(如创建不同类型的日志器:FileLogger、ConsoleLogger)。
(2)工厂方法(核心)
- 核心:定义创建对象的接口,让子类决定实例化哪个类(将创建逻辑延迟到子类)。
- 场景:产品类型可能扩展(如电商系统的支付方式:支付宝、微信支付、银行卡支付,后续可能加银联支付)。
- 代码示例:
java
// 抽象产品:支付接口
public interface Payment {
void pay(double amount);
}
// 具体产品1:支付宝支付
public class AliPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount + "元");
}
}
// 具体产品2:微信支付
public class WeChatPay implements Payment {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount + "元");
}
}
// 抽象工厂:支付工厂接口
public interface PaymentFactory {
Payment createPayment();
}
// 具体工厂1:支付宝工厂
public class AliPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new AliPay();
}
}
// 具体工厂2:微信支付工厂
public class WeChatPayFactory implements PaymentFactory {
@Override
public Payment createPayment() {
return new WeChatPay();
}
}
// 测试
public class Test {
public static void main(String[] args) {
// 选择支付宝支付
PaymentFactory factory = new AliPayFactory();
Payment payment = factory.createPayment();
payment.pay(100); // 输出:支付宝支付:100.0元
// 扩展:新增银联支付,只需加UnionPay类和UnionPayFactory类,无需改原有代码(符合开闭原则)
}
}
- 优点:符合开闭原则,扩展新产品只需加新工厂和产品类;
- 缺点:产品和工厂成对增加,类数量增多。
(3)抽象工厂
- 核心:创建一系列相关 / 依赖的产品族(而非单个产品),如 "小米工厂" 生产小米手机、小米电视;"华为工厂" 生产华为手机、华为电视。
- 场景:产品有明确的 "族" 划分,且族内产品需配套使用(如 GUI 框架:Windows 风格的按钮、文本框;Mac 风格的按钮、文本框)。
3. 建造者模式(Builder)
4. 原型模式(Prototype)
三、结构型模式(7 种:解决 "类 / 对象组合" 问题)
核心是通过合理的类 / 对象组合,优化结构,提高复用性和扩展性。
1. 适配器模式(Adapter)
-
核心:将复杂对象的构建与表示分离,一步一步构建复杂对象(如组装电脑:CPU、内存、硬盘分步选)。
-
场景:对象属性多、可选参数多(如订单对象、用户对象、HTTP 请求对象)。
-
代码示例(链式调用,Java 常用):
java// 复杂对象:电脑 public class Computer { // 必选参数 private String cpu; private String memory; // 可选参数 private String hardDisk; private String graphicsCard; // 私有构造,只能通过Builder创建 private Computer(Builder builder) { this.cpu = builder.cpu; this.memory = builder.memory; this.hardDisk = builder.hardDisk; this.graphicsCard = builder.graphicsCard; } // 建造者类 public static class Builder { // 必选参数(构造方法强制传入) private String cpu; private String memory; // 可选参数 private String hardDisk; private String graphicsCard; public Builder(String cpu, String memory) { this.cpu = cpu; this.memory = memory; } // 链式设置可选参数 public Builder hardDisk(String hardDisk) { this.hardDisk = hardDisk; return this; } public Builder graphicsCard(String graphicsCard) { this.graphicsCard = graphicsCard; return this; } // 构建对象 public Computer build() { return new Computer(this); } } @Override public String toString() { return "Computer{" + "cpu='" + cpu + '\'' + ", memory='" + memory + '\'' + ", hardDisk='" + hardDisk + '\'' + ", graphicsCard='" + graphicsCard + '\'' + '}'; } } // 测试 public class Test { public static void main(String[] args) { // 分步构建:必选参数+可选参数 Computer computer = new Computer.Builder("i7-13700K", "16GB") .hardDisk("1TB SSD") .graphicsCard("RTX 4070") .build(); System.out.println(computer); // 输出:Computer{cpu='i7-13700K', memory='16GB', hardDisk='1TB SSD', graphicsCard='RTX 4070'} } } -
优点:参数清晰,避免构造方法参数过多;可灵活构建不同配置的对象;
-
缺点:代码量增加,适合复杂对象。
-
核心:通过复制(克隆)已有对象创建新对象,避免重复初始化(如从一个基础用户对象克隆出多个不同属性的用户)。
-
场景:创建对象成本高(如对象需读取数据库 / 网络数据)、需批量创建相似对象。
-
关键:实现
Cloneable接口,重写clone()方法(浅克隆 / 深克隆)。 -
核心:将一个类的接口转换成客户端期望的另一个接口,让不兼容的类可以一起工作(如充电器转接头)。
-
场景:
- 集成第三方库,但其接口与现有系统不兼容;
- 重构老代码,不想修改原有逻辑,只需适配新接口。
-
分类:类适配器(继承)、对象适配器(组合,推荐)。
-
代码示例(对象适配器):
java
// 现有系统期望的接口:支付回调
public interface PaymentCallback {
void onSuccess(String orderId);
}
// 第三方支付的接口(不兼容)
public class ThirdPayCallback {
// 第三方的回调方法名和参数不同
public void callback(String orderNo, int status) {
if (status == 1) {
System.out.println("第三方回调:订单" + orderNo + "支付成功");
}
}
}
// 适配器:将第三方接口适配成系统期望的接口
public class PayCallbackAdapter implements PaymentCallback {
// 组合第三方对象
private ThirdPayCallback thirdPayCallback;
public PayCallbackAdapter(ThirdPayCallback thirdPayCallback) {
this.thirdPayCallback = thirdPayCallback;
}
@Override
public void onSuccess(String orderId) {
// 适配参数,调用第三方方法
thirdPayCallback.callback(orderId, 1);
}
}
// 测试
public class Test {
public static void main(String[] args) {
// 客户端只需调用统一的PaymentCallback接口,无需关心第三方实现
PaymentCallback callback = new PayCallbackAdapter(new ThirdPayCallback());
callback.onSuccess("ORDER_123456");
// 输出:第三方回调:订单ORDER_123456支付成功
}
}
2. 装饰器模式(Decorator)
- 核心:动态地给对象添加额外功能,且不改变其原有结构(如给咖啡加奶、加糖,基础咖啡 + 装饰器 = 不同口味)。
- 场景:需灵活扩展对象功能,且不想通过继承实现(继承会导致类爆炸)。
- 对比继承:继承是 "静态" 扩展(编译期确定),装饰器是 "动态" 扩展(运行期可选)。
- 代码示例:
java
// 抽象组件:咖啡
public interface Coffee {
double cost(); // 价格
String getDesc(); // 描述
}
// 具体组件:基础咖啡(黑咖啡)
public class BlackCoffee implements Coffee {
@Override
public double cost() {
return 10; // 基础价格10元
}
@Override
public String getDesc() {
return "黑咖啡";
}
}
// 抽象装饰器:继承Coffee,组合Coffee
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
}
// 具体装饰器1:加奶
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return coffee.cost() + 2; // 奶加2元
}
@Override
public String getDesc() {
return coffee.getDesc() + "+牛奶";
}
}
// 具体装饰器2:加糖
public class SugarDecorator extends CoffeeDecorator {
public SugarDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return coffee.cost() + 1; // 糖加1元
}
@Override
public String getDesc() {
return coffee.getDesc() + "+糖";
}
}
// 测试
public class Test {
public static void main(String[] args) {
// 动态组合:黑咖啡+奶+糖
Coffee coffee = new BlackCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
System.out.println(coffee.getDesc()); // 输出:黑咖啡+牛奶+糖
System.out.println(coffee.cost()); // 输出:13.0
}
}
3. 代理模式(Proxy)
- 核心:为另一个对象提供一个替身,控制对它的访问(如中介、经纪人)。
- 分类:
- 静态代理:手动编写代理类;
- 动态代理:运行时生成代理类(JDK 动态代理、CGLIB);
- 场景:权限控制、日志记录、缓存、远程调用(RPC)、懒加载。
- 代码示例(JDK 动态代理,日志记录):
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 业务接口
public interface UserService {
void addUser(String username);
}
// 真实业务类
public class UserServiceImpl implements UserService {
@Override
public void addUser(String username) {
System.out.println("添加用户:" + username);
}
}
// 动态代理处理器:添加日志功能
public class LogProxyHandler implements InvocationHandler {
// 目标对象
private Object target;
public LogProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 前置增强:记录日志
System.out.println("日志:调用" + method.getName() + "方法,参数:" + args[0]);
// 调用目标方法
Object result = method.invoke(target, args);
// 后置增强:可添加其他逻辑
System.out.println("日志:" + method.getName() + "方法调用完成");
return result;
}
}
// 测试
public class Test {
public static void main(String[] args) {
// 真实对象
UserService userService = new UserServiceImpl();
// 创建代理对象
UserService proxy = (UserService) Proxy.newProxyInstance(
userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
new LogProxyHandler(userService)
);
// 调用代理方法
proxy.addUser("张三");
// 输出:
// 日志:调用addUser方法,参数:张三
// 添加用户:张三
// 日志:addUser方法调用完成
}
}
四、行为型模式(11 种:解决 "对象交互" 问题)
核心是规范对象之间的通信方式,降低耦合,提高交互的灵活性。
1. 策略模式(Strategy)
- 核心:定义一系列算法,将每个算法封装起来,使它们可互相替换(如排序算法:冒泡、快排、归并,可按需切换)。
- 场景:多个算法解决同一类问题,需动态切换(如电商的优惠策略:满减、折扣、优惠券)。
- 代码示例:
java
// 抽象策略:优惠接口
public interface DiscountStrategy {
double calculate(double amount); // 计算优惠后价格
}
// 具体策略1:满减(满100减20)
public class FullReduceStrategy implements DiscountStrategy {
@Override
public double calculate(double amount) {
return amount >= 100 ? amount - 20 : amount;
}
}
// 具体策略2:折扣(8折)
public class DiscountStrategyImpl implements DiscountStrategy {
@Override
public double calculate(double amount) {
return amount * 0.8;
}
}
// 上下文:订单(持有策略,调用策略)
public class Order {
private DiscountStrategy strategy;
// 动态设置策略
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
// 计算最终价格
public double getFinalPrice(double amount) {
return strategy.calculate(amount);
}
}
// 测试
public class Test {
public static void main(String[] args) {
Order order = new Order();
double originalAmount = 100;
// 选择满减策略
order.setStrategy(new FullReduceStrategy());
System.out.println("满减后价格:" + order.getFinalPrice(originalAmount)); // 输出:80.0
// 切换为折扣策略
order.setStrategy(new DiscountStrategyImpl());
System.out.println("折扣后价格:" + order.getFinalPrice(originalAmount)); // 输出:80.0
}
}
2. 观察者模式(Observer)
- 核心:定义对象间的一对多依赖,当一个对象(被观察者)状态变化时,所有依赖它的对象(观察者)都会收到通知并自动更新(如公众号推送、事件监听)。
- 场景:消息通知、事件驱动、实时数据更新(如股票价格变动通知)。
- 代码示例(Java 内置 Observer/Observable):
java
import java.util.Observable;
import java.util.Observer;
// 被观察者:公众号
public class PublicAccount extends Observable {
private String title; // 文章标题
public void publishArticle(String title) {
this.title = title;
// 标记状态变化
setChanged();
// 通知所有观察者
notifyObservers(title);
}
}
// 观察者:用户
public class User implements Observer {
private String name;
public User(String name) {
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
System.out.println(name + "收到公众号推送:" + arg);
}
}
// 测试
public class Test {
public static void main(String[] args) {
// 创建公众号
PublicAccount account = new PublicAccount();
// 添加观察者(订阅)
account.addObserver(new User("张三"));
account.addObserver(new User("李四"));
// 发布文章(触发通知)
account.publishArticle("Java设计模式详解");
// 输出:
// 李四收到公众号推送:Java设计模式详解
// 张三收到公众号推送:Java设计模式详解
}
}
3. 模板方法模式(Template Method)
- 核心:定义一个算法的骨架,将算法的可变步骤延迟到子类实现(如泡茶 / 泡咖啡:烧水、冲泡、倒出,其中 "冲泡" 步骤不同)。
- 场景:多个子类有共同的算法骨架,仅部分步骤不同(如数据库操作:连接、执行 SQL、关闭连接,其中 "执行 SQL" 可变)。
- 代码示例:
java
// 抽象模板:饮品制作
public abstract class DrinkTemplate {
// 模板方法:算法骨架(final防止子类重写)
public final void makeDrink() {
boilWater(); // 固定步骤1:烧水
brew(); // 可变步骤:冲泡
pourInCup(); // 固定步骤2:倒入杯中
addCondiments(); // 可选步骤:加调料(钩子方法)
}
// 固定步骤
private void boilWater() {
System.out.println("烧开水");
}
private void pourInCup() {
System.out.println("倒入杯中");
}
// 抽象步骤:子类实现
protected abstract void brew();
// 钩子方法:可选实现(默认空)
protected void addCondiments() {}
}
// 具体模板1:咖啡
public class Coffee extends DrinkTemplate {
@Override
protected void brew() {
System.out.println("冲泡咖啡粉");
}
@Override
protected void addCondiments() {
System.out.println("加奶和糖");
}
}
// 具体模板2:茶
public class Tea extends DrinkTemplate {
@Override
protected void brew() {
System.out.println("冲泡茶叶");
}
// 茶不加调料,使用默认钩子方法
}
// 测试
public class Test {
public static void main(String[] args) {
DrinkTemplate coffee = new Coffee();
System.out.println("制作咖啡:");
coffee.makeDrink();
System.out.println("\n制作茶:");
DrinkTemplate tea = new Tea();
tea.makeDrink();
}
}
总结
- 设计模式的核心是 "解耦":创建型解耦 "对象创建",结构型解耦 "类 / 对象组合",行为型解耦 "对象交互",最终目标是代码可维护、可扩展。
- 选择模式的关键是场景 :
- 需控制对象创建:单例、工厂、建造者;
- 需适配 / 扩展对象功能:适配器、装饰器、代理;
- 需规范对象交互:策略、观察者、模板方法;
- 避免过度设计:设计模式是 "工具",不是 "教条",简单场景用简单代码,只有当代码出现耦合、难以扩展时,再引入设计模式。