Preview
Skip to content
CodingDiary
返回

设计模式之结构型设计模式-装饰者模式

编辑页面
导读

BufferedReader、InputStream、OutputStream……JDK IO 类为什么可以层层包装?装饰者模式!本文用「购买 MacBook Pro 升级内存/硬盘套餐」的场景讲解:基础套餐 → 叠加内存装饰器 → 叠加硬盘装饰器,动态扩展功能。比继承更灵活,符合开闭原则。

1. 模式简介

在不改变原有对象的基础之上,将新功能附加到原有对象上,提供了比继承更有弹性的解决方案(扩展原有对象的功能),属于结构型设计模式

2. 示例代码

这回咱们就土豪一把,模拟购买 2019 款 MacBook Pro 16 寸,来演示装饰者模式的逻辑。

/**
 * <p>
 * 苹果笔记本
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019/12/13 16:51
 */
public interface MacBookPro {
    /**
     * 套餐名称
     *
     * @return 套餐名称
     */
    String getComboName();

    /**
     * 硬盘
     *
     * @return 硬盘
     */
    String getHardDisk();

    /**
     * 内存
     *
     * @return 内存
     */
    String getMemory();

    /**
     * 价格
     *
     * @return 价格
     */
    Double getPrice();
}
/**
 * <p>
 * 套餐 - 装饰器
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019/12/13 17:08
 */
@AllArgsConstructor
public abstract class ComboDecorator implements MacBookPro {
    private MacBookPro macBookPro;

    /**
     * 套餐名称
     *
     * @return 套餐名称
     */
    @Override
    public String getComboName() {
        return this.macBookPro.getComboName();
    }

    /**
     * 硬盘
     *
     * @return 硬盘
     */
    @Override
    public String getHardDisk() {
        return this.macBookPro.getHardDisk();
    }

    /**
     * 内存
     *
     * @return 内存
     */
    @Override
    public String getMemory() {
        return this.macBookPro.getMemory();
    }

    /**
     * 价格
     *
     * @return 价格
     */
    @Override
    public Double getPrice() {
        return this.macBookPro.getPrice();
    }
}
/**
 * <p>
 * 苹果笔记本基础套餐
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019/12/13 16:52
 */
public class BaseMacBookProCombo implements MacBookPro {

    /**
     * 套餐名称
     *
     * @return 套餐名称
     */
    @Override
    public String getComboName() {
        return "基础套餐";
    }

    /**
     * 硬盘
     *
     * @return 硬盘
     */
    @Override
    public String getHardDisk() {
        return "1T 固态硬盘";
    }

    /**
     * 内存
     *
     * @return 内存
     */
    @Override
    public String getMemory() {
        return "16GB 2666MHz DDR4 内存";
    }

    /**
     * 价格
     *
     * @return 价格
     */
    @Override
    public Double getPrice() {
        return 22199d;
    }
}
/**
 * <p>
 * 内存套餐
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019/12/13 17:17
 */
public class MemoryCombo extends ComboDecorator {
    public MemoryCombo(MacBookPro macBookPro) {
        super(macBookPro);
    }

    /**
     * 套餐名称
     *
     * @return 套餐名称
     */
    @Override
    public String getComboName() {
        return super.getComboName() + " + 升级内存";
    }

    /**
     * 内存
     *
     * @return 内存
     */
    @Override
    public String getMemory() {
        return super.getMemory() + " + 16G 内存";
    }

    /**
     * 价格
     *
     * @return 价格
     */
    @Override
    public Double getPrice() {
        return super.getPrice() + 2936;
    }
}
/**
 * <p>
 * 硬盘套餐
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019/12/13 17:15
 */
public class HardDiskCombo extends ComboDecorator {
    public HardDiskCombo(MacBookPro macBookPro) {
        super(macBookPro);
    }

    /**
     * 套餐名称
     *
     * @return 套餐名称
     */
    @Override
    public String getComboName() {
        return super.getComboName() + " + 升级硬盘";
    }

    /**
     * 硬盘
     *
     * @return 硬盘
     */
    @Override
    public String getHardDisk() {
        return super.getHardDisk() + " + 1TB 固态硬盘";
    }

    /**
     * 价格
     *
     * @return 价格
     */
    @Override
    public Double getPrice() {
        return super.getPrice() + 2936;
    }
}
/**
 * <p>
 * 装饰者模式 - 测试类
 * </p>
 *
 * @author yangkai.shen
 * @date Created in 2019/12/13 17:19
 */
public class PatternTest {
    public static void main(String[] args) {
        // MacBookPro 基础套餐
        MacBookPro macBookPro = new BaseMacBookProCombo();
        printInfo(macBookPro);

        // 升级内存 16G -> 64G
        macBookPro = new MemoryCombo(macBookPro);
        macBookPro = new MemoryCombo(macBookPro);
        macBookPro = new MemoryCombo(macBookPro);
        printInfo(macBookPro);

        // 升级硬盘 1T -> 2T
        macBookPro = new HardDiskCombo(macBookPro);
        printInfo(macBookPro);
    }

    private static void printInfo(MacBookPro macBookPro) {
        System.out.println("当前套餐: " + macBookPro.getComboName());
        System.out.println("内存: " + macBookPro.getMemory());
        System.out.println("硬盘: " + macBookPro.getHardDisk());
        System.out.println("总价为: " + macBookPro.getPrice() + "");
        System.out.println("\n======================================\n");
    }
}
当前套餐: 基础套餐
内存: 16GB 2666MHz DDR4 内存
硬盘: 1T 固态硬盘
总价为: 22199.0

======================================

当前套餐: 基础套餐 + 升级内存 + 升级内存 + 升级内存
内存: 16GB 2666MHz DDR4 内存 + 16G 内存 + 16G 内存 + 16G 内存
硬盘: 1T 固态硬盘
总价为: 31007.0

======================================

当前套餐: 基础套餐 + 升级内存 + 升级内存 + 升级内存 + 升级硬盘
内存: 16GB 2666MHz DDR4 内存 + 16G 内存 + 16G 内存 + 16G 内存
硬盘: 1T 固态硬盘 + 1TB 固态硬盘
总价为: 33943.0

======================================

3. UML 图例

design-pattern-observer-uml

4. 应用

// JDK 中的 IO 类
// BufferedReader、InputStream、OutputStream

// Spring 中处理事务缓存 TransactionAwareCacheDecorator

// Spring MVC 中对 Header 的处理 HttpHeadResponseDecorator

5. 场景

6. 优缺点

优点: 1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。 2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。 3、装饰者完全遵守开闭原则。

缺点: 1、会出现更多的代码,更多的类,增加程序复杂性。 2、动态装饰时,多层装饰时会更复杂。

7. 完整代码地址

https://github.com/xkcoding/design-pattern/tree/master/src/main/java/com/xkcoding/design/pattern/structural/decorator


编辑页面
分享到:

上一篇
简单 HTTP 工具
下一篇
设计模式之行为型设计模式-观察者模式