前言:为什么要学模式组合?
学单个设计模式就像学武术招式,模式组合才是真正的实战!很多面试官喜欢问:"这几个模式有什么区别?能一起用吗?"------因为他们想知道你是否真的懂设计模式,而不是只会背定义。
第一章:装饰者模式 - "俄罗斯套娃"的艺术
1.1 生活中的装饰者
想象你去星巴克点咖啡:
-
基础:拿铁咖啡(20元)
-
加糖:+2元
-
加奶:+5元
-
加奶油:+8元
如果用传统继承,你会需要:
java
class Latte {} // 拿铁
class LatteWithSugar {} // 拿铁加糖
class LatteWithMilk {} // 拿铁加奶
class LatteWithSugarAndMilk {} // 拿铁加糖加奶
// ... 类爆炸!
1.2 装饰者模式的拯救
装饰者模式说:"别急,我们来玩俄罗斯套娃!"
java
// 核心咖啡
Coffee coffee = new Latte();
// 加糖(套一层)
coffee = new Sugar(coffee);
// 加奶(再套一层)
coffee = new Milk(coffee);
// 加奶油(再套一层)
coffee = new Cream(coffee);
System.out.println("总价:" + coffee.cost()); // 20+2+5+8=35元
1.3 装饰者的核心魔法
java
// 魔法代码:既是Component,又有Component
abstract class CoffeeDecorator implements Coffee { // 我是咖啡
protected Coffee coffee; // 我也有咖啡
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee; // 套娃开始了!
}
}
关键理解:装饰者就像套娃,每个娃娃外表看起来都一样,但大娃娃里面套着小娃娃。
第二章:桥接模式 - "婚姻介绍所"的智慧
2.1 当装饰者遇到麻烦
假设咖啡不仅有配料,还有不同杯型:
-
小杯、中杯、大杯
-
热饮、冰饮
如果用装饰者处理所有维度:
java
// 越来越复杂...
Coffee coffee = new Large(
new Iced(
new Sugar(
new Milk(new Latte()))));
// 这代码看起来像什么?
2.2 桥接模式的哲学
桥接模式说:"别把所有东西都套在一起!有些维度应该平等相处。"
比如图形系统:
-
维度1:形状(圆形、方形)
-
维度2:颜色(红色、蓝色)
传统继承需要:红圆、蓝圆、红方、蓝方 → 4个类
桥接模式:形状类 + 颜色类 → 2+2=4个类(但更灵活)
2.3 桥接模式的代码之美
java
// 形状和颜色是平等的"合伙人"
abstract class Shape {
protected Color color; // 桥接的关键!
public Shape(Color color) {
this.color = color; // 形状"持有"颜色
}
}
class Circle extends Shape {
public void draw() {
System.out.print("圆形");
color.applyColor(); // 让颜色来上色
}
}
// 使用
Shape redCircle = new Circle(new Red());
redCircle.draw(); // 输出:圆形[红色]
关键理解:桥接模式让两个维度"门当户对、平等合作",而不是一个依附另一个。
第三章:装饰者 vs 桥接
3.1 快速区分口诀
java
装饰者模式:A ← B ← C (B和C装饰A)
咖啡 ← 糖 ← 奶
"纵向套娃"
桥接模式:A × B (A和B平等)
形状 × 颜色
"横向合作"
3.2 经典对比表
| 方面 | 装饰者模式 | 桥接模式 |
|---|---|---|
| 关系 | 主从关系(装饰者依附被装饰者) | 平等关系 |
| 变化 | 纵向叠加功能 | 横向分离维度 |
| 比喻 | 穿衣打扮(内衣+衬衫+外套) | 婚姻介绍(男方×女方) |
| 代码 | new A(new B(new C())) |
new A(new B()) |
3.3 实战选择题
场景:设计一个游戏角色系统,有职业(战士、法师)和装备(剑、法杖)
应该用哪个模式?
-
装饰者? 装备不是"装饰"职业,装备可以独立存在
-
桥接? 职业和装备是两个平等维度
第四章:抽象工厂 - "家族企业"的传承
4.1 当维度太多怎么办?
假设我们不仅有:
-
咖啡类型(拿铁、美式)
-
杯型(大、中、小)
-
配料(糖、奶)
还要支持不同品牌:
-
星巴克:默认加糖
-
瑞幸:默认加奶
-
COSTA:默认加奶油
4.2 抽象工厂登场
抽象工厂说:"我来负责创建一整套产品!"
java
// 抽象工厂:定义咖啡店的"产品线"
interface CoffeeShopFactory {
Coffee createCoffee(); // 咖啡
Dessert createDessert(); // 甜点
Cup createCup(); // 杯子
}
// 星巴克工厂
class StarbucksFactory implements CoffeeShopFactory {
public Coffee createCoffee() {
Coffee coffee = new Latte(new LargeCup()); // 桥接
coffee = new Sugar(coffee); // 装饰者
return coffee; // 星巴克默认大杯拿铁加糖
}
}
4.3 三种模式的分工
java
抽象工厂:老板(决定用什么"品牌套餐")
桥接模式:建筑师(设计"户型结构")
装饰者模式:装修工(添加"软装配饰")
第五章:终极组合
5.1 现实案例:豪华咖啡系统
java
public class UltimateCoffeeSystem {
public static void main(String[] args) {
// 1. 抽象工厂:选择品牌
CoffeeShopFactory factory = new StarbucksFactory();
// 2. 创建基础咖啡(工厂内部用桥接)
Coffee coffee = factory.createCoffee();
// 此时得到:大杯拿铁(桥接结果)+ 糖(装饰者)
// 3. 用户个性化定制(额外装饰)
if (userWantsMilk) {
coffee = new Milk(coffee); // 再加奶
}
// 最终:大杯拿铁 + 糖 + 奶
System.out.println("您的订单:" + coffee.getDesc());
System.out.println("总价:¥" + coffee.cost());
}
}
5.2 组合模式的威力
假设我们要支持:
-
3个品牌(星巴克、瑞幸、COSTA)
-
4种咖啡(拿铁、美式、卡布、摩卡)
-
3种杯型(大、中、小)
-
5种配料(糖、奶、奶油、焦糖、肉桂)
传统继承需要 :3 × 4 × 3 × 2^5 = 1152 个类!
模式组合只需:3 + 4 + 3 + 5 = 15 个类!
5.3 什么时候用这个"豪华套餐"?
java
// 适合用:
if (系统复杂 && 需求多变 && 需要灵活扩展) {
使用模式组合();
}
// 不适合用:
if (需求简单 || 时间紧张 || 团队经验不足) {
用最简单的方式实现();
}
第六章:警告!不要盲目追求模式
6.1 什么时候该用简单设计?
java
// 场景:公司内部工具,就5个人用
// 简单方案:
class 简单咖啡 {
private String 类型;
private String 杯型;
private List<String> 配料;
public double 计算价格() {
// 简单逻辑直接写
double 价格 = 基础价格[类型] * 杯型系数[杯型];
for (String 料 : 配料) {
价格 += 配料价格[料];
}
return 价格;
}
}
// 简单、直接、好维护
6.2 设计模式使用原则
原则1:从简单开始
// 第一版:直接实现
// 第二版:发现需要加配料 → 引入装饰者
// 第三版:发现多品牌 → 引入工厂
// 第四版:发现规格复杂 → 引入桥接
// 不要第一版就上全套!
原则2:为变化而设计,不为可能性
// 错误的:“万一以后要支持100种配料呢?”
// 正确的:“目前有3种配料,用简单方案,等真有10种时再重构”
原则3:团队能力很重要
// 如果团队都不懂设计模式:
// 用复杂模式 = 维护灾难
// 用简单方案 = 大家都能改
总结:设计模式的真正价值
学设计模式不是为了炫技,而是为了:
-
写出更灵活的代码(明天需求变了,我改得少)
-
写出更易维护的代码(同事能看懂,能接手)
-
写出更优雅的代码(自己看着舒服)
记住:
-
模式是工具,不是目的
-
简单够用就好,不要过度设计
-
理解思想比记住结构更重要