zoukankan      html  css  js  c++  java
  • 设计模式05----装饰者模式

    1. 什么时候使用装饰者模式

    比如有一家店卖饮品,饮品就有不少种,每一种还可以加项,比如给可乐加冰,加糖,兑水什么的,每次加项的价格还不同,就会将代码弄的很繁琐,这种情况下就可以使用装饰者模式来实现. 

    何时使用

    2. 什么是装饰者模式

    上述的例子中,可以以饮品为主体,用不用的各种需求来装饰它,比如有一个可乐对象,那我用一个加冰对象装饰一下,再用加糖对象装饰一下,最后能得到一个加冰加糖可乐,这时候就将原可乐对象扩展,得到了加冰和加糖两种装饰. 
    装饰者模式: 动态地将责任附加到对象上,对扩展功能来说,装饰者比继承更有弹性更灵活(因为子类继承父类扩展功能的前提,是已知要扩展的功能是什么样的,而这是在编译时就要确定的,但是装饰者模式可以实现动态(在运行时)去扩展功能).
    3. 装饰者模式结构

    Decorator:是装饰者的父类,每个装饰者都需要继承这个抽象类(或实现这个接口). 
    ConcreteDecoratorA/B:具体的装饰者,就对应上述例子中的加冰,加糖等. 
    ConcreteComponent:具体的对象,就是上述例子中的可乐. 
    Component:装饰者模式中最顶级的父类,装饰者与被装饰者都是它的子类或实现类才行.
    4. 举例说明

    (1)建立最顶级的父类

    饮品类:所有的被装饰的类都需要继承它.

    package componet;
    
    /**
     * Created by zyf on 2017/3/30.
     * 装饰者模式中最顶级的父类
     */
    public abstract class 饮品 {
        String name;
    
        /**
         * 每个饮品的价格不同,所以讲price方法抽象化<br/>
         * 让每个实现"饮品"类的子类自己决定是多少钱
         * */
        public abstract int price();
    
        /***
         * 得到饮品的名字
         * @return 名字
         */
        public String getName(){
            return name;
        }
    }

    (2)被装饰的可乐Component类

    package componet;
    
    /**
     * Created by zyf on 2017/3/30.
     */
    public class 可乐Component extends 饮品 {
    
        public 可乐Component() {
            //设置name为可乐
            //这个name属性是从饮品类中继承来的
            name = "可乐";
        }
    
        /***
         * 实现父类的抽象方法
         * @return 可乐的价格
         */
        @Override
        public int price() {
            //可乐30块一瓶~
            return 30;
        }
    }

    (3)被装饰的啤酒Component类

    package componet;
    
    /**
     * Created by zyf on 2017/3/30.
     */
    public class 啤酒Component  extends 饮品{
        public 啤酒Component() {
            //设置name为啤酒
            //这个name属性是从饮品类中继承来的
            name = "啤酒";
        }
    
        /***
         * 实现父类的抽象方法
         * @return 啤酒的价格
         */
        @Override
        public int price() {
            //啤酒3块一瓶~
            return 3;
        }
    }

    (4)Decorator类,所有装饰类的父类

    package decorator;
    
    import componet.饮品;
    
    /**
     * Created by zyf on 2017/3/30.
     * 装饰者模式中,所有装饰者的父类
     */
    public abstract class Decorator extends 饮品 {
    
        /***
         * 声明一个饮品引用,准备接受一个饮品对象<br/>
         */
        protected 饮品 yp;
    
        public Decorator(饮品 yp) {
            this.yp = yp;
        }
    
    }

    (5)装饰类:加醋Decorator类

    package decorator;
    
    import componet.饮品;
    
    /**
     * Created by zyf on 2017/3/30.
     */
    public class 加醋Decorator extends Decorator {
    
    
        public 加醋Decorator(饮品 yp) {
            super(yp);
        }
    
        public void addVinegar(){
            System.out.println("还要加醋,加完了");
        }
    
        /***
         * 那么加醋后的价格应该是多少呢?<br/>
         * 应该是加粗的价格加饮品的价格
         * @return 加醋五块
         */
        @Override
        public int price() {
            return 5 + yp.price();
        }
    
        /***
         * 再复写一个名字的方法<br/>
         * 现在已经不是单纯的饮品了
         * @return
         */
        @Override
        public String getName() {
            //在这里加个醋
            addVinegar();
            return "加醋的" + yp.getName();
        }
    }

    (6)装饰类:兑水Decorator类

    package decorator;
    
    import componet.饮品;
    
    /**
     * Created by zyf on 2017/3/30.
     */
    public class 兑水Decorator extends Decorator {
    
    
        public 兑水Decorator(饮品 yp) {
            super(yp);
        }
    
        public void 兑水(){
            System.out.println("饮料兑水....尴尬不老铁...");
        }
    
        /***
         * 那么兑水后的价格应该是多少呢?<br/>
         * 应该是兑水的价格加饮品的价格
         * @return 兑水2块
         */
        @Override
        public int price() {
            return 2 + yp.price();
        }
    
        /***
         * 再复写一个名字的方法<br/>
         * 现在已经不是单纯的饮品了
         * @return
         */
        @Override
        public String getName() {
            兑水();
            return "兑水了的" + yp.getName();
        }
    }

    (7)测试类

    public static void main(String[] args) {
      //可以看到,我们操作的引用一直是这个yp
      //但是这个引用指向的对象已经换了好几次了
      //这就是为什么装饰类也要是饮品类的子类,因为只有这样,装饰类与被装饰类才能被当做同一个类型使用(通过接口或继承实现)
        饮品 yp = new 可乐Component();
        yp = new 兑水Decorator(yp);
        yp = new 加醋Decorator(yp);
    //  上面与下面这一行是一样的,是不是和IO流很像?
    //  yp = new 加醋Decorator(new 兑水Decorator(new 可乐Component()));
    
        System.out.println("饮品名:" + yp.getName() + "---价格:" + yp.price());
    }

    note:我的理解是:

    实际上装饰者就先拿到被装饰者的对象(比如饮品--可乐),这个对象可能有自己的一些方法可以被调用(比如getPrice);
    我们创建装饰者的对象,然后就可以给被装饰者进行装饰,比如重写被装饰者对象的方法(getPrice),因为顶级父类是一样的(因为可乐和Decortor都继承自饮品这个顶级父类),因此可以重写。

     那映射到IO流中,比如FileInputStream作为被装饰类,而BufferedInputStream作为具体的装饰类(它继承自FIlterInputStream类),同时FileInputStream和FilterInputStream都继承自InputStream这个顶级父类。因此BufferedInputStream可以装饰FileInputStream类,比如重写了InputStream顶级父类的read方法,还添加了一些装饰方法,比如mark和reset方法。

    参考文献:https://blog.csdn.net/android_zyf/article/details/68343953

  • 相关阅读:
    八数码(BFS)
    食物链(并查集)
    最大异或对(Trie)
    解决espeak编译的一些问题
    AcWing 3779. 相等的和
    AcWing 3775. 数组补全(环图)
    AcWing 3728. 城市通电(最小生成树)
    AcWing 3727. 乘方相乘(进位制)
    tarjan
    LC刷题简要记录
  • 原文地址:https://www.cnblogs.com/Hermioner/p/10021049.html
Copyright © 2011-2022 走看看