深入理解装饰者模式

简介: 深入理解装饰者模式

基础部分


装饰者模式的类图




1dc618a0ed9580ce8bfa6facb208c08f.png


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


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

用装饰者实现计算总价:


类图:

1dc618a0ed9580ce8bfa6facb208c08f.png

煎饼类


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());
    }
}


运行结果:


高级部分


装饰者模式的本质



1dc618a0ed9580ce8bfa6facb208c08f.png


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


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


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

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

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


new A(new B( new C(....)))


这就是横向扩展,解决了java的单继承弊端,防止了继承的链路太长。



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


我们用过spring开发的都知道,spring使用动态代理帮我们在类方法执行的前,中,后进行一些公共逻辑的提取,帮我们简化代码。


同样的: 装饰者模式可以通过装饰者增加功能,甚至给装饰前的对象织入自己的逻辑呢。


代码演示:

1dc618a0ed9580ce8bfa6facb208c08f.png

封装销售单的数据


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);
    }
}


运行结果:

1dc618a0ed9580ce8bfa6facb208c08f.png

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

5d4c6812c8535adbb050f4ddf2e1bce8.png


java源码重点使用的地方


io流


典型的装饰者模式


Spring 中的TransactionAwareCacheDecorator 类


1dc618a0ed9580ce8bfa6facb208c08f.png


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


5d4c6812c8535adbb050f4ddf2e1bce8.png


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


装饰者模式的优缺点


优点:


1、装饰者是继承的有力补充,比继承灵活,不改变原有对象的情况下动态地给一个对象

扩展功能,即插即用。


2、通过使用不同装饰类以及这些装饰类的排列组合,可以实现不同效果。


3、装饰者完全遵守开闭原则。


缺点:


1、会出现更多的代码,更多的类,增加程序复杂性。


2、动态装饰时,多层装饰时会更复杂。


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


与适配器模式

image.png


与组合模式


1dc618a0ed9580ce8bfa6facb208c08f.png


与策略模式


5d4c6812c8535adbb050f4ddf2e1bce8.png


与模板方法模式


46a9d80a6e05e4e3b19d57a0ee70bcdf.png


相关文章
|
6月前
|
设计模式 Java
Java设计模式【十】:装饰者模式
Java设计模式【十】:装饰者模式
49 0
|
21天前
|
设计模式 缓存 C#
C# 一分钟浅谈:装饰者模式与代理模式
【10月更文挑战第12天】本文介绍了面向对象编程中的两种常见设计模式:装饰者模式和代理模式。装饰者模式允许在运行时动态地给对象添加功能,而代理模式则通过代理对象控制对另一个对象的访问。文章详细讲解了这两种模式的概念、常见问题、如何避免问题以及代码示例,帮助读者更好地理解和应用这些设计模式。
35 13
|
6月前
|
设计模式
装饰器模式
装饰器模式
32 0
|
6月前
|
设计模式 C++
【C++】—— 装饰器模式
【C++】—— 装饰器模式
装饰者模式
装饰者模式
71 0
|
设计模式
2023-6-26-第八式装饰器模式
2023-6-26-第八式装饰器模式
74 0
|
前端开发 BI
关于装饰器模式我所知道的
关于装饰器模式我所知道的
77 0
|
设计模式
我认为的装饰器模式
我认为的装饰器模式
97 0
|
Java
结构型模式-装饰者模式
结构型模式-装饰者模式
86 0