首页 > 技术文章 > 装饰者模式(包装模式)

zhouheblog 2019-10-28 10:51 原文

应用场景:老王是卖豆浆的,现在的豆浆品种不断增多,如下图,可以任意搭配,这种情况如果用继承的方式会形成 类爆炸,并且后期扩展也比较麻烦,这时候就可以用到装置者模式。

 

 

 

代码实现:

1.豆浆属于饮品,创建一个饮品的接口,后期可能还会卖果汁之类的其他饮品

package com.zkxtd.test;

/**
 * 饮品
 * @Author zhouhe
 * @Date 2019/10/28 9:59
 */
public interface Drink {

    //抽象组件
    //价格
    public double money();
    //描述
    public String desc();
}

 

2.创建被装饰者,豆浆,实现饮品Drink接口

package com.zkxtd.test;

/**
 * 豆浆
 * 豆浆属于饮品,所以要实现 Drink 这个接口
 * @Author zhouhe
 * @Date 2019/10/28 10:02
 */

public class Soya implements Drink{

    //被装饰者
    @Override
    public double money() {
        return 5;
    }

    @Override
    public String desc() {
        return "纯豆浆";
    }
}

 

3.创建一个装饰器,它是一个抽象类,实现Drink,返回装饰后的结果

package com.zkxtd.test;

/**
 * 抽象类
 * @Author zhouhe
 * @Date 2019/10/28 10:08
 */
public abstract class Decorator implements Drink{

    //装饰器
    //1.抽象类
    //2.实现抽象组件接口
    //3.持有抽象接口的引用

    //定义私有的饮品接口引用
    private Drink drink;
    public Decorator(Drink drink){
        this.drink = drink;
    }

    /**
     * 重写方法,返回装饰后的结果
     * @return
     */
    @Override
    public double money() {
        return drink.money();
    }

    @Override
    public String desc() {
        return drink.desc();
    }
}

 

4.具体装饰

  红豆

package com.zkxtd.test;

/**
 * 红豆
 * @Author zhouhe
 * @Date 2019/10/28 10:15
 */
public class RedBean extends Decorator{

    /**
     * 在使用ReadBean 的时候需要把 drink 传给父类
     * @param drink
     */
    public RedBean(Drink drink) {
        super(drink);
    }


    /**
     * 重写价格
     * @return
     */
    @Override
    public double money() {
        return super.money()+3.2;
    }

    /**
     * 重写描述
     * @return
     */
    @Override
    public String desc() {
        return super.desc()+"+红豆";
    }
}

  鸡蛋

package com.zkxtd.test;

/**
 * @Author zhouhe
 * @Date 2019/10/28 10:20
 */
public class Egg extends Decorator {

    public Egg(Drink drink) {
        super(drink);
    }

    @Override
    public double money() {
        return super.money()+3.9;
    }

    @Override
    public String desc() {
        return super.desc()+"+鸡蛋";
    }
}

  糖

package com.zkxtd.test;

/**
 * 糖
 * @Author zhouhe
 * @Date 2019/10/28 10:21
 */
public class Sugar extends Decorator {

    public Sugar(Drink drink) {
        super(drink);
    }

    @Override
    public double money() {
        return super.money()+2.1;
    }

    @Override
    public String desc() {
        return super.desc()+"+糖";
    }
}

 

5.测试类

package com.zkxtd.test;

/**
 * @Author zhouhe
 * @Date 2019/10/28 10:24
 */
public class Test {

    public static void main(String[] args) {
        //开始搭配

        //1.创建豆浆对象(豆浆属于饮品,我们返回一个饮品)
        Drink soya = new Soya();
        System.out.println(soya.money());   //打印纯豆浆的信息
        System.out.println(soya.desc());

        //2.往纯豆浆里面加红豆
        //把红豆加到豆浆里面,返回的还是饮品
        Drink redBeanSoya = new RedBean(soya);
        System.out.println(redBeanSoya.money());   //打印加红豆的豆浆的信息
        System.out.println(redBeanSoya.desc());

        //3.向红豆豆浆中加入鸡蛋
        //返回的还是饮品
        Drink erbs = new Egg(redBeanSoya);
        System.out.println(erbs.money());   //打印相关信息
        System.out.println(erbs.desc());
    }
}

 

 

 使用装饰者模式之后,任何的一个具体装饰都可以装饰当前的一个饮品

 

现在如果需要加入一种新的辅料西瓜,那么只要建一个西瓜类,然后进行搭配就可以了

package com.zkxtd.test;

/**
 * 西瓜
 * @Author zhouhe
 * @Date 2019/10/28 10:37
 */
public class Xigua extends Decorator {

    public Xigua(Drink drink) {
        super(drink);
    }

    @Override
    public double money() {
        return super.money()+4.0;
    }

    @Override
    public String desc() {
        return super.desc()+"+西瓜";
    }
}

 

 

 

还可以装饰果汁,果汁也属于饮品

package com.zkxtd.test;

/**
 * 果汁
 * @Author zhouhe
 * @Date 2019/10/28 10:43
 */
public class Fruit implements Drink{
    
    @Override
    public double money() {
        return 6;
    }

    @Override
    public String desc() {
        return "果汁";
    }
}

  在测试类中开始搭配果汁

 

 

推荐阅读