• java常用设计模式(四)装饰者模式


      设计模式第四篇,装饰者模式,大家多多指教。

     简介

      装饰者模式是动态的将责任附加到对象上(引自《Head First设计模式》)。这里的重点在于动态这两个字,我们都知道继承的实现的方式,它是是类编译的时候就去加载文件,属于一种静态的附加,而我们要实现动态的附加就不能单纯的通过继承来实现。在这种背景下,装饰者模式就应运而生了。装饰者模式的实现:首先所有的类都有一个共同的抽象,这个抽象可以是一个抽象类,也可以是一个接口,所有的类该抽象的子类或者实现。语言描述比较抽象,下面我们通过一个例子来描述该模式。

     例子

      我们举一个吃火锅的例子,根据装饰者模式的特点:

      首先我们要有个共同的抽象,这里我们通过接口来实现,这个接口我们称之为锅底。  

    /*
     * 锅底
     */
    public interface Hotpot {
        /** 描述 */
        String getName();
        /** 价格 */    
        Double getPrice();
    
    }

      第二步,我们设计具体的锅底类,我们分为酱香锅、香辣锅和清汤锅,这是底锅,每笔单都必须有且只有一个。

    public class JamHotpot implements Hotpot{
        @Override
        public String getName() {
            return "酱香锅";
        }
    
        @Override
        public Double getPrice() {
            return 4.0;
        }
    }
    
    public class SpicyHotpot implements Hotpot{
        @Override
        public String getName() {
            return "香辣锅";
        }
    
        @Override
        public Double getPrice() {
            return 4.0;
        }
    }
    
    public class ClearSoupHotpot implements Hotpot{
        @Override
        public String getName() {
            return "清汤锅";
        }
    
        @Override
        public Double getPrice() {
            return 3.0;
        }
    }

      第三步,我们新增一个配菜接口,继承共同的接口锅底,这个接口用来作为所有配菜的父级,该接口可以拓展所以配菜的共有属性

    /*
     * 配菜
     */
    public interface SideDish extends Hotpot {
        //可拓展配菜的公共属性
    }

      第四步,我们需要新增具体的配菜类,这里的配菜类我们注意到比锅底类多了成员变量和构造方法,而且getName和getPrice方法多了加了传入参数,这个就是为了动态的去附加功能,具体怎么实现参考最后一步。

    /*
     * 牛肉
     */
    public class Beef implements SideDish {
    
        private Hotpot hotpot;
    
        public Beef(Hotpot hotpot) {
            this.hotpot = hotpot;
        }
    
        @Override
        public String getName() {
            return hotpot.getName() + ",牛肉";
        }
    
        @Override
        public Double getPrice() {
            return hotpot.getPrice() + 38.0;
        }
    }
    
    /*
     * 生菜
     */
    public class Lettuce implements SideDish {
    
        private Hotpot hotpot;
    
        public Lettuce(Hotpot hotpot) {
            this.hotpot = hotpot;
        }
    
        @Override
        public String getName() {
            return hotpot.getName() + ",生菜";
        }
    
        @Override
        public Double getPrice() {
            return hotpot.getPrice() + 8.0;
        }
    }

      最后我们测试:

    public class DecoratorTest {
    
        public static void main(String[] args) {
            System.out.println("=======酱香锅底加牛肉");
            Hotpot hotpot = new JamHotpot();
            hotpot = new Beef(hotpot);
            hotpot = new Lettuce(hotpot);
            System.out.println("===火锅的价格为:" + hotpot.getPrice() + "元");
            //如果我们需要更多的配菜参考上面的方式去添加更多的配菜  
        }
    }

     总结

      如上面所描写的那样,我只需要前台传递给我们所点的锅底和配菜,我们后台去动态的生成一个新的火锅对象。这么设计的好处的拓展极其方便,就算这个火锅店加了新的锅底,或者是配菜,对我们之前的代码是没有任何影响的,这就是所谓的开闭原则(对拓展开放,对修改关闭),后面我们会讲到设计模式相应的原则。除了上面的好处之外,装饰者模式也有他本身不能忽视的问题,那就是每次生成一笔订单都会生成很多小对象,如果该笔单对象很多的话,对我们的内存是个不小的考验,而且过多的小类会让我们的系统变得很复杂,让人很难以理解。所以装饰器模式有利有弊(更多的是利大于弊),我们可以好好思考装饰者模式应用场景,然后进一步掌握这种设计思想。

     番外

      想了解装饰者模式的更多详细信息可以通过jdk源码或者spring源码去了解。jdk中IO操作就有通过装饰者模式实现的,FilterInputStream和它的实现就是一个装饰者模式,同样的在spring源码中,凡事以Decorator结尾的类都是实现的装饰者模式,当然不是所有的装饰器模式都是以Decorator结尾的。

  • 相关阅读:
    gif&png&jpg&webp
    设计点滴&css效果点滴
    backbone点滴
    js自己总结的小东西(打印出来方便学习)
    nodejs点滴
    js类型
    mongo学亮的分享
    npm package.json中的dependencies和devDependencies的区别
    161130、Dubbo+SpringMVC工程创建详解
    161129、详解5种跨域方式及其原理
  • 原文地址:https://www.cnblogs.com/blue-and-white/p/11078991.html
Copyright © 2020-2023  润新知