从装饰者到桥接再到工厂:模式组合的艺术

前言:为什么要学模式组合?

学单个设计模式就像学武术招式,模式组合才是真正的实战!很多面试官喜欢问:"这几个模式有什么区别?能一起用吗?"------因为他们想知道你是否真的懂设计模式,而不是只会背定义。


第一章:装饰者模式 - "俄罗斯套娃"的艺术

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:团队能力很重要

复制代码
// 如果团队都不懂设计模式:
// 用复杂模式 = 维护灾难
// 用简单方案 = 大家都能改

总结:设计模式的真正价值

学设计模式不是为了炫技,而是为了:

  1. 写出更灵活的代码(明天需求变了,我改得少)

  2. 写出更易维护的代码(同事能看懂,能接手)

  3. 写出更优雅的代码(自己看着舒服)

记住

  • 模式是工具,不是目的

  • 简单够用就好,不要过度设计

  • 理解思想比记住结构更重要

相关推荐
你怎么知道我是队长21 小时前
C语言---缓冲区
c语言·开发语言
一勺菠萝丶21 小时前
PDF24 转图片出现“中间横线”的根本原因与终极解决方案(DPI 原理详解)
java
姓蔡小朋友21 小时前
Unsafe类
java
一只专注api接口开发的技术猿1 天前
如何处理淘宝 API 的请求限流与数据缓存策略
java·大数据·开发语言·数据库·spring
superman超哥1 天前
Rust 异步递归的解决方案
开发语言·后端·rust·编程语言·rust异步递归
荒诞硬汉1 天前
对象数组.
java·数据结构
期待のcode1 天前
Java虚拟机的非堆内存
java·开发语言·jvm
黎雁·泠崖1 天前
Java入门篇之吃透基础语法(二):变量全解析(进制+数据类型+键盘录入)
java·开发语言·intellij-idea·intellij idea
仙俊红1 天前
LeetCode484周赛T4
java