设计模式之装饰器模式

作者: 先生zeng | 来源:发表于2019-06-23 22:56 被阅读0次

    在阎宏博士的《JAVA与模式》的书中,对装饰器模式的描述如下:
    装饰模式又名包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

    装饰器可以对客户端以透明的方式来动态的给对象附加上更多的责任。客户端在装饰前和装饰后不会有什么感觉不同。装饰模式的类图如下: image.png

      在装饰模式中的角色有:

    ●  抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。
      ●  具体构件(ConcreteComponent)角色:定义一个将要接收附加责任的类。

    ●  装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

    ●  具体装饰(ConcreteDecorator)角色:负责给构件对象“贴上”附加的责任。

    这一个例子比较不好理解。所以我不打算用这本书里的例子来举例。用Head First 设计模式这本书中的咖啡的例子来详解:

    为星巴兹咖啡连锁店设计饮料菜单,咖啡可加的调料有豆浆、牛奶、摩卡等。可在咖啡的基础上加入不同的调料,星巴兹会根据所加的调料收取不同的费用,要注意到,以后可能有新的调料被加入进来供顾客选择。并且本店现有DarkRoast(深焙)、HouseBlend(综合)、Decaf(低咖啡因)及Espresso(浓咖啡)四种类型的咖啡,而且以后可能会添加新的咖啡种类。

    使用装饰者模式,四种咖啡为具体组件,调料为具体装饰者。 image.png

    代码实现

    1.抽象组件角色:Beverage(饮料)

    public abstract class Beverage {
        String description = "Unknown Beverage";
      
        public String getDescription() {
            return description;
        }
        public abstract double cost();
    }
    

    2.具体组件角色:四种类型的咖啡

    (1)DarkRoast
    public class DarkRoast extends Beverage {
        public DarkRoast() {
            description = "Dark Roast Coffee";
        }
     
        public double cost() {
            return .99;
        }
    }
    
    (2)Decaf
    public class Decaf extends Beverage {
        public Decaf() {
            description = "Decaf Coffee";
        }
     
        public double cost() {
            return 1.05;
        }
    }
    
    (3)Espresso
    public class Espresso extends Beverage {
      
        public Espresso() {
            description = "Espresso";
        }
      
        public double cost() {
            return 1.99;
        }
    }
    
    (4)HouseBlend
    public class HouseBlend extends Beverage {
        public HouseBlend() {
            description = "House Blend Coffee";
        }
     
        public double cost() {
            return .89;
        }
    }
    

    3.装饰者角色:CondimentDecorator

    public abstract class CondimentDecorator extends Beverage {
        public abstract String getDescription();
    }
    

    4.具体装饰者:各种调料

    (1)Milk
    public class Milk extends CondimentDecorator {
        Beverage beverage;
     
        public Milk(Beverage beverage) {
            this.beverage = beverage;
        }
     
        public String getDescription() {
            return beverage.getDescription() + ", Milk";
        }
     
        public double cost() {
            return .10 + beverage.cost();
        }
    }
    
    (2)Mocha
    public class Mocha extends CondimentDecorator {
        Beverage beverage;
     
        public Mocha(Beverage beverage) {
            this.beverage = beverage;
        }
     
        public String getDescription() {
            return beverage.getDescription() + ", Mocha";
        }
     
        public double cost() {
            return .20 + beverage.cost();
        }
    }
    
    (3)Whip
    public class Whip extends CondimentDecorator {
        Beverage beverage;
     
        public Whip(Beverage beverage) {
            this.beverage = beverage;
        }
     
        public String getDescription() {
            return beverage.getDescription() + ", Whip";
        }
     
        public double cost() {
            return .10 + beverage.cost();
        }
    }
    
    (4)Soy
    public class Soy extends CondimentDecorator {
        Beverage beverage;
     
        public Soy(Beverage beverage) {
            this.beverage = beverage;
        }
     
        public String getDescription() {
            return beverage.getDescription() + ", Soy";
        }
     
        public double cost() {
            return .15 + beverage.cost();
        }
    }
    

    5.测试

    /** 
    * //为了某个实现类在不修改原始类的基础上进行动态地覆盖或者增加方法
            //该实现保持跟原有类的层级关系
            //采用装饰模式
            //装饰器模式实际上一种非常特殊的适配器模式
    
            //虽然 DataInputStream 功能更强大
            //DataInputStream 同样要实现InputStream
            InputStream in = null;
            FilterInputStream fis = new DataInputStream(in);
    */
    public class StarbuzzCoffee
    {
     
        public static void main(String args[])
        {
             //原来的功能依旧对外开放,依旧保留
            //新的功能同样的也可以使用
            Beverage beverage = new Espresso();
            System.out.println(beverage.getDescription() + " $" + beverage.cost());
     
            Beverage beverage2 = new DarkRoast();
            beverage2 = new Mocha(beverage2);
            beverage2 = new Mocha(beverage2);
            beverage2 = new Whip(beverage2);
            System.out.println(beverage2.getDescription() + " $" + beverage2.cost());
     
            Beverage beverage3 = new HouseBlend();
            beverage3 = new Soy(beverage3);
            beverage3 = new Mocha(beverage3);
            beverage3 = new Whip(beverage3);
            System.out.println(beverage3.getDescription() + " $" + beverage3.cost());
        }
    }
    

    这是整体类图


    image.png

    然后这是我对装饰器和适配器两种不同设计模式收集到的一些区别:


    image.png

    在什么情况下使用?
    1.需要扩展一个类的功能,给一个类附加责任。
    2.动态的给一个对象附加功能,这些功能可以动态的撤销。
    3.需要增加由一些基本功能的排列组合而产生的非常大量的功能。

    装饰模式的优点

    (1)装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。装饰模式允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。

    (2)通过使用不同的具体装饰类以及这些装饰类的排列组合,设计师可以创造出很多不同行为的组合。

    装饰模式的缺点

    由于使用装饰模式,可以比使用继承关系需要较少数目的类。使用较少的类,当然使设计比较易于进行。但是,在另一方面,使用装饰模式会产生比使用继承关系更多的对象。更多的对象会使得查错变得困难,特别是这些对象看上去都很相像。

    设计模式在JAVA I/O库中的应用。
      装饰模式在Java语言中的最著名的应用莫过于Java I/O标准库的设计了。

    由于Java I/O库需要很多性能的各种组合,如果这些性能都是用继承的方法实现的,那么每一种组合都需要一个类,这样就会造成大量性能重复的类出现。而如果采用装饰模式,那么类的数目就会大大减少,性能的重复也可以减至最少。因此装饰模式是Java I/O库的基本模式。

    以后会专门在JAVA I/O库中运用到了哪些设计模式进行深入的源码阅读探究。

    感谢您阅读我的文章,如果满意可以帮我点赞,谢谢哈。
    如果对文章部分还有什么见解或者疑惑,可以私信评论我,欢迎技术讨论。如果需要获取完整的文件资源,可以加我微信z985085305,获取我整理的全套笔记。
    思想的碰撞最能促进技术的进步哦。

    相关文章

      网友评论

        本文标题:设计模式之装饰器模式

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