美文网首页设计模式笔记
设计模式笔记(三): 装饰者模式

设计模式笔记(三): 装饰者模式

作者: yeonon | 来源:发表于2018-07-22 13:34 被阅读13次

    有时候我们希望对某个类做一些扩展,该如何做呢?

    初次尝试

    最简单的方式是直接继承该类,然后在子类中扩展功能即可,如下图所示:


    类爆炸

    在上述图中,只是想在Beverage这个类的基础上加上几种调料,如果直接继承扩展的话,也许就需要非常多的子类才能使得体系完整而且如果某种调料的价格上调了,是不是必须到每个和该调料的相关类里修改价格才行?如果调料的种类越多,子类就越多(想象一下排列组合)。显然,这种方式虽然简单直接,但是不可取。

    再次尝试

    可以尝试将调料作为字段放入基类中,然后几个子类对调料的价格做具体计算就行。如下图所示:


    第二次尝试

    确实,这种方式较第一种好了很多(至少子类没有那么多了),但这也有一个很严重的问题,那就是子类很有可能不需要某种调料,尽管子类可以不计算某种调料的价格,但这不符合面向对象的思想(回想一下被人诟病的Vector类的设计)。

    用组合可以吗?将这些调料抽离出来作为单独的类,他们都实现同一个接口,然后在子类中新增一个调料的集合并添加增加或减少调料的API,之后便可以动态调整调料了。代码不难写,可以尝试一下,感觉确实可行,我认为在这个场景下,没什么问题,望指正。

    装饰者模式

    下面使用装饰者模式来实现目标。代码如下:

    public abstract class Beverage {
    
        private String desc = "Beverage";
    
        public String getDesc() {
            return desc;
        }
    
        public void setDesc(String desc) {
            this.desc = desc;
        }
    
        public abstract double cost();
    }
    
    public abstract class CondimentDecorator extends Beverage {
    
        public abstract String getDesc();
    }
    
    public class HouseBlend extends Beverage {
    
        public HouseBlend() {
            setDesc("House Blend");
        }
    
        @Override
        public double cost() {
            return 0.89;
        }
    }
    
    public class Milk extends CondimentDecorator {
    
        private Beverage beverage;
    
        public Milk(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public String getDesc() {
            return beverage.getDesc() + ", Milk";
        }
    
        @Override
        public double cost() {
            return 0.50 + beverage.cost();
        }
    }
    public class Mocha extends CondimentDecorator {
    
        private Beverage beverage;
    
        public Mocha(Beverage beverage) {
            this.beverage = beverage;
        }
    
        @Override
        public String getDesc() {
            return beverage.getDesc() + ", Mocha";
        }
    
        @Override
        public double cost() {
            return 0.20 + beverage.cost();
        }
    }
    

    从代码中不难看出,利用上述的体系,我们可以任意的给类做层层包装,就如下图所示:


    层层包装

    每一层(除最内层)的类委托内层的类做处理,内层的处理完毕后再添加本层的处理,最终得到我能想要的结果(在本文描述的场景中即计算加入调料后的总价格)

    下面是测试类:

    public class Main {
    
        public static void main(String[] args) {
            Beverage beverage1 = new HouseBlend();
            System.out.println(beverage1.getDesc() + " $" + beverage1.cost());
    
            Beverage beverage2 = new HouseBlend();
            beverage2 = new Milk(beverage2);
            beverage2 = new Mocha(beverage2);
            System.out.println(beverage2.getDesc() + " $" + beverage2.cost());
        }
    }
    

    运行该测试类可以看到如下结果:

    House Blend $0.89
    House Blend, Milk, Mocha $1.59
    

    House Blend不加调料的价格是0.89, 加入了Milk和Mocha后的价格是1.59。如果我们想更改调料的价格,只需到表达该调料的类中修改其价格即可。

    小结

    现在我们可以给出装饰者模式的定义了:装饰者模式动态的将责任附加到对象上,若要扩展功能,装饰者提供了比继承更有弹性的替代方案。

    装饰者的最主要的目的就是添加行为到被包装的类上,本质上其实就是封装,故该模式很符合开闭原则

    装饰者模式也有缺点,我们有可能要添加很多代码相差不多的小类,这可能会对维护造成麻烦。

    装饰者模式在JDK中也有使用,例如Java IO。inputstream,OutputStream体系的类,仔细看看源码,可以看出这个体系确实使用了装饰者模式。

    本系列文章参考书籍是《Head First 设计模式》,文中代码示例出自书中。

    相关文章

      网友评论

        本文标题:设计模式笔记(三): 装饰者模式

        本文链接:https://www.haomeiwen.com/subject/ydylmftx.html