1、博客内容均出自于咕泡学院架构师第三期
2、架构师系列内容:架构师学习笔记(持续更新)
指在不改变原有对象的基础之上,将功能附加到对象上,提供了比继承更有弹性的替代方案(扩展原油对象的功能)。属于结构型模式。
适用场景:
优缺点:
优点:
缺点:
生活应用场景:
比如,给煎饼加鸡蛋;给蛋糕加上一些水果;给房子装修等,为对象扩展一些额外的职能
场景:早上上班在煎饼摊买煎饼果子, 卖煎饼的大姐可以给你的煎饼加鸡蛋,也可以加香肠等等,只要摊子上有的你都可以加。
我们用代码来实现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的关系;静态代理会做功能增强,同一个指责变得不一样;装饰器更多考虑是扩展。
扩展登录方式的场景
源代码:
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");
}
}
为某个类实现动态增加或者覆盖原有方法的情况, 采用装饰器模式。
装饰者模式最本质的特征是将原有的类的附加功能抽离出来,简化原有类的逻辑。
装饰者和适配器模式都是包装模式(Wrapper Pattern),装饰者也是一种特殊的代理模式
装饰者模式 | 适配器模式 | |
---|---|---|
形式 | 是一种非常特别的适配器模式 | 没有层级关系,装饰者模式有层级关系 |
定义 | 装饰者和被装饰者都实现同一个接口,主要目的是为了扩展之后依旧保留OOP关系 | 适配器和被适配着没有必然的联系,通常是采用继承或者代理的形式进行包装 |
关系 | 满足is-a的关系 | 满足has-a的关系 |
功能 | 注重覆盖,扩展 | 注重兼容,转换 |
设计 | 前置考虑 | 后置考虑 |
装饰者模式在源码中也应用的非常多。在jdk中体现最明显的类就是IO相关的类,如BufferedReader、InputStream、OutputStream,看一下常用的InputStream的类结构
在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 类,找到它的包定位:
从名字上来看其实更容易理解了。比如 FifoCache 先入先出算法的缓存;LruCache 最近最少使用的缓存;TransactionlCache 事务相关的缓存,都是采用装饰者模式。