为了账号安全,请及时绑定邮箱和手机立即绑定

深入理解装饰者模式

标签:
设计模式

基础部分

装饰者模式的类图

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

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

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

类图:
在这里插入图片描述
煎饼类

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 的关系
功能 注重覆盖、扩展注重 兼容、转换
设计 设计前置考虑 后置考虑

与组合模式

在这里插入图片描述

与策略模式

在这里插入图片描述

与模板方法模式

在这里插入图片描述

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

如果帮助到您,欢迎收藏和转发,转载请标明作者和出处,谢谢。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消