【193期】如何利用装饰者模式在不改变原有对象的基础上扩展功能

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 【193期】如何利用装饰者模式在不改变原有对象的基础上扩展功能

点击上方“Java面试题精选”,关注公众号

面试刷图,查缺补漏

号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅

阶段汇总集合:

阅读目录

  • 什么是装饰者模式
  • 普通示例
  • 装饰者模式示例
  • 类图关系
  • 装饰者模式使用场景
  • 装饰者模式优点
  • 装饰者模式缺点
  • 什么是装饰者模式

    装饰者模式(DecoratorPattern)是指在不改变原有对象的基础之上,将功能附加到对
    象上,提供了比继承更有弹性的替代方案(扩展原有对象的功能),属于结构型模式。

    装饰者模式在生活中也有很多形象的例子,比如说给蛋糕加上一些水果,给披萨加上榴莲,或者说给烧饼加上鸡蛋火腿之类等等。

    下面我们就以给蛋糕加上水果为例来看看如果不用装饰者模式要怎么实现,如果使用装饰者模式又要怎么实现,对比之后就知道装饰者模式的优势了。

    普通示例

    新建一个普通的蛋糕类:

    
    package com.zwx.design.pattern.decorator.common;
    
    import java.math.BigDecimal;
    
    public class Cake {
    
        public String getCakeMsg(){
            return "我是一个8英寸的普通蛋糕";
        }
    
        public BigDecimal getPrice(){
            return new BigDecimal("68");
        }
    }
    

    这时候,我们需要给蛋糕加点芒果,那可以再新建一个类去继承普通Cake类,然后重写其中的方法:

    
    package com.zwx.design.pattern.decorator.common;
    
    import java.math.BigDecimal;
    
    public class CakeAddMango extends Cake {
        @Override
        public String getCakeMsg() {
            return super.getCakeMsg() + "+1个芒果";
        }
    
        @Override
        public BigDecimal getPrice() {
            return super.getPrice().add(new BigDecimal("10"));
        }
    }
    

    这时候,如果不仅仅加芒果,还要再加点葡萄,那么可以再写一个类,继承CakeAddMango,然后重写其中的方法。

    
    package com.zwx.design.pattern.decorator.common;
    
    import java.math.BigDecimal;
    
    public class CakeAddMangoAndGrape extends CakeAddMango {
        @Override
        public String getCakeMsg() {
            return super.getCakeMsg() + "+1个葡萄";
        }
    
        @Override
        public BigDecimal getPrice() {
            return super.getPrice().add(new BigDecimal("5"));
        }
    }
    

    写个测试类测一下:

    
    package com.zwx.design.pattern.decorator.common;
    
    public class TestCake {
        public static void main(String[] args) {
            //普通蛋糕
            Cake cake = new Cake();
            System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());
    
            //加芒果蛋糕
            CakeAddMango cakeAddMango = new CakeAddMango();
            System.out.println(cakeAddMango.getCakeMsg() + ",价格:" + cakeAddMango.getPrice());
    
            //加芒果和葡萄蛋糕
            CakeAddMangoAndGrape cakeAddMangoAndGrape = new CakeAddMangoAndGrape();
            System.out.println(cakeAddMangoAndGrape.getCakeMsg() + ",价格:" + cakeAddMangoAndGrape.getPrice());
        }
    }
    

    输出如下结果:

    
    我是一个8英寸的普通蛋糕,价格:68
    我是一个8英寸的普通蛋糕+1个芒果,价格:78
    我是一个8英寸的普通蛋糕+1个芒果+1个葡萄,价格:83
    

    看起来挺好的,能实现,但是假如我们加2个芒果呢?或者是我要加2个普通呢,或者说芒果和葡萄要组合,数量不一定,那利用现有的类是无法实现的,只能不断加类去重写,如果业务变更频繁,修改起来会是致命的。

    正因为普通的实现方法有这种缺陷,才有了装饰者模式,接下来我们来看看同一个需求利用装饰者模式是怎么实现的吧。

    往期面试题:

    装饰者模式示例

    1、新建一个蛋糕的抽象类:

    
    package com.zwx.design.pattern.decorator;
    
    import java.math.BigDecimal;
    
    public abstract class Cake {
        public abstract String getCakeMsg();
    
        public abstract BigDecimal getPrice();
    }
    

    2、然后新建一个普通蛋糕的类:

    
    package com.zwx.design.pattern.decorator;
    
    import java.math.BigDecimal;
    
    public class BaseCake extends Cake {
        @Override
        public String getCakeMsg() {
            return "我是一个8英寸的普通蛋糕";
        }
    
        @Override
        public BigDecimal getPrice() {
            return new BigDecimal("68");
        }
    }
    

    3、新建一个蛋糕的装饰器类,内部持有蛋糕Cake对象,这个就是扩展的关键:

    
    package com.zwx.design.pattern.decorator;
    
    import java.math.BigDecimal;
    
    public abstract class CakeDecorator extends Cake{
        private Cake cake;
    
        public CakeDecorator(Cake cake) {
            this.cake = cake;
        }
    
        @Override
        public String getCakeMsg() {
            return this.cake.getCakeMsg();
        }
    
        @Override
        public BigDecimal getPrice() {
            return this.cake.getPrice();
        }
    }
    

    4、新建一个芒果蛋糕的装饰器类继承CakeDecorator类:

    
    package com.zwx.design.pattern.decorator;
    
    import java.math.BigDecimal;
    
    public class CakeAddGrapeDecorator extends CakeDecorator {
        public CakeAddGrapeDecorator(Cake cake) {
            super(cake);
        }
    
        @Override
        public String getCakeMsg() {
            return super.getCakeMsg() + "+1个葡萄";
        }
    
        @Override
        public BigDecimal getPrice() {
            return super.getPrice().add(new BigDecimal("5"));
        }
    }
    

    5、新建一个葡萄的装饰器类继承CakeDecorator类:

    
    package com.zwx.design.pattern.decorator;
    
    import java.math.BigDecimal;
    
    public class CakeAddMangoDecorator extends CakeDecorator {
    
        public CakeAddMangoDecorator(Cake cake) {
            super(cake);
        }
    
        @Override
        public String getCakeMsg() {
            return super.getCakeMsg() + "+1个芒果";
        }
    
        @Override
        public BigDecimal getPrice() {
            return super.getPrice().add(new BigDecimal("10"));
        }
    }
    

    6、最后写一个测试类测试一下:

    
    package com.zwx.design.pattern.decorator;
    
    public class TestCakeDecorator {
        public static void main(String[] args) {
            Cake cake = null;
            //普通蛋糕
            cake = new BaseCake();
            System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());
            //加一个芒果
            cake = new CakeAddMangoDecorator(cake);
            System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());
            //加一个葡萄
            cake = new CakeAddGrapeDecorator(cake);
            System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());
            //再加一个芒果
            cake = new CakeAddMangoDecorator(cake);
            System.out.println(cake.getCakeMsg() + ",价格:" + cake.getPrice());
        }
    }
    

    输出结果为:

    
    我是一个8英寸的普通蛋糕,价格:68
    我是一个8英寸的普通蛋糕+1个芒果,价格:78
    我是一个8英寸的普通蛋糕+1个芒果+1个葡萄,价格:83
    我是一个8英寸的普通蛋糕+1个芒果+1个葡萄+1个芒果,价格:93
    

    我们可以看到,使用装饰者模式之后,扩展之前的功能变得极为方便,可以根据现有的装饰器进行任意组合。

    类图关系

    看一下类图,首先是一个基础抽象类定义了基本方法,然后是基础实现和基础装饰器继承并重写抽象类中的方法:
    【193期】如何利用装饰者模式在不改变原有对象的基础上扩展功能

    装饰者模式使用场景

  • 1、用于扩展一个类的功能或给一个类添加附加职责。
  • 2、动态的给一个对象添加功能,这些功能可以再动态的撤销。
  • 注:MyBatis中的二级缓存就是用了装饰者模式来进行动态扩展,感兴趣的可以去了解下。

    往期面试题:

    装饰者模式优点

  • 1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象 扩展功能,即插即用。
  • 2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
  • 3、装饰者完全遵守开闭原则。
  • 装饰者模式缺点

  • 1、会出现更多的代码,更多的类,增加程序复杂性。
  • 2、动态装饰以及多层装饰时会更加复杂。
  • END

    来源:blog.csdn.net/zwx900102/article/details/107740212

    十期推荐

    与其在网上拼命找题?** 不如马上关注我们~**

    【193期】如何利用装饰者模式在不改变原有对象的基础上扩展功能

    原文始发于微信公众号(Java面试题精选):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 【193期】如何利用装饰者模式在不改变原有对象的基础上扩展功能


     上一篇
    【192期】面试官——线程池中多余的线程是如何回收的? 【192期】面试官——线程池中多余的线程是如何回收的?
    点击上方“Java面试题精选”,关注公众号 面试刷图,查缺补漏 号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅 阶段汇总集合: 最近阅读了JDK线程池ThreadPoolExecutor的源码,对线程池执行
    2021-04-05
    下一篇 
    【194期】Redis——第三方jar没有封装的命令我们该怎么执行? 【194期】Redis——第三方jar没有封装的命令我们该怎么执行?
    点击上方“Java面试题精选”,关注公众号 面试刷图,查缺补漏 号外:往期面试题,10篇为一个单位归置到本公众号菜单栏-面试题,有需要的欢迎翻阅 阶段汇总集合: 今天对redis的进阶操作给大家介绍一下,以及对于jedis和redisTem
    2021-04-05