Spring源码学习笔记:经典设计模式之装饰者模式

1、博客内容均出自于咕泡学院架构师第三期
2、架构师系列内容:架构师学习笔记(持续更新)

0、装饰者模式(Decorator Pattern)

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

适用场景:

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

优缺点:
优点:

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

缺点:

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

生活应用场景:
比如,给煎饼加鸡蛋;给蛋糕加上一些水果;给房子装修等,为对象扩展一些额外的职能

1、简单实现

场景:早上上班在煎饼摊买煎饼果子, 卖煎饼的大姐可以给你的煎饼加鸡蛋,也可以加香肠等等,只要摊子上有的你都可以加。
我们用代码来实现V1版本
首先创建一个煎饼Battercake类:

package com.jarvisy.demo.pattern.decorator.battercake.v1;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:17
* @description :
*/
public class Battercake {


    protected String getMsg(){
        return "煎饼";
    }


    protected int getPrice(){
        return 5;
    }
}

这个时候我想加一个鸡蛋,所以创建一个加鸡蛋的煎饼BattercakeWithEgg 类:

package com.jarvisy.demo.pattern.decorator.battercake.v1;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:20
* @description :
*/
public class BattercakeWithEgg extends Battercake {


    @Override
    protected String getMsg() {
        return super.getMsg() + "+1个鸡蛋";
    }


    @Override
    protected int getPrice() {
        // 加一个鸡蛋1块钱
        return super.getPrice() + 1;
    }
}

接着我又改变主意了,要再加一个香肠,我们又得创建一个加了香肠的类 BattercakeWithEggAndSausage:

package com.jarvisy.demo.pattern.decorator.battercake.v1;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:22
* @description :
*/
public class BattercakeWithEggAndSausage extends BattercakeWithEgg {


    @Override
    protected String getMsg() {
        return super.getMsg()+"+1根香肠";
    }


    @Override
    protected int getPrice() {
        // 1根香肠2块钱
        return super.getPrice()+2;
    }
}

测试代码:

package com.jarvisy.demo.pattern.decorator.battercake.v1;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:23
* @description :
*/
public class BattercakeTest {


    public static void main(String[] args) {
        Battercake battercake = new Battercake();
        System.out.println(battercake.getMsg()+",总价格:"+battercake.getPrice());


        BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg();
        System.out.println(battercakeWithEgg.getMsg()+",总价格:"+battercakeWithEgg.getPrice());


        BattercakeWithEgg battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();
        System.out.println(battercakeWithEggAndSausage.getMsg()+",总价格:"+battercakeWithEggAndSausage.getPrice());
    }
}

运行结果没啥问题,但是如果我的需求变更了,要加两个鸡蛋或者加两根香肠的煎饼,又或者说还有其他可以加的东西,那么现在的类结构是创建不出来的,也无法自动计算出价格,除非再根据需求定制一个类。这样的话,如果需求一直变,就需要一直加类,显然是不科学的。
下面就是用装饰者模式来解决问题,V2版本
首先创建一个煎饼果子的抽象Battercake类:

package com.jarvisy.demo.pattern.decorator.battercake.v2;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:36
* @description :
*/
public abstract class Battercake {
    protected abstract String getMsg();


    protected abstract int getPrice();
}

创建一个基本的煎饼果子(啥都不加的煎饼)BaseBattercake:

package com.jarvisy.demo.pattern.decorator.battercake.v2;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:39
* @description :
*/
public class BaseBattercake extends Battercake {
    @Override
    protected String getMsg() {
        return "煎饼";
    }


    @Override
    protected int getPrice() {
        return 5;
    }
}

然后再创建一个扩展套餐的抽象装饰者BattercakeDecorator类:

package com.jarvisy.demo.pattern.decorator.battercake.v2;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:41
* @description :
*/
public 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 getPrice() {
        return this.battercake.getPrice();
    }
}

创建鸡蛋装饰者类EggDecorator,香肠装饰者类SausageDecorator:

package com.jarvisy.demo.pattern.decorator.battercake.v2;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:45
* @description :
*/
public class EggDecorator extends BattercakeDecorator {
    public EggDecorator(Battercake battercake) {
        super(battercake);
    }


    @Override
    protected String getMsg() {
        return super.getMsg() + "+1个鸡蛋";
    }


    @Override
    protected int getPrice() {
        return super.getPrice() + 1;
    }
}
package com.jarvisy.demo.pattern.decorator.battercake.v2;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22:45
* @description :
*/
public class SausageDecorator extends BattercakeDecorator {
    public SausageDecorator(Battercake battercake) {
        super(battercake);
    }


    @Override
    protected String getMsg() {
        return super.getMsg() + "+1根香肠";
    }


    @Override
    protected int getPrice() {
        return super.getPrice() + 2;
    }
}

测试代码:

package com.jarvisy.demo.pattern.decorator.battercake.v2;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 22: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.getPrice());

    }
}

结果也能正确计算出来,但是不再需要每次都定制一个类。我们只要使用装饰者类就可以了。

装饰者模式跟静态代理最大的区别就是指责不同;静态代理不一定要满足is-a的关系;静态代理会做功能增强,同一个指责变得不一样;装饰器更多考虑是扩展。

类图:
Spring源码学习笔记:经典设计模式之装饰者模式_第1张图片

2、再来一个例子

扩展登录方式的场景
源代码:

package com.jarvisy.demo.pattern.decorator.passport.old;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 23:17
* @description :
*/
public class Member {


    private String username;
    private String password;
    private String mid;
    private String info;


    public String getUsername() {
        return username;
    }


    public void setUsername(String username) {
        this.username = username;
    }


    public String getPassword() {
        return password;
    }


    public void setPassword(String password) {
        this.password = password;
    }


    public String getMid() {
        return mid;
    }


    public void setMid(String mid) {
        this.mid = mid;
    }


    public String getInfo() {
        return info;
    }


    public void setInfo(String info) {
        this.info = info;
    }
}
package com.jarvisy.demo.pattern.decorator.passport.old;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 23:17
* @description :
*/
public class ResultMsg {


    private int code;
    private String msg;
    private Object data;


    public ResultMsg(int code, String msg, Object data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }


    public int getCode() {
        return code;
    }


    public void setCode(int code) {
        this.code = code;
    }


    public String getMsg() {
        return msg;
    }


    public void setMsg(String msg) {
        this.msg = msg;
    }


    public Object getData() {
        return data;
    }


    public void setData(Object data) {
        this.data = data;
    }
}
package com.jarvisy.demo.pattern.decorator.passport.old;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 23:16
* @description :
*/
public interface ISignService {
    ResultMsg register(String username, String password);




    /**
     * 登录的方法
     * @param username
     * @param password
     * @return
     */
    ResultMsg login(String username, String password);
}
package com.jarvisy.demo.pattern.decorator.passport.old;




/**
* @author :Jarvisy
* @date :Created in 2020/9/21 23:17
* @description :
*/
public class SignService implements ISignService {


    /**
     * 注册方法
     *
     * @param username
     * @param password
     * @return
     */
    public ResultMsg register(String username, String password) {
        return new ResultMsg(200, "注册成功", new Member());
    }




    /**
     * 登录的方法
     *
     * @param username
     * @param password
     * @return
     */
    public ResultMsg login(String username, String password) {
        return null;
    }


}

使用装饰者模式改造代码;
创建一个新的接口继承原来的接口:

package com.jarvisy.demo.pattern.decorator.passport.upgrade;


import com.jarvisy.demo.pattern.decorator.passport.old.ISignService;
import com.jarvisy.demo.pattern.decorator.passport.old.ResultMsg;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 23:19
* @description :
*/
public interface ISignForThirdService extends ISignService {


    /**
     * QQ登录
     *
     * @param id
     * @return
     */
    ResultMsg loginForQQ(String id);


    /**
     * 微信登录
     *
     * @param id
     * @return
     */
    ResultMsg loginForWeChat(String id);




    /**
     * 手机号登录
     *
     * @param tel
     * @param code
     * @return
     */
    ResultMsg loginForTel(String tel, String code);


    /**
     * 注册后自动登录
     *
     * @param username
     * @param passport
     * @return
     */
    ResultMsg loginForRegister(String username, String passport);
}

创建新的逻辑处理类 SigninForThirdService,实现新创建的接口:

package com.jarvisy.demo.pattern.decorator.passport.upgrade;




import com.jarvisy.demo.pattern.decorator.passport.old.ISignService;
import com.jarvisy.demo.pattern.decorator.passport.old.ResultMsg;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 23:24
* @description :第三方登录自由适配
*/
public class SignForThirdService implements ISignForThirdService {


    private ISignService signService;


    public SignForThirdService(ISignService signService) {
        this.signService = signService;
    }


    public ResultMsg register(String username, String password) {
        return signService.register(username, password);
    }


    public ResultMsg login(String username, String password) {
        return signService.login(username, password);
    }


    public ResultMsg loginForQQ(String id) {
        return null;
    }


    public ResultMsg loginForWeChat(String id) {
        return null;
    }




    public ResultMsg loginForTel(String tel, String code) {
        return null;
    }


    public ResultMsg loginForRegister(String username, String passport) {
        return null;
    }
}

测试类:

package com.jarvisy.demo.pattern.decorator.passport.upgrade;


import com.jarvisy.demo.pattern.decorator.passport.old.SignService;


/**
* @author :Jarvisy
* @date :Created in 2020/9/21 23:25
* @description :
*/
public class DecoratorTest {


    public static void main(String[] args) {


        //满足一个is-a
        ISignForThirdService signForThirdService = new SignForThirdService(new SignService());
        signForThirdService.loginForQQ("ssdfasdfdfsdfs");

    }

}

为某个类实现动态增加或者覆盖原有方法的情况, 采用装饰器模式。

装饰者模式最本质的特征是将原有的类的附加功能抽离出来,简化原有类的逻辑。

3、装饰者模式和适配器模式对比

装饰者和适配器模式都是包装模式(Wrapper Pattern),装饰者也是一种特殊的代理模式

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

4、装饰者模式在源码的应用

装饰者模式在源码中也应用的非常多。在jdk中体现最明显的类就是IO相关的类,如BufferedReader、InputStream、OutputStream,看一下常用的InputStream的类结构
Spring源码学习笔记:经典设计模式之装饰者模式_第2张图片
在Spring中的TransactionAwareCacheDecorator类 这个类主要是用来处理事务缓存的

public class TransactionAwareCacheDecorator implements Cache {


   private final Cache targetCache;




   /**
    * Create a new TransactionAwareCache for the given target Cache.
    * @param targetCache the target Cache to decorate
    */
   public TransactionAwareCacheDecorator(Cache targetCache) {
      Assert.notNull(targetCache, "Target Cache must not be null");
      this.targetCache = targetCache;
   }
}

TransactionAwareCacheDecorator 就是对 Cache 的一个包装。再来看一个 MVC 中的
装饰者模式 HttpHeadResponseDecorator 类:

public class HttpHeadResponseDecorator extends ServerHttpResponseDecorator {
    public HttpHeadResponseDecorator(ServerHttpResponse delegate) {
        super(delegate);
    }

}

MyBatis 中的一段处理缓存的设计 org.apache.ibatis.cache.Cache 类,找到它的包定位:
Spring源码学习笔记:经典设计模式之装饰者模式_第3张图片
从名字上来看其实更容易理解了。比如 FifoCache 先入先出算法的缓存;LruCache 最近最少使用的缓存;TransactionlCache 事务相关的缓存,都是采用装饰者模式。

你可能感兴趣的:(java设计模式,Spring源码学习笔记,架构师学习笔记,java,设计模式,spring)