基础部分
装饰者模式的类图
装饰者模式的简单入门案例
来看一个这样的场景,有些小伙伴可能在上班路上碰到卖煎饼的路边摊,都会顺带一个到公司茶水间吃早餐。卖煎饼的大姐可以给你的煎饼加鸡蛋,也可以加香肠.
用装饰者实现计算总价:
类图:
煎饼类
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、动态装饰时,多层装饰时会更复杂。
和其他模式的相同和不同对比
与适配器模式
与组合模式
与策略模式
与模板方法模式