美文网首页
深入理解装饰者模式

深入理解装饰者模式

作者: List_21f6 | 来源:发表于2020-06-03 00:11 被阅读0次

    基础部分

    装饰者模式的类图

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    装饰者模式的简单入门案例

    来看一个这样的场景,有些小伙伴可能在上班路上碰到卖煎饼的路边摊,都会顺带一个到公司茶水间吃早餐。卖煎饼的大姐可以给你的煎饼加鸡蛋,也可以加香肠.
    用装饰者实现计算总价:

    类图:


    在这里插入图片描述

    煎饼类

    public class Battercake {
        protected String getMsg(){
            return "煎饼";
        }
        public int getPrice(){
            return 5;
        }
    }
    

    基础套餐

    public class BaseBattercake extends Battercake {
        @Override
        protected String getMsg() {
            return "煎饼";
        }
    
        @Override
        protected int getPrice() {
            return 5;
        }
    }
    
    

    抽象装饰类

    /**
     * Title: BattercakeDecorator
     * Description: 加蛋糕的套餐
     *
     * @author hfl
     * @version V1.0
     * @date 2020-05-31
     */
    public abstract class BattercakeDecorator extends Battercake {
        private Battercake battercake;
    
        public BattercakeDecorator(Battercake battercake) {
            this.battercake = battercake;
        }
    
    
        //、用于扩展一个类的功能或给一个类添加附加职责。
        protected abstract void doSomething();
    
        @Override
        protected String getMsg() {
            return this.battercake.getMsg();
        }
    
        @Override
        protected int getPrice() {
            return this.battercake.getPrice() ;
        }
    }
    
    

    鸡蛋装饰者

    /**
     * Title: EggDecorator
     * Description: 鸡蛋装饰者
     *
     * @author hfl
     * @version V1.0
     * @date 2020-05-31
     */
    public class EggDecorator extends BattercakeDecorator {
    
        public EggDecorator(Battercake battercake) {
            super(battercake);
        }
    
        @Override
        protected void doSomething() {
        }
    
        @Override
        protected String getMsg() {
            return super.getMsg()+"+1个鸡蛋";
        }
    
        @Override
        protected int getPrice() {
            return super.getPrice()+ 1;
        }
    }
    
    /**
     * Title: SausageDecorator
     * Description: 香肠装饰者
     *
     * @author hfl
     * @version V1.0
     * @date 2020-05-31
     */
    public class SausageDecorator extends BattercakeDecorator {
        public SausageDecorator(Battercake battercake) {
            super(battercake);
        }
    
        @Override
        protected void doSomething() {
    
        }
        @Override
        protected String getMsg() {
            return super.getMsg() + "1根香肠";
        }
    
        @Override
        protected int getPrice() {
            return super.getPrice() + 2;
        }
    }
    
    

    测试类:

    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);
            //跟静态代理最大区别就是职责不同
            //静态代理不一定要满足is-a 的关系
            //静态代理会做功能增强,同一个职责变得不一样
            //装饰器更多考虑是扩展
            System.out.println(battercake.getMsg() + ",总价:" + battercake.getPrice());
        }
    }
    

    运行结果:


    在这里插入图片描述

    高级部分

    装饰者模式的本质

    在这里插入图片描述 在这里插入图片描述

    怎么理解本质说的这2个方面:

    类的层面考虑:横向扩展(动态扩展)--- 类比继承

    我们如果有个类,有一些方法,而如果需要扩展这个类的方法,可能想到的是继承,可是java是单继承,所以为了满足客户端的需求,又做到单一性原则,可能需要继承好几层。纵向的链路会特别长。

    A(){}
    B extends A{}
    C extens B{}
    ...
    

    而装饰者模式,相当于横向扩展,大多数情况下,只会继承一层,但是要将继承的类,放在属性上,通过构造函数注入。一个装饰者扩展了某个功能后,返回的外观还是和注入的类同一个父类,下一个装饰者可以将上一个装饰者注入到自己,然后实现自己的方法功能,调用起来就好像这个样子:
    new A(new B( new C(....)))
    这就是横向扩展,解决了java的单继承弊端,防止了继承的链路太长。

    对象方法的层面考虑:为装饰者透明的增加功能,甚至可以控制功能访问 --- 类比AOP

    我们用过spring开发的都知道,spring使用动态代理帮我们在类方法执行的前,中,后进行一些公共逻辑的提取,帮我们简化代码。
    同样的: 装饰者模式可以通过装饰者增加功能,甚至给装饰前的对象织入自己的逻辑呢。
    代码演示:


    在这里插入图片描述

    封装销售单的数据

    public class SaleModel {
        /**
         * 销售的商品
         */
        private String goods;
        /**
         * 销售的数量
         */
        private int saleNum;
    
        public String getGoods() {
            return goods;
        }
    
        public void setGoods(String goods) {
            this.goods = goods;
        }
    
        public int getSaleNum() {
            return saleNum;
        }
    
        public void setSaleNum(int saleNum) {
            this.saleNum = saleNum;
        }
    
        @Override
        public String toString() {
            return "SaleModel{" +
                    "goods='" + goods + '\'' +
                    ", saleNum=" + saleNum +
                    '}';
        }
    }
    

    商品销售管理的业务接口

    public interface GoodSaleEbi {
        /**
         * 保存销售信息
         * @param user 操作人员
         * @param cunstomer 客户
         * @param saleModel 销售数据
         * @return 是否保存成功
         */
        public boolean sale(String user, String cunstomer, SaleModel saleModel);
    }
    

    基本的业务实现对象

    public class GoodSaleEbo implements GoodSaleEbi {
        @Override
        public boolean sale(String user, String cunstomer, SaleModel saleModel) {
            System.out.println(user + "保存了," + cunstomer + "购买" + saleModel + "的销售数据");
            return true;
        }
    }
    

    抽象的装饰器

    public  abstract class Decorator implements GoodSaleEbi {
        /**
         * 持有被装饰的组件对象
         */
        protected GoodSaleEbi ebi;
    
        /**
         * 通过构造方法传入被装饰的对象
         * @param ebi 被装饰的对象
         */
        public Decorator(GoodSaleEbi ebi) {
            this.ebi = ebi;
        }
    
    
    }
    
    

    实现权限控制的装饰器

    /**
     * 实现权限控制的装饰器
     */
    public class CheckDecorator  extends Decorator{
        /**
         * 通过构造方法传入被装饰的对象
         *
         * @param ebi 被装饰的对象
         */
        public CheckDecorator(GoodSaleEbi ebi) {
            super(ebi);
        }
        //权限控制逻辑
    
    
        @Override
        public boolean sale(String user, String cunstomer, SaleModel saleModel) {
            if (!"张三".equals(user)){
                System.out.println("对不起" + user + ",你没有保存销售单的权限");
                return false;
            }else{
                return this.ebi.sale(user,cunstomer,saleModel);
            }
        }
    }
    

    实现日志记录

    public class LogDecorator extends Decorator {
    
        public LogDecorator(GoodSaleEbi ebi) {
            super(ebi);
        }
    
        @Override
        public boolean sale(String user, String cunstomer, SaleModel saleModel) {
            //执行业务
            boolean f = this.ebi.sale(user, cunstomer, saleModel);
            //在执行业务功能后记录日志
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            System.out.println("日志记录:" + user + "于" + simpleDateFormat.format(new Date())
                    + "时保存了一条销售记录,客户是:" + cunstomer + ",购买记录是:" + saleModel);
            return f;
        }
    }
    

    测试:client

    public class client {
        public static void main(String[] args) {
            //得到业务接口,组合装饰器
            GoodSaleEbi ebi = new CheckDecorator(new LogDecorator(new GoodSaleEbo()));
            //准备测试数据
            SaleModel saleModel = new SaleModel();
            saleModel.setGoods("moto 手机");
            saleModel.setSaleNum(2);
            //调用业务功能
            ebi.sale("张三","张三丰",saleModel);
            ebi.sale("李四","张三丰",saleModel);
        }
    }
    
    

    运行结果:


    在这里插入图片描述

    在方法的前后都植入了逻辑,相当于模拟了Aop的功能。


    在这里插入图片描述

    java源码重点使用的地方

    io流

    典型的装饰者模式

    Spring 中的TransactionAwareCacheDecorator 类

    在这里插入图片描述

    MyBatis 中的一段处理缓存的设计org.apache.ibatis.cache.Cache

    在这里插入图片描述

    装饰者模式总结(深入反复理解)

    装饰者模式的优缺点

    优点:

    • 1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象
      扩展功能,即插即用。
    • 2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。
    • 3、装饰者完全遵守开闭原则。
      缺点:
    • 1、会出现更多的代码,更多的类,增加程序复杂性。
    • 2、动态装饰时,多层装饰时会更复杂。

    和其他模式的相同和不同对比

    与适配器模式

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

    与组合模式

    在这里插入图片描述

    与策略模式

    在这里插入图片描述

    与模板方法模式

    在这里插入图片描述

    以上截图的地方大部分来自<研磨设计模式>一书。


    个人微信公众号:
    搜索: 怒放de每一天
    不定时推送相关文章,期待和大家一起成长!!


    在这里插入图片描述

    如果帮助到您,转发请备注出处和谢谢。

    相关文章

      网友评论

          本文标题:深入理解装饰者模式

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