天天看点

设计模式-装饰者模式-Decorator_Pattern

背景: 如图一所示,抽象基类组件,其他组件的具体体现类继承自抽象类Component。现在有这样的需求,以methodA()为例,我们需要扩展每一个具体类中methodA()的行为,而且扩展形式多种多样,比如说有时可能是将methodA()中返回值加上某个数,有时可能是减去某个数,有时可能是乘以某个数…那么请思考我们怎样设计编码才更为合理?

设计模式-装饰者模式-Decorator_Pattern

图一

背景具体化: 如图二所示 ,抽象基类组件为火锅,describe()抽象方法用来描述是什么类型火锅,由子类实现,cost()抽象方法用来计算火锅价格,由子类实现。现在基本的火锅我们有了,可以开火了…等等,我们总不能只喝汤底吧。我们得来点肉、菜、鱼、虾…火锅的配菜可能有上百种,看看我们如何通过以火锅底料为基础,进行扩展成一锅美味的火锅。首先是describe()方法,我们是通过字符串拼接的形式来展示一个完整的火锅描述,然后cost()通过相加各个单品配菜价格最后加上火锅底料得到火锅总价。

设计模式-装饰者模式-Decorator_Pattern

基于以上背景与原型思路,扩展的尝试开始了:java中使用继承可以很方便的扩展父类的方法,我们可以在编译期去改变其行为,所以我们试试继承是否可以解决我们的问题。先预设如下几种火锅料,固定锅底+至少一种食材,看图下至少15种组合,那就意味着使用继承有15个子类,每换个火锅底料,改动其中一个材料或者添加一种材料,在硬编码情况下,每种情况便是一个子类,类多到new都让人头疼不已,显然这是一条不归路。

设计模式-装饰者模式-Decorator_Pattern

装饰者模式解决问题

设计模式-装饰者模式-Decorator_Pattern

如上图所示,如果客人想要麻辣红油火锅,那就用麻辣红油来装饰火锅,再点鱿鱼就拿鱿鱼来装饰一遍,再点丸子再那丸子装饰一遍,再点两份生菜就拿生菜来装饰两遍。。。如此一来,同样是硬编码,但实体类却大大的减少了,而且也 灵活得多,点什么配菜就拿什么类来装饰,点几份就装饰几次。如果使用继承得话那么客人配菜要多份将没法处理。

装饰者理论类图

设计模式-装饰者模式-Decorator_Pattern

装饰者模式说明:动态地将责任附件到对象上,若要扩展功能,装饰者提供了比继承更有弹性得替代方案

以下为最终解决方案类图:

设计模式-装饰者模式-Decorator_Pattern
/**
 * 基本抽象组件
 */
public abstract class HotPot {
    /**
     * 描述
     * @return
     */
    abstract String describe();

    /**
     * 统计
     * @return
     */
    abstract double cost ();
}
/**
 *被装饰者--具体组件--麻辣红油火锅
 */
public class SpicyHotPot extends HotPot {
    /**
     * 火锅描述
     * @return
     */
    @Override
    String describe() {
        return "麻辣红油火锅";
    }

    /**
     * 底料
     * @return
     */
    @Override
    double cost() {
        return 32;
    }
}


/**
 * 装饰者组件
 */
public abstract class SideDish extends HotPot {
}


/**
 * 装饰者--牛肉
 */
public class Beef extends SideDish {
    private  HotPot hotPot;

    public Beef(HotPot hotPot) {
        this.hotPot = hotPot;
    }

    @Override
    String describe() {
        return hotPot.describe()+"+牛肉";
    }

    @Override
    double cost() {
        return hotPot.cost()+168;
    }
}

/**
 * 装饰者--生菜
 */
public class Lettuce extends SideDish {
    private HotPot hotPot;

    public Lettuce(HotPot hotPot) {
        this.hotPot = hotPot;
    }

    @Override
    String describe() {
        return hotPot.describe()+"+生菜";
    }

    @Override
    double cost() {
        return hotPot.cost()+13;
    }
}

/**
 * 跑测试
 */
public class Test {
    public static void main(String[] args) {
        HotPot hotPot= new SpicyHotPot();
         hotPot = new Beef(hotPot);
         hotPot = new Lettuce(hotPot);
        System.out.println(hotPot.describe());
        System.out.println(hotPot.cost());
    }
}
           
代码输出结果:麻辣红油火锅+牛肉+生菜
213.0
           

结语: 上述代码简单的展示了在不破环原有代码的封装的情况下,通过装饰者模式动态的去扩展基本组件功能。虽然装饰者模式看着不错,但也同样有它的弊端,本文通过火锅来举例也有意放大问题,突出矛盾,虽然没有简单继承的子类多,但每个配菜都是一个装饰者,按火锅的上百个配菜也是有着上百个装饰类了,容易产生错误,维护也很麻烦。

继续阅读