Day08-设计模式之装饰者模式

发布时间 2023-04-16 21:03:20作者: 晴天阴天下雨天

设计模式之装饰者模式

引例

需求:假设现在有烧饵块:白米饵块(WhiteRice),紫米饵块(PurpleRice),黄米饵块(yellowRice),调料有热狗(HotDog),油条(oilNoodle),鸡柳(chicken),客户可以单点白米的,或者白米+调料的组合,计算相应的费用,要求在扩展饵块种类的时候,具有良好的扩展性,改动维护方便。

img

一般解法

方案一

image-20230403202829931

类图分析:

  • ShaoErkuai是一个抽象类

    -description就是对烧饵块的描述,比如是白米,还是紫米。

    -cost()方法就是计算费用,ShaoErKuai中做成一个抽象方法。

  • PurpleRice就是单种烧饵块,继承了ShaoErKuai,并实现了cost()方法。

  • WhiteRice&&HotDog就是白米的+热狗,这个组合可以很多。

问题:这样设计会有很多类,当我们增加一种烧饵块,或者一个新的调料时,类的数量会倍增,就会出现类爆炸。

方案二

image-20230403210346768

1) 方案 2 可以控制类的数量,不至于造成很多的类

2) 考虑到用户可以添加多份调料,可以将 hasHotDog返回一个对应 int

3) 问题:在增加或者删除调料种类时,需要加上新的方法,并修改基类中的cost()方法。这种设计违反了开放关闭原则(类应该对扩展开放,对修改关闭。)

4) 改进:考虑使用 装饰者 模式

装饰者模式介绍

装饰者模式(Decorator Pattern)是结构型模式,也称装饰器模式/修饰模式。

定义:它可以动态的将新功能附加到对象上,同时又不改变其结构。在对象功能扩展方面,它比继承更有弹性。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法结构完整性的前提下,提供了额外的功能。

类图:

在这里插入图片描述

  • Component抽象类:主体,比如类似前面的ShaoErKuai
  • ConcreteComponent类:具体的主体,比如前面的白米饵块。
  • Decorator类:装饰者,比如前面的调料
  • ConcreteDecorator类:具体的装饰者,比如前面的热狗或者油条。

装饰者解法实现

类图:

image-20230403214215533
Decorator 是一个装饰类,含有一个被装饰的对象(ShaoErKuai obj)和的cost()方法进行一个费用的叠加计算,递归的计算价格。
HotDog和OilNoodle是具体的装饰者
ShaoErKuai是被装饰者主体
WhiteRice和PurpleRice是具体实现的被装饰者实体

代码实现:

1.抽象类

package day08_decorator.test01;

import lombok.Data;

/**
 * @author coolsheep
 * @date 2023/4/3 22:02
 * @Describe 烧饵块抽象类
 */
@Data
public abstract class ShaoErKuai {

    public String description;
    private float price = 0.0f;

    /**
     * 用来返回烧饵块的价格,需要在具体类中自己实现
     *
     * @return
     */
    public abstract float cost();

}

2.被装饰者

/**
 * @author coolsheep
 * @date 2023/4/3 22:06
 * @Describe
 */
public class WhiteRice extends ShaoErKuai {

    public WhiteRice() {
        setDescription("白米烧饵块");
        setPrice(3.0f);
    }

    @Override
    public float cost() {
        return super.getPrice();
    }
}

3.装饰者

/**
 * @author coolsheep
 * @date 2023/4/3 22:04
 * @Describe 装饰者
 */
public class Decorator extends ShaoErKuai {

    private ShaoErKuai obj;

    public Decorator(ShaoErKuai obj) {//组合
        this.obj = obj;
    }

    @Override
    public float cost() {
        return super.getPrice() + obj.cost();
    }

    @Override
    public String getDescription() {
        //obj.getDescription()输出被装饰者的信息
        return description + ":" + getPrice() + " && " + obj.getDescription() + ":" + obj.getPrice();
    }

}



/**
 * @author coolsheep
 * @date 2023/4/3 22:32
 * @Describe 具体的装饰类
 */
public class HotDog extends Decorator{

    public HotDog(ShaoErKuai obj) {
        super(obj);
        setDescription("热狗");
        setPrice(1.5f);
    }
}


/**
 * @author coolsheep
 * @date 2023/4/3 22:35
 * @Describe 具体的装饰类
 */
public class OilNoodle extends Decorator {
    public OilNoodle(ShaoErKuai obj) {
        super(obj);
        setDescription(" 油条 ");
        setPrice(2.0f);
    }
}


4.客户端测试

/**
 * @author coolsheep
 * @date 2023/4/3 22:37
 * @Describe
 */
public class Client {
    public static void main(String[] args) {
        //需求:点一份白米饵块+热狗+油条
        //点一份白米饵块
        ShaoErKuai order = new WhiteRice();
        System.out.println("order 费用=" + order.cost());
        System.out.println("order 订单描述=" + order.getDescription());
        //order 加一份热狗
        order = new HotDog(order);
        System.out.println("order 加一份热狗,费用="+order.cost());
        System.out.println("order 加一份热狗,订单描述="+order.getDescription());
        order = new OilNoodle(order);
        System.out.println("order 加了一份油条,费用="+order.cost());
        System.out.println("order 加了一份油条,订单描述="+order.getDescription());
    }
}

小结

桥接模式和装饰者模式的区别:

桥接模式是为了实现两个没有关联的维度的东西的自由组合,这里没有关联是指各自拥有自己的属性和方法,没有相同点(使用聚合或者组合)。装饰者模式使用了继承必然是两个种类具有相同的一些属性和方法,它不是为了实现两个维度的自由组合,是为了实现对对象的一层一层又一层包装,调用方法时,每一层包装递归的调用上一层的包装。

​ 这里的包装可以举一个例子,如同月饼的包装,可以包一层,再包一层,再包一层,每一层的包装可以形同也可以不同。装饰者模式将月饼传入装饰类,每包一次就将上一次包好的月饼传入装饰类,进行下一次的包装。