Head First Design Patterns - 装饰者模式

什么是装饰者模式

装饰者模式动态地将额外责任附加到对象上。对于拓展功能,装饰者提供子类化的弹性替代方案。 --《Head First Design Patterns》中的定义

为什么会有装饰者模式

根据上述定义,简单来说,装饰者模式就是对原有的类,增加一些额外的功能或者行为。用普通的继承或者组合也可以实现,但是单纯用继承或者组合来做,会产生大量的拓展类而导致"类爆炸"。

书中用咖啡的价格为例,说明了类爆炸这个概念:

咖啡店的咖啡的种类有4种,每种的价格不一样

若除了咖啡本身,还可以要求加上不同的调料,例如蒸奶(milk)、豆奶(soy)、摩卡(Mocha)。这样子,子类的拓展组合,就会有很多种,从而导致类爆炸,如下图所示。

装饰者模式的特点

  • 装饰者和被装饰者继承同一个基类。因为装饰者必须能够取代被装饰者,这里利用继承达到"类型匹配",而不是利用继承获取"行为"
  • 装饰者拥有(has a)一个与被装饰者相同的基类类型属性(类似策略模式)
  • 可以用一个或多个装饰者类包装同一个对象
  • 装饰者可以在所委托被装饰者的行为之前或行为之后或行为之前与之后加上自己的行为,以达到特定的目的

装饰者模式的类图

例子

以上述咖啡店的咖啡价格为例子

  1. 基类:对应上图中的Component
java 复制代码
public abstract class Beverage {
    String description = "Unknown Beverage";

    public String getDescription() {
        return description;
    }

    public abstract double cost();
}

2. 咖啡种类的拓展类: 对应上图中的ConcreteComponent

java 复制代码
public class Espresso extends Beverage{
    public Espresso() {
        description = "Espresso";
    }

    @Override
    public double cost() {
        return 1.99;
    }
}

public class HouseBlend extends Beverage {
    public HouseBlend() {
        description = "HouseBlend";
    }

    @Override
    public double cost() {
        return .89;
    }
}

3. 调料类

java 复制代码
public class Mocha extends CondimentDecorator {
    public Mocha(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", Mocha";
    }

    @Override
    public double cost() {
        return beverage.cost() + .20;
    }
}

public class Whip extends CondimentDecorator{
    public Whip(Beverage beverage) {
        this.beverage = beverage;
    }

    @Override
    public String getDescription() {
        return beverage.getDescription() + ", whip";
    }

    @Override
    public double cost() {
        return beverage.cost() + .10;
    }
}

4. 测试

java 复制代码
public static void main(String[] args) {
        // 要一杯浓缩咖啡,不加调料
        Beverage beverage = new Espresso();
        System.out.println(beverage.getDescription() + " $" + beverage.cost());

        //要一杯HouseLand,带有2份mocha和whip
        Beverage beverage1 = new HouseBlend();
        beverage1 = new Mocha(beverage1);
        beverage1 = new Mocha(beverage1);
        beverage1 = new Whip(beverage1);

        System.out.println(beverage1.getDescription() + " $" + beverage1.cost());
    }

上述的咖啡喝调料可以在不改动基类代码的基础上,进行随意组合。

装饰者模式用到的设计原则

类应该对拓展开放,对修改关闭(开闭原则)

应用

java中的I/O流

参考:装饰者模式

相关推荐
董先生_ad986ad2 小时前
C# 中的 `lock` 关键字本质
开发语言·c#
Lxinccode2 小时前
Java查询数据库表信息导出Word-获取数据库实现[1]:KingbaseES
java·数据库·word·获取数据库信息·获取kingbasees信息
元亓亓亓2 小时前
Java后端开发day36--源码解析:HashMap
java·开发语言·数据结构
sd21315122 小时前
RabbitMQ 复习总结
java·rabbitmq
道剑剑非道2 小时前
QT 打包安装程序【windeployqt.exe】报错c000007d原因:Conda巨坑
开发语言·qt·conda
小邓儿◑.◑3 小时前
C++武功秘籍 | 入门知识点
开发语言·c++
码银5 小时前
Java 集合:泛型、Set 集合及其实现类详解
java·开发语言
大G哥5 小时前
PHP标签+注释+html混写+变量
android·开发语言·前端·html·php
东阳马生架构5 小时前
Nacos简介—4.Nacos架构和原理
java
傻啦嘿哟5 小时前
HTTP代理基础:网络新手的入门指南
开发语言·php