zoukankan      html  css  js  c++  java
  • 设计模式(六)之装饰器模式(Decorator Pattern)深入浅出

    内容定位:有重构项目需求的人群一定要掌握装饰者模式。

    装饰者模式的定义:

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

    装饰者模式的适用场景:

    • 用于扩展一个类的功能或给一个类添加附加职责。
    • 动态的给一个对象添加功能,这些功能可以再动态的撤销。

    装饰者模式源码案例:

    • JDK源码中BuffteredReader、LineNumberReader装饰Reader抽象类;FileInputStream、FilterInputStream、ByteArrayInputStream、DataInputStream、BufferedInputStream装饰的是InputStream接口,实现可扩展

    •  Spring源码中TransactionAwareCacheDecorator装饰Cache接口,主要用来处理事务缓存

     

    •  SpringMvc源码中HttpHeadResponseDecorator装饰ServerHttpResponse接口,这个接口负责委派

     去父类中查看

    •  Mybatis源码中对org.apache.ibatis.cache.Cache接口进行包装

    包装类有:

     生活场景案例

    案例:我们在买煎饼时需要添加鸡蛋、香肠、菜....等等

    (一)首先不用装饰者实现,通过继承实现一下

    煎饼类

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:18
     * @description: 煎饼类
     */
    public class Battercake {
    
        public String getMsg(){
            return "煎饼";
        }
    
    
        public int price(){
            return 5;
        }
    }

    煎饼加一个鸡蛋

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:20
     * @description: 煎饼加一个鸡蛋
     */
    public class BattercakeWithEgg extends Battercake {
    
        @Override
        public String getMsg() {
            return super.getMsg() + " +1个鸡蛋";
        }
    
        /**
         * 加一个鸡蛋加2元
         */
        @Override
        public int price() {
            return super.price() + 2;
        }
    }

    煎饼加一个香肠

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:22
     * @description:
     */
    public class BattercakeWithEggAndSausage extends BattercakeWithEgg {
    
        @Override
        public String getMsg() {
            return super.getMsg() + " +1个香肠";
        }
    
        /**
         * 1个香肠价格加2元
         */
        @Override
        public int price() {
            return super.price() + 2;
        }
    }

    测试

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:26
     * @description:
     */
    public class BattercakeTest {
    
        public static void main(String[] args) {
            Battercake battercake = new Battercake();
            System.out.println(battercake.getMsg() + ",总价格:" + battercake.price());
    
            Battercake battercakeWithEgg = new BattercakeWithEgg();
            System.out.println(battercakeWithEgg.getMsg() + ",总价格:" + battercakeWithEgg.price());
            Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
            System.out.println(battercakeWithEggAndSausage.getMsg() + ",总价格:" + battercakeWithEggAndSausage.price());
    
        }
    }

    类结构图

    我们会发现,如果需求频繁变动,比如加两个鸡蛋就需要不停的创建类,然后继承煎饼类。

    (二)下面用装饰者模式实现

    煎饼抽象类

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:44
     * @description: 煎饼抽象类
     */
    public abstract class Battercake {
    
        protected abstract String getMsg();
    
    
        protected abstract int price();
    }

    基础煎饼类

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:45
     * @description: 基础煎饼(什么也不加)
     */
    public class BaseBattercake extends Battercake {
    
        @Override
        public String getMsg(){
            return "煎饼";
        }
    
        @Override
        public int price(){
            return 5;
        }
    }

    装饰器抽象类(可有可无)

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:46
     * @description: 包装抽象类,继承煎饼抽象类,再怎么包装也是煎饼
     */
    public abstract class BattercakeDecorator extends Battercake {
    
    //    静态代理,委派模式:将修改的权力交给了装饰器
        private Battercake battercake;
    
        public BattercakeDecorator(Battercake battercake) {
            this.battercake = battercake;
        }
    
        @Override
        protected String getMsg() {
            return this.battercake.getMsg();
        }
    
        @Override
        protected int price() {
            return this.battercake.price();
        }
    }

    加鸡蛋的装饰器

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:50
     * @description: 鸡蛋包装类
     */
    public class EggDecorator extends BattercakeDecorator {
    
        public EggDecorator(Battercake battercake) {
            super(battercake);
        }
    
        @Override
        protected String getMsg() {
            return super.getMsg() + " +1个鸡蛋";
        }
    
        @Override
        protected int price() {
            return super.price() + 2;
        }
    }

    加香肠的装饰器

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:52
     * @description:
     */
    public class SausageDecorator extends BattercakeDecorator {
    
        public SausageDecorator(Battercake battercake) {
            super(battercake);
        }
    
        @Override
        public String getMsg() {
            return super.getMsg() + " +1个香肠";
        }
    
        /**
         * 1个香肠价格加2元
         */
        @Override
        public int price() {
            return super.price() + 2;
        }
    }

    测试用例

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 17:54
     * @description:
     */
    public class BattercakeTest {
    
        public static void main(String[] args) {
    
            Battercake battercake;
    //    路边买一个煎饼
            battercake = new BaseBattercake();
    //        煎饼有点小,再想加个鸡蛋
            battercake = new EggDecorator(battercake);
    //        再加一个鸡蛋
            battercake = new EggDecorator(battercake);
    //        再加一跟香肠
            battercake = new SausageDecorator(battercake);
    
            System.out.println(battercake.getMsg() + "总价:" + battercake.price());
    
        }
    
    }

    运行结果

     类结构图

     对比继承可以看出,将添加的行为交给了装饰器去做,有点类似静态代理和委派模式

     简单来说:装饰器模式就是通过构造器入参,代替继承实现可扩展的一种方式

    业务场景案例

    案例:系统需要在原有的登录方式上,添加QQ、微信等多种登录方式(这里拿适配器模式讲解的业务场景案例: 连接地址

     登录注册接口

    public interface ISiginService {
    
        /**
         * 注册方法
         */
        ResultMsg regist(String username,String password);
    
        /**
         * 登录方法
         */
        ResultMsg login(String username,String password);
    }

    返回对象类

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 11:55
     * @description:
     */
    public class ResultMsg {
    
        private int code;
        private String msg;
        private String data;
    
        public ResultMsg(int code, String msg, String data) {
            this.code = code;
            this.msg = msg;
            this.data = data;
        }
    }

    登录注册实现类

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 18:40
     * @description: 登录注册实现
     */
    public class SiginService implements ISiginService {
    
            /**
             * 注册方法
             */
            @Override
            public ResultMsg regist(String username,String password){
                return new ResultMsg(200,"注册成功",null);
            }
    
            /**
             * 登录方法
             */
            @Override
            public ResultMsg login(String username,String password){
                return new ResultMsg(200,"登录成功",null);
            }
    
    }

    扩展三方登录的接口

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 18:43
     * @description: 三方登录接口
     */
    public interface ISiginForThirdService extends ISiginService {
        /**
         * QQ登录
         */
        ResultMsg loginForQQ(String openId);
    
        /**
         * 微信登录
         */
        ResultMsg loginForWechat(String openId);
    
        /**
         * 新浪登录
         */
        ResultMsg loginForSina(String openId);
    
        /**
         * token登录
         */
        ResultMsg loginForToken(String token);
    
        /**
         * 手机验证登录
         */
        ResultMsg loginForTelphone(String telphone,String code);
    
        /**
         * 注册账号后自动登录
         */
        ResultMsg loginForRegister(String username,String passport);
    
    
    }

    三方登录包装器

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/12 18:46
     * @description: 三方登录服务 包装器
     */
    public class SiginForThirdService implements ISiginForThirdService {
    
        private ISiginService siginService;
    
        public SiginForThirdService(ISiginService siginService) {
            this.siginService = siginService;
        }
    
        @Override
        public ResultMsg loginForQQ(String openId) {
            return null;
        }
    
        @Override
        public ResultMsg loginForWechat(String openId) {
            return null;
        }
    
        @Override
        public ResultMsg loginForSina(String openId) {
            return null;
        }
    
        @Override
        public ResultMsg loginForToken(String token) {
            return null;
        }
    
        @Override
        public ResultMsg loginForTelphone(String telphone, String code) {
            return null;
        }
    
        @Override
        public ResultMsg loginForRegister(String username, String passport) {
            return null;
        }
    
        @Override
        public ResultMsg regist(String username, String password) {
            return this.siginService.regist(username,password);
        }
    
        @Override
        public ResultMsg login(String username, String password) {
            return this.siginService.login(username,password);
        }
    }

    测试用例

    /**
     * @author: ZhouCong
     * @date: Create in 2021/1/13 10:34
     * @description:
     */
    public class DecoratorTest {
    
        public static void main(String[] args) {
            SiginForThirdService siginForThirdService = new SiginForThirdService(new SiginService());
            siginForThirdService.loginForQQ("fdfdfasd");
        }
    }

    类结构图

     总结

    模式对比:

      装饰器模式 适配器模式
    形式 是一种非常特别的适配器模式 没有层级关系,装饰器模式有层级关系
    定义 装饰者和被装饰者都实现同一个接口,主要目的是为了扩展之后依旧保留OOP关系 适配者和被适配者没有必然的联系,通常是采用继承或代理的形式进行包装
    关系 满足is-a关系 满足has-a关系
    功能 注重覆盖、扩展 注重兼容、转换
    设计 前置考虑,架构设计之初考虑 后置考虑,亡羊补牢

     装饰器模式的优点:

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

    装饰器模式的缺点:

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

    以上对装饰器模式的介绍到此结束,欢迎批评指正。 附:源码地址

  • 相关阅读:
    django 设置局域网内访问项目
    104. Maximum Depth of Binary Tree
    multi dimension array
    Longest Plindrome Subarray
    BlockQueue
    H2O
    HoppingIterator
    midStack
    Reverse Words in a String II
    public boolean canPlaceFlowers(List<Boolean> flowerbed, int numberToPlace)
  • 原文地址:https://www.cnblogs.com/itzhoucong/p/14267897.html
Copyright © 2011-2022 走看看