设计模式--装饰者模式

设计模式--装饰者模式

今天偶然的机会接触到了装饰者模式,当我们需要很多的子类组合去实现一个功能时,可以考虑下使用装饰者模式。例如衣服有尺寸、规格、颜色,咖啡有种类、糖度、温度,这样的动态组合会衍生出指数增长的子类,装饰者模式就适用于这样的情景。

1、适用场景

  • 使用子类拓展一个类的功能呈现爆炸性增长,请改为装饰者模式
  • 动态撤销、增加一个类的功能,各个功能类相互独立。

2、角色

  • 抽象构件角色(Component):定义一个接口或抽象类,装饰者角色通过它实现给具体的构件角色赋能,例如衣服。
  • 具体构件角色(ConcreteComponent):实现或继承抽象构件角色,被装饰者角色赋能的对象,例如裤子、外套。
  • 抽象装饰者角色(Decorator): 同样实现或继承抽象构件角色,一般情况下是一个抽象类,用来拓展抽象构件角色的功能,例如拓展衣服的尺寸。
  • 具体装饰者角色(ConcretDecorator) :继承于抽象装饰者角色,实现具体的赋能动作,例如尺寸为 XL 。

组件构成参考博客:装饰者角色组成

看了上面的描述你可能还会有点懵,请看下面的构图:

5

看完之后是否有一定的印象了呢? ConcreteComponent 及 ConcreteDecorator 都可以拓展,且相互独立,这便是装饰者模式。

3、例子

以衣服的材质、价格为例子演示下装饰者模式。

衣服--抽象构件角色(Component)

package com.lin.decorator;

import java.math.BigDecimal;

/**
 * 抽象构件角色(Component),衣服总称
 */
public abstract class Clothes {
    String description = "clothes";

    /**
     * 衣服的描述
     *
     * @return 描述,具体构件角色可自定义描述
     */
    public String getDescription() {
        return description;
    }
    /**
     * 衣服的价格,由具体构件角色实现
     *
     * @return 价格
     */
    public abstract BigDecimal getCost();
}

裤子--具体构件角色

package com.lin.decorator;

import java.math.BigDecimal;
/**
 * 具体构件角色(ConcreteComponent),裤子
 */
public class Pants extends Clothes{
    /**
     * 裤子的描述
     */
    public Pants() {
        description = "Pants";
    }

    /**
     * 裤子的价格,此时是装饰前的。
     *
     * @return
     */
    @Override
    public BigDecimal getCost() {
        return new BigDecimal(40.0);
    }
}

背心--具体构件角色

package com.lin.decorator;

import java.math.BigDecimal;
/**
 * 具体构件角色(ConcreteComponent),背心
 */
public class Vest extends Clothes{
    /**
     * 背心的描述
     */
    public Vest() {
        description = "Vest";
    }

    /**
     * 背心的价格,此时是没有被装饰前的。
     *
     * @return 价格
     */
    @Override
    public BigDecimal getCost() {
        return new BigDecimal(40.0);
    }
}

衣服材质--抽象装饰者角色

package com.lin.decorator;

import java.math.BigDecimal;

/**
 * 抽象装饰者角色(Decorator), 衣服的材质
 */
public abstract class MaterialDecorator extends Clothes {
    /**
     * 由具体装饰者角色(ConcreteDecorator)实现每一次的描述
     *
     * @return
     */
    public abstract String getDescription();
    /**
     * 由具体装饰者角色(ConcreteDecorator)实现每一次的价格计算
     *
     * @return
     */
    public abstract BigDecimal getCost();
}

雪纺--具体装饰者角色

package com.lin.decorator;

import java.math.BigDecimal;

/**
 * 具体装饰者角色(ConcreteDecorator),衣服的材质为雪纺
 */
public class Chiffon extends MaterialDecorator {
    private Clothes clothes;
    /**
     * 保存每一次雪纺装饰的构件角色
     *
     * @param clothes
     */
    public Chiffon(Clothes clothes) {
        this.clothes = clothes;
    }

    /**
     * 保留每一次雪纺装饰的描述
     *
     * @return 描述
     */
    @Override
    public String getDescription() {
        return clothes.getDescription() + " 雪纺";
    }

    /**
     * 保留每一次雪纺装饰的价格
     *
     * @return 价格
     */
    @Override
    public BigDecimal getCost() {
        return new BigDecimal(30.0).add(clothes.getCost());
    }
}

纤维--具体装饰者角色

package com.lin.decorator;

import java.math.BigDecimal;
/**
 * 具体装饰者角色(ConcreteDecorator),衣服的材质为纤维
 */
public class Fibre extends MaterialDecorator {
    private Clothes clothes;
    /**
     * 保存每一次纤维装饰的构件角色
     *
     * @param clothes
     */
    public Fibre(Clothes clothes) {
        this.clothes = clothes;
    }

    /**
     * 保留每一次纤维装饰的描述
     *
     * @return 描述
     */
    @Override
    public String getDescription() {
        return clothes.getDescription() + " 纤维";
    }

    /**
     * 保留每一次纤维装饰的价格
     *
     * @return 价格
     */
    @Override
    public BigDecimal getCost() {
        return new BigDecimal(50.0).add(clothes.getCost());
    }
}

测试类

package com.lin.controller;

import com.lin.decorator.*;

public class test {

    public static void main(String[] args) {
        Clothes clothes1 = new Pants();
        System.out.println(clothes1.getDescription() + " 价格为:" + clothes1.getCost());

        Clothes clothes2 = new Pants();
        clothes2 = new Fibre(clothes2);
        System.out.println(clothes2.getDescription() + " 价格为:" + clothes2.getCost());

        Clothes clothes3 = new Vest();
        clothes3 = new Chiffon(clothes3);
        System.out.println(clothes3.getDescription() + " 价格为:" + clothes3.getCost());
    }
}

运行结果如下:

Pants 价格为:40
Pants 纤维 价格为:90
Vest 雪纺 价格为:70

装饰者模式的写法多种多样,寻找自己易于理解的点实现类似的功能即可。

4、缺点

1、增加了抽象装饰者类和具体装饰者类,一定程度增加了系统的复杂度,加大了系统的学习和理解成本。

2、灵活性也意味着更容易出错,对于多次被多次修饰的对象,调试时寻找错误可能需要找到多个地方。

posted @ 2021-01-30 16:53  MyDistance  阅读(69)  评论(0编辑  收藏  举报