作者 :一个热爱分享的程序猿 🐵
适合人群 :编程小白、设计模式新手、想写出优雅代码的你
难度指数:⭐⭐⭐ (别怕,咱们用人话讲!)
📖 目录
🤔 什么是设计模式?
想象一下,你是个大厨 👨🍳,每天要做各种菜。如果每次都瞎琢磨怎么做,那得累死!聪明的厨师会总结出一些"套路":
- 炒菜套路:热锅→放油→爆香→翻炒→出锅
- 炖汤套路:冷水下锅→大火烧开→小火慢炖→加盐出锅
设计模式就是编程界的"烹饪套路"! 🎯
它是前辈程序员在无数个996加班夜里,用头发和血泪总结出来的最佳实践。1994年,四位大神(GoF - Gang of Four)写了本书《设计模式》,总结了23种经典模式,从此成为程序员的"武林秘籍"。
为什么要学设计模式?
好处 | 比喻 | 说明 |
---|---|---|
🏗️ 代码更易维护 | 房子有设计图 | 半年后回来看代码,不会问"这TM是谁写的?" |
🔄 代码更易复用 | 乐高积木 | 写一次,到处用,不用重复造轮子 |
📈 代码更易扩展 | 手机装APP | 需求变了?加个模块就行,不用推倒重来 |
💬 沟通更高效 | 黑话暗号 | "用个单例",懂的都懂,不用解释半天 |
🎯 设计模式的六大原则
在学具体模式之前,先记住这六大金科玉律(就像学武功前要先练内功):
1️⃣ 单一职责原则 (SRP - Single Responsibility Principle)
人话版: 一个类只干一件事,别让它累死 😵
生活例子:
一个服务员不可能既做菜、又收银、还打扫卫生。分工明确,效率才高!
java
// ❌ 不好的例子:一个类干太多事
class Employee {
void calculateSalary() { } // 算工资
void saveToDatabase() { } // 存数据库
void generateReport() { } // 生成报表
}
// ✅ 好的例子:职责分离
class SalaryCalculator {
void calculate() { }
}
class EmployeeRepository {
void save() { }
}
class ReportGenerator {
void generate() { }
}
2️⃣ 开闭原则 (OCP - Open Closed Principle)
人话版: 对扩展开放,对修改关闭(新需求?加代码,别改老代码!)
生活例子:
你买了个手机,想要新功能就装个APP,不需要拆手机改硬件吧?📱
java
// ❌ 不好的例子:每次加新图形都要改代码
class GraphicEditor {
void drawShape(Shape s) {
if (s.type == 1) drawRectangle(s);
else if (s.type == 2) drawCircle(s);
// 每次加新图形都要在这里加 if...太痛苦了!
}
}
// ✅ 好的例子:使用多态,添加新图形不用改老代码
interface Shape {
void draw();
}
class Rectangle implements Shape {
void draw() { /* 画矩形 */ }
}
class Circle implements Shape {
void draw() { /* 画圆形 */ }
}
3️⃣ 里氏替换原则 (LSP - Liskov Substitution Principle)
人话版: 子类可以替换父类,程序照样跑
生活例子:
老板说"找个人来送货",无论来的是正式员工还是临时工,送货这事儿都能干!🚚
java
class Bird {
void fly() { /* 飞翔 */ }
}
class Sparrow extends Bird {
void fly() { /* 麻雀的飞法 */ } // ✅ 可以
}
class Ostrich extends Bird {
void fly() {
throw new Exception("鸵鸟不会飞!"); // ❌ 违反原则
}
}
4️⃣ 依赖倒置原则 (DIP - Dependency Inversion Principle)
人话版: 依赖抽象,不要依赖具体实现
生活例子:
你用的是"充电接口"(抽象),不管是苹果充电器还是安卓充电器(具体实现),插上就能用!🔌
java
// ❌ 不好的例子:直接依赖具体类
class Driver {
private Benz car; // 只能开奔驰,换辆车就不会开了?
void drive() {
car.run();
}
}
// ✅ 好的例子:依赖抽象接口
interface Car {
void run();
}
class Driver {
private Car car; // 任何实现Car接口的车都能开
void drive() {
car.run();
}
}
5️⃣ 接口隔离原则 (ISP - Interface Segregation Principle)
人话版: 接口要小而精,不要大而全
生活例子:
你去银行办业务,只需要"取款"功能,不需要柜台把"贷款"、"理财"、"换汇"功能全塞给你吧?🏦
java
// ❌ 不好的例子:接口太胖
interface Worker {
void work();
void eat();
void sleep();
}
class Robot implements Worker {
void work() { /* OK */ }
void eat() { /* 机器人不吃饭!*/ } // 尴尬
void sleep() { /* 机器人不睡觉!*/ } // 尴尬
}
// ✅ 好的例子:接口拆分
interface Workable {
void work();
}
interface Eatable {
void eat();
}
interface Sleepable {
void sleep();
}
6️⃣ 迪米特法则 (LoD - Law of Demeter)
人话版: 最少知识原则,只跟朋友说话
生活例子:
你想买瓶可乐,直接给钱让朋友帮你买就行,不用管他怎么走到便利店、怎么挑选、怎么结账!💰
java
// ❌ 不好的例子:知道太多细节
class Person {
void buyCoke() {
Wallet wallet = new Wallet();
Money money = wallet.getMoney();
Store store = new Store();
Coke coke = store.getCoke();
store.pay(money);
// 细节暴露太多了!
}
}
// ✅ 好的例子:只跟直接朋友通信
class Person {
void buyCoke() {
friend.buyFor(this); // 让朋友帮忙买,不管细节
}
}
🎨 创建型模式(5种)
创建型模式关注的是怎么优雅地创建对象。就像生孩子,有的一胎一个,有的一胎多个,还有的用克隆技术... 👶
1. 单例模式 (Singleton) 👑
💡 核心思想
全世界独此一份! 确保一个类只有一个实例,并提供全局访问点。
🌍 生活例子
- 总统:一个国家同时只能有一个总统
- Windows任务管理器:你打开多少次,都只有一个窗口
- 公司的打印机服务:全公司共用一个打印服务
📊 UML图示
css
┌─────────────────────┐
│ Singleton │
├─────────────────────┤
│ - instance: Singleton │ (静态私有实例)
├─────────────────────┤
│ - Singleton() │ (私有构造函数)
│ + getInstance(): Singleton │ (公共获取方法)
└─────────────────────┘
💻 代码实现
1️⃣ 饿汉式(推荐):
java
/**
* 饿汉式单例:类加载时就创建实例
* 优点:线程安全,简单
* 缺点:可能造成资源浪费(如果一直不用)
*/
public class President {
// 类加载时就创建实例(饿汉:一上来就饿,先吃了再说)
private static final President INSTANCE = new President();
private String name;
// 私有构造函数,防止外部 new
private President() {
this.name = "习大大";
System.out.println("总统上任了!");
}
// 公共获取方法
public static President getInstance() {
return INSTANCE;
}
public void manage() {
System.out.println(name + "正在治理国家...");
}
}
// 使用示例
public class Test {
public static void main(String[] args) {
President p1 = President.getInstance();
President p2 = President.getInstance();
System.out.println(p1 == p2); // true,是同一个对象!
p1.manage(); // 习大大正在治理国家...
}
}
2️⃣ 懒汉式(双重检查锁定):
java
/**
* 懒汉式单例:用的时候才创建
* 优点:延迟加载,节省资源
* 缺点:代码复杂
*/
public class TaskManager {
// volatile 防止指令重排序
private static volatile TaskManager instance;
private TaskManager() {
System.out.println("任务管理器启动!");
}
public static TaskManager getInstance() {
if (instance == null) { // 第一次检查
synchronized (TaskManager.class) { // 加锁
if (instance == null) { // 第二次检查
instance = new TaskManager();
}
}
}
return instance;
}
public void showProcesses() {
System.out.println("显示正在运行的进程...");
}
}
3️⃣ 枚举式(最优雅):
java
/**
* 枚举单例:最简洁、最安全的方式
* 优点:防止反射攻击、防止序列化破坏、线程安全
* 缺点:不能延迟加载
*/
public enum DatabaseConnection {
INSTANCE;
private Connection connection;
DatabaseConnection() {
// 初始化数据库连接
System.out.println("数据库连接已建立!");
}
public void query(String sql) {
System.out.println("执行SQL: " + sql);
}
}
// 使用
DatabaseConnection.INSTANCE.query("SELECT * FROM users");
⚠️ 使用场景
- ✅ 频繁创建和销毁的对象
- ✅ 创建对象耗时或耗资源
- ✅ 需要频繁访问数据库或文件
- ❌ 需要多个实例的场景
🎭 现实应用
- Spring框架中的Bean默认是单例
- 数据库连接池
- 线程池
- 配置管理类
2. 工厂方法模式 (Factory Method) 🏭
💡 核心思想
不直接new对象,而是通过工厂来生产! 定义一个创建对象的接口,让子类决定实例化哪个类。
🍕 生活例子
你去披萨店点餐:
- 你:"老板,来个披萨!"
- 老板:"要什么口味?"
- 你:"芝士的!"
- 老板让芝士披萨工厂生产 → 给你一个芝士披萨
你不需要知道披萨怎么做,工厂帮你搞定一切!
📊 UML图示
scss
┌──────────────┐
│ Creator │ (抽象工厂)
├──────────────┤
│ + factoryMethod(): Product │
│ + someOperation() │
└──────────────┘
▲
│
┌───────┴────────┐
│ │
┌─────┴──────┐ ┌──────┴──────┐
│ ConcreteCreatorA │ │ ConcreteCreatorB │
├────────────┤ ├─────────────┤
│ + factoryMethod() │ │ + factoryMethod() │
└────────────┘ └─────────────┘
│ │
└────────┬───────┘
▼
┌─────────┐
│ Product │ (产品接口)
└─────────┘
💻 代码实现
java
// 产品接口:披萨
interface Pizza {
void prepare(); // 准备
void bake(); // 烘烤
void cut(); // 切片
void box(); // 装盒
}
// 具体产品:芝士披萨
class CheesePizza implements Pizza {
public void prepare() { System.out.println("准备芝士披萨材料..."); }
public void bake() { System.out.println("烘烤芝士披萨..."); }
public void cut() { System.out.println("切芝士披萨..."); }
public void box() { System.out.println("装盒芝士披萨..."); }
}
// 具体产品:蔬菜披萨
class VeggiePizza implements Pizza {
public void prepare() { System.out.println("准备蔬菜披萨材料..."); }
public void bake() { System.out.println("烘烤蔬菜披萨..."); }
public void cut() { System.out.println("切蔬菜披萨..."); }
public void box() { System.out.println("装盒蔬菜披萨..."); }
}
// 抽象工厂:披萨店
abstract class PizzaStore {
// 工厂方法:由子类实现
protected abstract Pizza createPizza(String type);
// 业务方法
public Pizza orderPizza(String type) {
Pizza pizza = createPizza(type); // 调用工厂方法
// 统一的制作流程
pizza.prepare();
pizza.bake();
pizza.cut();
pizza.box();
return pizza;
}
}
// 具体工厂:纽约披萨店
class NYPizzaStore extends PizzaStore {
@Override
protected Pizza createPizza(String type) {
if (type.equals("cheese")) {
return new CheesePizza();
} else if (type.equals("veggie")) {
return new VeggiePizza();
}
return null;
}
}
// 使用示例
public class Test {
public static void main(String[] args) {
PizzaStore nyStore = new NYPizzaStore();
Pizza pizza = nyStore.orderPizza("cheese");
// 输出:
// 准备芝士披萨材料...
// 烘烤芝士披萨...
// 切芝士披萨...
// 装盒芝士披萨...
}
}
⚠️ 使用场景
- ✅ 创建对象需要大量重复代码
- ✅ 客户端不知道具体需要创建哪个类的实例
- ✅ 一个类希望由其子类指定创建对象
3. 抽象工厂模式 (Abstract Factory) 🏭🏭
💡 核心思想
工厂的工厂! 提供一个创建一系列相关或相互依赖对象的接口,无需指定具体类。
🪑 生活例子
你要装修房子,需要买全套家具:
- 现代风格套装:现代沙发 + 现代茶几 + 现代衣柜
- 古典风格套装:古典沙发 + 古典茶几 + 古典衣柜
抽象工厂保证:你买的家具风格统一,不会出现"现代沙发配古典茶几"的悲剧!
💻 代码实现
java
// 产品族接口:沙发
interface Sofa {
void sit();
}
// 产品族接口:茶几
interface CoffeeTable {
void place();
}
// 现代风格产品
class ModernSofa implements Sofa {
public void sit() { System.out.println("坐在现代风格沙发上"); }
}
class ModernCoffeeTable implements CoffeeTable {
public void place() { System.out.println("放在现代风格茶几上"); }
}
// 古典风格产品
class ClassicalSofa implements Sofa {
public void sit() { System.out.println("坐在古典风格沙发上"); }
}
class ClassicalCoffeeTable implements CoffeeTable {
public void place() { System.out.println("放在古典风格茶几上"); }
}
// 抽象工厂
interface FurnitureFactory {
Sofa createSofa();
CoffeeTable createCoffeeTable();
}
// 现代家具工厂
class ModernFurnitureFactory implements FurnitureFactory {
public Sofa createSofa() { return new ModernSofa(); }
public CoffeeTable createCoffeeTable() { return new ModernCoffeeTable(); }
}
// 古典家具工厂
class ClassicalFurnitureFactory implements FurnitureFactory {
public Sofa createSofa() { return new ClassicalSofa(); }
public CoffeeTable createCoffeeTable() { return new ClassicalCoffeeTable(); }
}
// 使用
public class Test {
public static void main(String[] args) {
// 客户选择现代风格
FurnitureFactory factory = new ModernFurnitureFactory();
Sofa sofa = factory.createSofa();
CoffeeTable table = factory.createCoffeeTable();
sofa.sit(); // 坐在现代风格沙发上
table.place(); // 放在现代风格茶几上
// 风格统一!✅
}
}
4. 建造者模式 (Builder) 🏗️
💡 核心思想
分步骤构建复杂对象! 将对象的构建过程和表示分离,可以创建不同表示。
🍔 生活例子
你去麦当劳点汉堡:
- "我要一个汉堡!"
- "要不要芝士?" → 加芝士
- "要不要培根?" → 加培根
- "要不要生菜?" → 不要
一步步定制你的专属汉堡!
💻 代码实现
java
// 产品:电脑
class Computer {
private String cpu; // 必需
private String ram; // 必需
private String storage; // 可选
private String gpu; // 可选
private String monitor; // 可选
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
this.gpu = builder.gpu;
this.monitor = builder.monitor;
}
@Override
public String toString() {
return "Computer{" +
"cpu='" + cpu + '\'' +
", ram='" + ram + '\'' +
", storage='" + storage + '\'' +
", gpu='" + gpu + '\'' +
", monitor='" + monitor + '\'' +
'}';
}
// 建造者
public static class Builder {
// 必需参数
private final String cpu;
private final String ram;
// 可选参数
private String storage = "256GB SSD";
private String gpu = "集成显卡";
private String monitor = "无";
public Builder(String cpu, String ram) {
this.cpu = cpu;
this.ram = ram;
}
public Builder storage(String storage) {
this.storage = storage;
return this;
}
public Builder gpu(String gpu) {
this.gpu = gpu;
return this;
}
public Builder monitor(String monitor) {
this.monitor = monitor;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
// 使用示例
public class Test {
public static void main(String[] args) {
// 办公电脑:只要基本配置
Computer officePC = new Computer.Builder("i5", "8GB")
.build();
// 游戏电脑:要高配
Computer gamingPC = new Computer.Builder("i9", "32GB")
.storage("2TB SSD")
.gpu("RTX 4090")
.monitor("4K显示器")
.build();
System.out.println(officePC);
System.out.println(gamingPC);
}
}
⚠️ 使用场景
- ✅ 对象有很多参数,而且有些是可选的
- ✅ 构造函数参数过多,可读性差
- ✅ 需要创建不可变对象
🎭 现实应用
- StringBuilder / StringBuffer
- Lombok的
@Builder
注解 - HTTP请求构建器
5. 原型模式 (Prototype) 🐑
💡 核心思想
克隆复制! 用原型实例指定创建对象的种类,通过复制这个原型创建新对象。
🐑 生活例子
还记得1996年的多莉羊吗?科学家通过克隆技术,复制出了一只一模一样的羊!
💻 代码实现
java
// 实现Cloneable接口
class Sheep implements Cloneable {
private String name;
private int age;
private String color;
public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}
// 重写clone方法
@Override
protected Sheep clone() {
try {
return (Sheep) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
@Override
public String toString() {
return "Sheep{name='" + name + "', age=" + age + ", color='" + color + "'}";
}
}
// 使用示例
public class Test {
public static void main(String[] args) {
// 原型羊
Sheep dolly = new Sheep("多莉", 3, "白色");
// 克隆10只羊
for (int i = 1; i <= 10; i++) {
Sheep cloneSheep = dolly.clone();
System.out.println("第" + i + "只克隆羊: " + cloneSheep);
}
}
}
⚠️ 注意事项
- 浅拷贝 vs 深拷贝问题
- 如果对象包含引用类型,需要手动实现深拷贝
🏗️ 结构型模式(7种)
结构型模式关注的是怎么把类或对象组合成更大的结构。就像搭积木,怎么搭得既稳固又漂亮!🧱
6. 适配器模式 (Adapter) 🔌
💡 核心思想
转换接口! 将一个类的接口转换成客户期望的另一个接口,让原本不兼容的类可以合作。
🔌 生活例子
你去日本旅游,带的是中国的两孔插头,但日本是三孔插座。怎么办?买个转换插头(适配器)!
💻 代码实现
java
// 目标接口:中国插头(两孔)
interface ChinesePlug {
void chargeWith2Pins();
}
// 被适配者:日本插头(三孔)
class JapanesePlug {
public void chargeWith3Pins() {
System.out.println("使用三孔插头充电");
}
}
// 适配器
class PlugAdapter implements ChinesePlug {
private JapanesePlug japanesePlug;
public PlugAdapter(JapanesePlug japanesePlug) {
this.japanesePlug = japanesePlug;
}
@Override
public void chargeWith2Pins() {
System.out.println("使用适配器转换...");
japanesePlug.chargeWith3Pins();
}
}
// 使用
public class Test {
public static void main(String[] args) {
JapanesePlug japanesePlug = new JapanesePlug();
ChinesePlug adapter = new PlugAdapter(japanesePlug);
adapter.chargeWith2Pins();
// 输出:
// 使用适配器转换...
// 使用三孔插头充电
}
}
7. 装饰器模式 (Decorator) 🎁
💡 核心思想
动态添加功能! 不改变原有对象,给对象动态添加新功能。
☕ 生活例子
你点了一杯咖啡:
- 基础:咖啡(10元)
- 加糖 → 咖啡+糖(12元)
- 加奶 → 咖啡+糖+奶(15元)
- 加冰 → 咖啡+糖+奶+冰(18元)
每加一样,都是在原来基础上"装饰"!
💻 代码实现
java
// 组件接口:饮料
interface Beverage {
String getDescription();
double cost();
}
// 具体组件:咖啡
class Coffee implements Beverage {
@Override
public String getDescription() {
return "咖啡";
}
@Override
public double cost() {
return 10.0;
}
}
// 装饰器抽象类
abstract class CondimentDecorator implements Beverage {
protected Beverage beverage;
public CondimentDecorator(Beverage beverage) {
this.beverage = beverage;
}
}
// 具体装饰器:糖
class Sugar extends CondimentDecorator {
public Sugar(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 糖";
}
@Override
public double cost() {
return beverage.cost() + 2.0;
}
}
// 具体装饰器:奶
class Milk extends CondimentDecorator {
public Milk(Beverage beverage) {
super(beverage);
}
@Override
public String getDescription() {
return beverage.getDescription() + " + 奶";
}
@Override
public double cost() {
return beverage.cost() + 3.0;
}
}
// 使用示例
public class Test {
public static void main(String[] args) {
// 点一杯基础咖啡
Beverage coffee = new Coffee();
System.out.println(coffee.getDescription() + " = " + coffee.cost() + "元");
// 加糖
coffee = new Sugar(coffee);
System.out.println(coffee.getDescription() + " = " + coffee.cost() + "元");
// 再加奶
coffee = new Milk(coffee);
System.out.println(coffee.getDescription() + " = " + coffee.cost() + "元");
// 输出:
// 咖啡 = 10.0元
// 咖啡 + 糖 = 12.0元
// 咖啡 + 糖 + 奶 = 15.0元
}
}
🎭 现实应用
- Java I/O流:
new BufferedReader(new FileReader("file.txt"))
- Servlet过滤器链
8. 代理模式 (Proxy) 🕵️
💡 核心思想
找个代理人! 为其他对象提供一个代理,控制对这个对象的访问。
🏠 生活例子
你要买房,找个房产中介(代理),他帮你:
- 过滤不合适的房源
- 讨价还价
- 办理手续
你不用直接跟房东打交道!
💻 代码实现
java
// 接口:房屋
interface House {
void rent();
}
// 真实对象:房东
class Landlord implements House {
@Override
public void rent() {
System.out.println("租房成功!");
}
}
// 代理:中介
class Agent implements House {
private Landlord landlord;
public Agent() {
this.landlord = new Landlord();
}
@Override
public void rent() {
before();
landlord.rent(); // 调用真实对象
after();
}
private void before() {
System.out.println("中介:筛选房源、带你看房...");
}
private void after() {
System.out.println("中介:办理合同、收取中介费...");
}
}
// 使用
public class Test {
public static void main(String[] args) {
House agent = new Agent();
agent.rent();
// 输出:
// 中介:筛选房源、带你看房...
// 租房成功!
// 中介:办理合同、收取中介费...
}
}
三种代理类型
类型 | 说明 | 使用场景 |
---|---|---|
静态代理 | 编译时确定代理类 | 简单场景 |
动态代理 | 运行时生成代理类 | Spring AOP |
远程代理 | 代理不同地址空间的对象 | RPC调用 |
9. 桥接模式 (Bridge) 🌉
💡 核心思想
分离抽象和实现! 把抽象和实现解耦,让它们可以独立变化。
🎨 生活例子
画图软件:
- 形状:圆形、方形、三角形
- 颜色:红色、绿色、蓝色
如果用继承:红圆形、绿圆形、蓝圆形、红方形、绿方形...(类爆炸💥)
用桥接模式:形状和颜色分开,自由组合!
10. 组合模式 (Composite) 🌳
💡 核心思想
树形结构! 将对象组合成树形结构,让客户端统一处理单个对象和组合对象。
📁 生活例子
电脑的文件系统:
📁 我的电脑
├── 📁 文档
│ ├── 📄 报告.docx
│ └── 📄 总结.xlsx
├── 📁 图片
│ ├── 🖼️ 照片1.jpg
│ └── 🖼️ 照片2.png
└── 📄 readme.txt
文件夹可以包含文件和文件夹,统一用"文件系统节点"来处理!
💻 代码实现
java
// 抽象组件
abstract class FileSystemNode {
protected String name;
public FileSystemNode(String name) {
this.name = name;
}
public abstract void display(int depth);
}
// 叶子节点:文件
class File extends FileSystemNode {
public File(String name) {
super(name);
}
@Override
public void display(int depth) {
System.out.println(" ".repeat(depth) + "📄 " + name);
}
}
// 组合节点:文件夹
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 void remove(FileSystemNode node) {
children.remove(node);
}
@Override
public void display(int depth) {
System.out.println(" ".repeat(depth) + "📁 " + name);
for (FileSystemNode child : children) {
child.display(depth + 2);
}
}
}
// 使用
public class Test {
public static void main(String[] args) {
Folder root = new Folder("我的电脑");
Folder docs = new Folder("文档");
docs.add(new File("报告.docx"));
docs.add(new File("总结.xlsx"));
Folder pics = new Folder("图片");
pics.add(new File("照片1.jpg"));
pics.add(new File("照片2.png"));
root.add(docs);
root.add(pics);
root.add(new File("readme.txt"));
root.display(0);
}
}
11. 外观模式 (Facade) 🎭
💡 核心思想
简化复杂接口! 为子系统提供一个统一的高层接口,让子系统更易用。
🎬 生活例子
你要看电影:
- 打开投影仪
- 拉上窗帘
- 打开音响
- 调节音量
- 播放电影
太复杂了!有了"智能家居系统"(外观),只需说:"播放电影",全部自动完成!
12. 享元模式 (Flyweight) 🪶
💡 核心思想
共享对象,节省内存! 运用共享技术有效支持大量细粒度对象。
♟️ 生活例子
下围棋:
- 棋盘有361个点
- 如果每个棋子都new一个对象,太浪费内存了!
- 实际上只需要2个对象:黑棋、白棋,通过坐标区分位置
🎭 现实应用
- Java的String常量池
- 数据库连接池
- 线程池
🎭 行为型模式(11种)
行为型模式关注的是对象之间怎么协作、怎么通信。就像舞台上的演员,各司其职,配合默契!💃🕺
13. 策略模式 (Strategy) 🎯
💡 核心思想
定义一系列算法,让它们可以互相替换! 让算法的变化独立于使用算法的客户。
🚗 生活例子
你要去机场:
- 打车:快,但贵 💸
- 地铁:便宜,但挤 🚇
- 公交:最便宜,但慢 🚌
根据不同情况,选择不同的出行策略!
💻 代码实现
java
// 策略接口
interface TravelStrategy {
void travel(String destination);
}
// 具体策略:打车
class TaxiStrategy implements TravelStrategy {
@Override
public void travel(String destination) {
System.out.println("打车去" + destination + ",快速但昂贵!");
}
}
// 具体策略:地铁
class SubwayStrategy implements TravelStrategy {
@Override
public void travel(String destination) {
System.out.println("坐地铁去" + destination + ",便宜但拥挤!");
}
}
// 具体策略:公交
class BusStrategy implements TravelStrategy {
@Override
public void travel(String destination) {
System.out.println("坐公交去" + destination + ",最便宜但最慢!");
}
}
// 上下文:旅行者
class Traveler {
private TravelStrategy strategy;
public void setStrategy(TravelStrategy strategy) {
this.strategy = strategy;
}
public void goTo(String destination) {
strategy.travel(destination);
}
}
// 使用
public class Test {
public static void main(String[] args) {
Traveler traveler = new Traveler();
// 赶时间,打车!
traveler.setStrategy(new TaxiStrategy());
traveler.goTo("机场");
// 不着急,坐公交
traveler.setStrategy(new BusStrategy());
traveler.goTo("公司");
}
}
⚠️ 使用场景
- ✅ 一个系统有许多类,它们仅仅是行为不同
- ✅ 需要动态选择算法
- ✅ 避免多重条件语句
14. 观察者模式 (Observer) 👀
💡 核心思想
发布-订阅机制! 一对多依赖,一个对象状态改变,所有依赖者都会收到通知。
📰 生活例子
你订阅了某个公众号:
- 公众号发布新文章 → 你收到推送
- 你取消关注 → 不再收到推送
这就是观察者模式!
💻 代码实现
java
import java.util.*;
// 观察者接口
interface Observer {
void update(String message);
}
// 具体观察者:粉丝
class Fan implements Observer {
private String name;
public Fan(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 收到通知:" + message);
}
}
// 主题接口
interface Subject {
void attach(Observer observer); // 订阅
void detach(Observer observer); // 取消订阅
void notifyObservers(); // 通知
}
// 具体主题:公众号
class WeChatAccount implements Subject {
private String name;
private List<Observer> fans = new ArrayList<>();
private String article;
public WeChatAccount(String name) {
this.name = name;
}
@Override
public void attach(Observer observer) {
fans.add(observer);
System.out.println("新增粉丝!当前粉丝数:" + fans.size());
}
@Override
public void detach(Observer observer) {
fans.remove(observer);
System.out.println("粉丝取关!当前粉丝数:" + fans.size());
}
@Override
public void notifyObservers() {
for (Observer fan : fans) {
fan.update(article);
}
}
public void publishArticle(String article) {
this.article = article;
System.out.println("\n" + name + " 发布新文章:" + article);
notifyObservers();
}
}
// 使用
public class Test {
public static void main(String[] args) {
WeChatAccount account = new WeChatAccount("Java技术栈");
Fan fan1 = new Fan("小明");
Fan fan2 = new Fan("小红");
Fan fan3 = new Fan("小刚");
account.attach(fan1);
account.attach(fan2);
account.attach(fan3);
account.publishArticle("《设计模式入门》");
System.out.println();
account.detach(fan2); // 小红取关了
account.publishArticle("《Spring Boot实战》");
}
}
🎭 现实应用
- Java的事件监听机制
- Spring的事件驱动
- MQ消息队列
- RxJava响应式编程
15. 模板方法模式 (Template Method) 📝
💡 核心思想
定义算法骨架,细节由子类实现! 在父类定义算法结构,将某些步骤延迟到子类。
🍳 生活例子
做菜的固定流程:
- 准备食材
- 热锅
- 炒菜(每个菜不同)
- 装盘
骨架固定,具体炒什么菜由子类决定!
💻 代码实现
java
// 抽象类:做菜模板
abstract class CookingTemplate {
// 模板方法(final防止子类修改流程)
public final void cook() {
prepareIngredients();
heatPan();
fry(); // 钩子方法,由子类实现
serve();
}
// 具体方法
private void prepareIngredients() {
System.out.println("1. 准备食材");
}
private void heatPan() {
System.out.println("2. 热锅");
}
// 抽象方法:由子类实现
protected abstract void fry();
private void serve() {
System.out.println("4. 装盘上菜\n");
}
}
// 具体类:炒青菜
class FryVegetables extends CookingTemplate {
@Override
protected void fry() {
System.out.println("3. 大火快炒青菜");
}
}
// 具体类:炖肉
class StewMeat extends CookingTemplate {
@Override
protected void fry() {
System.out.println("3. 小火慢炖肉");
}
}
// 使用
public class Test {
public static void main(String[] args) {
CookingTemplate veg = new FryVegetables();
veg.cook();
CookingTemplate meat = new StewMeat();
meat.cook();
}
}
🎭 现实应用
- Java的InputStream(read方法)
- Servlet的HttpServlet
- Spring的JdbcTemplate
16. 命令模式 (Command) 🎮
💡 核心思想
把请求封装成对象! 将请求发送者和接收者解耦,支持撤销、重做、日志等功能。
🕹️ 生活例子
遥控器:
- 按"开"键 → 电视打开
- 按"关"键 → 电视关闭
- 按"撤销"键 → 恢复上一步操作
你不需要知道电视怎么实现的,只需要按按钮!
💻 代码实现
java
// 接收者:电视
class TV {
public void turnOn() {
System.out.println("电视打开了 📺");
}
public void turnOff() {
System.out.println("电视关闭了 ⬛");
}
}
// 命令接口
interface Command {
void execute(); // 执行
void undo(); // 撤销
}
// 具体命令:打开电视
class TurnOnCommand implements Command {
private TV tv;
public TurnOnCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.turnOn();
}
@Override
public void undo() {
tv.turnOff();
}
}
// 具体命令:关闭电视
class TurnOffCommand implements Command {
private TV tv;
public TurnOffCommand(TV tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.turnOff();
}
@Override
public void undo() {
tv.turnOn();
}
}
// 调用者:遥控器
class RemoteControl {
private Command command;
private Command lastCommand; // 记录上一次命令,用于撤销
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
lastCommand = command;
}
public void pressUndo() {
if (lastCommand != null) {
System.out.println("撤销上一步操作...");
lastCommand.undo();
}
}
}
// 使用
public class Test {
public static void main(String[] args) {
TV tv = new TV();
Command turnOn = new TurnOnCommand(tv);
Command turnOff = new TurnOffCommand(tv);
RemoteControl remote = new RemoteControl();
remote.setCommand(turnOn);
remote.pressButton(); // 打开电视
remote.setCommand(turnOff);
remote.pressButton(); // 关闭电视
remote.pressUndo(); // 撤销,重新打开
}
}
17. 责任链模式 (Chain of Responsibility) ⛓️
💡 核心思想
请求沿着链传递,直到有对象处理它! 避免请求发送者与接收者耦合。
📋 生活例子
请假流程:
- 请1天假 → 组长批准
- 请3天假 → 经理批准
- 请7天假 → 总监批准
- 请30天假 → 老板批准
请求沿着"组长→经理→总监→老板"链传递!
💻 代码实现
java
// 抽象处理者
abstract class Approver {
protected Approver nextApprover; // 下一个处理者
protected String name;
public Approver(String name) {
this.name = name;
}
public void setNext(Approver nextApprover) {
this.nextApprover = nextApprover;
}
// 处理请假请求
public abstract void handleRequest(int days);
}
// 具体处理者:组长
class TeamLeader extends Approver {
public TeamLeader(String name) {
super(name);
}
@Override
public void handleRequest(int days) {
if (days <= 1) {
System.out.println(name + ":请" + days + "天假,批准!✅");
} else {
System.out.println(name + ":我批不了,转给上级...");
if (nextApprover != null) {
nextApprover.handleRequest(days);
}
}
}
}
// 具体处理者:经理
class Manager extends Approver {
public Manager(String name) {
super(name);
}
@Override
public void handleRequest(int days) {
if (days <= 3) {
System.out.println(name + ":请" + days + "天假,批准!✅");
} else {
System.out.println(name + ":我批不了,转给上级...");
if (nextApprover != null) {
nextApprover.handleRequest(days);
}
}
}
}
// 具体处理者:总监
class Director extends Approver {
public Director(String name) {
super(name);
}
@Override
public void handleRequest(int days) {
if (days <= 7) {
System.out.println(name + ":请" + days + "天假,批准!✅");
} else {
System.out.println(name + ":我批不了,转给老板...");
if (nextApprover != null) {
nextApprover.handleRequest(days);
}
}
}
}
// 具体处理者:老板
class Boss extends Approver {
public Boss(String name) {
super(name);
}
@Override
public void handleRequest(int days) {
if (days <= 30) {
System.out.println(name + ":请" + days + "天假,批准!✅");
} else {
System.out.println(name + ":请这么久?不批!❌");
}
}
}
// 使用
public class Test {
public static void main(String[] args) {
// 构建责任链
Approver leader = new TeamLeader("张组长");
Approver manager = new Manager("李经理");
Approver director = new Director("王总监");
Approver boss = new Boss("刘老板");
leader.setNext(manager);
manager.setNext(director);
director.setNext(boss);
// 请假
System.out.println("====== 小明请1天假 ======");
leader.handleRequest(1);
System.out.println("\n====== 小红请3天假 ======");
leader.handleRequest(3);
System.out.println("\n====== 小刚请7天假 ======");
leader.handleRequest(7);
System.out.println("\n====== 小李请100天假 ======");
leader.handleRequest(100);
}
}
🎭 现实应用
- Servlet的Filter过滤器链
- Spring的拦截器
- Netty的Pipeline
18-23. 其他行为型模式(快速了解)
18. 迭代器模式 (Iterator) 🔄
遍历集合元素! Java的Iterator就是这个模式。
java
List<String> list = new ArrayList<>();
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
19. 中介者模式 (Mediator) 📞
对象间通过中介者通信! 就像房产中介,买家卖家不直接联系。
生活例子: 机场调度塔台,所有飞机通过塔台协调,不互相通信。
20. 备忘录模式 (Memento) 💾
保存和恢复对象状态! 就像游戏存档。
生活例子: 你在玩《塞尔达传说》,打BOSS前先存个档,死了可以读档重来!
21. 状态模式 (State) 🚦
对象根据状态改变行为!
生活例子: 红绿灯
- 红灯 → 停止
- 黄灯 → 等待
- 绿灯 → 通行
22. 访问者模式 (Visitor) 🚶
在不修改类的前提下,定义新操作!
生活例子: 商场的顾客(访问者),可以买衣服、买食品、买电器,但商品类不需要修改。
23. 解释器模式 (Interpreter) 🔤
定义语言的文法,并解释执行!
生活例子: 计算器解析"1 + 2 * 3"这样的表达式。
🎯 实战技巧与建议
📚 学习路线图
第1阶段:入门(1-2周)
├── 理解六大原则
├── 掌握常用模式:单例、工厂、观察者
└── 模仿代码,多练习
第2阶段:进阶(1-2个月)
├── 学习全部23种模式
├── 看Spring源码中的应用
└── 在项目中尝试使用
第3阶段:精通(持续学习)
├── 灵活组合多种模式
├── 根据场景选择最优方案
└── 总结自己的设计经验
🤔 如何选择设计模式?
场景 | 推荐模式 |
---|---|
需要全局唯一对象 | 单例模式 |
需要创建复杂对象 | 建造者模式 |
需要创建大量相似对象 | 工厂模式 |
需要动态添加功能 | 装饰器模式 |
接口不兼容 | 适配器模式 |
需要发布-订阅机制 | 观察者模式 |
需要多种算法可切换 | 策略模式 |
需要控制访问 | 代理模式 |
需要撤销/重做 | 命令模式 |
⚠️ 常见误区
❌ 误区1:滥用设计模式
反例: 一个简单的登录功能,非要用上单例、工厂、策略、观察者...过度设计!
正解: 简单问题简单做,不要为了用模式而用模式。
❌ 误区2:死记硬背
反例: 只背定义和代码,不理解原理和场景。
正解: 理解模式的设计思想 和解决的问题,才能灵活运用。
❌ 误区3:一成不变
反例: 书上怎么写就怎么用,不敢改。
正解: 设计模式是参考 ,不是教条。根据实际情况调整!
💪 实战建议
-
多看优秀源码
- Spring框架
- JDK源码
- Apache Commons
-
多做练习
- 用设计模式重构老代码
- 在新项目中应用
- 参与开源项目
-
多思考总结
- 为什么这样设计?
- 还有更好的方案吗?
- 写技术博客记录
🎉 结语
恭喜你!🎊 读完这篇超长的设计模式指南,你已经从青铜段位晋级到白银了!
记住这几点:
-
设计模式不是银弹 🔫
不要为了用模式而用模式,简单问题简单做。
-
理解比记忆更重要 🧠
搞懂模式解决什么问题,比背代码重要。
-
实践出真知 💻
光看不练假把式,多写代码才能掌握。
-
持续学习 📚
设计模式是起点不是终点,继续探索架构设计、领域驱动设计(DDD)等更高级的内容。
📖 推荐阅读
- 📘 《设计模式:可复用面向对象软件的基础》(GoF圣经)
- 📗 《Head First 设计模式》(图解,通俗易懂)
- 📙 《大话设计模式》(中文,风趣幽默)
- 📕 《重构:改善既有代码的设计》(实战必读)
🙏 致谢
感谢你的耐心阅读!如果这篇文档对你有帮助,请:
- ⭐ 点个赞
- 🔄 转发分享
- 💬 留言交流
最后送你一句话:
优秀的程序员写代码,卓越的程序员写模式! 🚀
🎮 愿你早日成为设计模式王者!
Happy Coding! 💻✨