装饰器模式:初看上图感觉装饰器模式有点像俄罗斯套娃、某众汽⻋ ) ,⽽装饰器的核⼼就是再不改原有类的基础上给类新增功能。不改变原有类,可能有的⼩伙伴会想到继承、AOP切⾯
可以避免继承导致的⼦类过多,
模拟场景:
1、例如:公司内部的sso单点登录已经提供好了实现类,已有一套固定的校验体系。此时因为公司业务扩张,需要在原有的校验规则上,新增校验规则,同时还要保证原有的功能不被破坏,这时我们可以考虑使用装饰器模式,扩充原有的单点登录功能。
要想实现拦截的功能,就必须实现此接口HandlerInterceptor
package com.qf.design.structure.decorate.basic;
public interface HandlerInterceptor {
/**
* prehandler方法拦截,需要被实现
*/
boolean preHandler(String request,String response,Object handler);
}
父类的基础拦截功能:SsoInterceptor
package com.qf.design.structure.decorate.basic;
public class SsoInterceptor implements HandlerInterceptor{
/**
* 父类的基础拦截功能
* @param request
* @param response
* @param handler
* @return
*/
@Override
public boolean preHandler(String request, String response, Object handler) {
//模拟获取cookie
String substring = request.substring(1, 8);
//是否拦截
return substring.equals("success");
}
}
重复书写父类实习代码,一旦父类的类变化了,又需要新写子类继承
package com.qf.design.structure.decorate.tradition;
import com.qf.design.structure.decorate.basic.SsoInterceptor;
import java.util.HashMap;
import java.util.Map;
public class LoginSsoDecoder extends SsoInterceptor {
public LoginSsoDecoder(){}
private static Map<String,String> authMap=new HashMap<>();
static {
authMap.put("huahua", "queryUserInfo");
authMap.put("doudou", "queryUserInfo");
}
@Override
public boolean preHandler(String request, String response, Object handler) {
//模拟获取cookie
String substring = request.substring(1, 8);
//是否拦截
boolean success = substring.equals("success");
if (!success) return false;
//自己需要拓展的拦截逻辑
String userId = request.substring(9);
String method = authMap.get(userId);
// 模拟方法校验
return "queryUserInfo".equals(method);
}
}
测试:ApiTest
package com.qf.design.structure.decorate.tradition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApiTest {
private static Logger logger= LoggerFactory.getLogger(ApiTest.class);
public static void main(String[] args) {
LoginSsoDecoder loginSsoDecoder=new LoginSsoDecoder();
String request="ssuccessshuahuas";
boolean result = loginSsoDecoder.preHandler(request, "response", "");
logger.info("登录校验:"+request+(result?"通过":"失败"));
}
}
作为中转站,接受传递的子类,并调用子类的preHandler
被继承的接⼝可以通过构造函数传递其实现类
package com.qf.design.structure.decorate.design;
import com.qf.design.structure.decorate.basic.HandlerInterceptor;
public abstract class SsoDecoder implements HandlerInterceptor {
private HandlerInterceptor handlerInterceptor;
public SsoDecoder(HandlerInterceptor handlerInterceptor){
this.handlerInterceptor=handlerInterceptor;
}
@Override
public boolean preHandler(String request, String response, Object handler) {
return handlerInterceptor.preHandler(request,response,handler);
}
}
package com.qf.design.structure.decorate.design;
import com.qf.design.structure.decorate.basic.HandlerInterceptor;
import java.util.HashMap;
import java.util.Map;
public class LoginSsoDecoder extends SsoDecoder{
private static Map<String,String> authMap=new HashMap<>();
static {
authMap.put("huahua", "queryUserInfo");
authMap.put("doudou", "queryUserInfo");
}
public LoginSsoDecoder(HandlerInterceptor handlerInterceptor){
super(handlerInterceptor);
}
@Override
public boolean preHandler(String request, String response, Object handler) {
//父类的拦截
boolean result = super.preHandler(request, response, handler);
if (!result) return false;
//自己需要拓展的拦截逻辑
String userId = request.substring(9);
String method = authMap.get(userId);
// 模拟方法校验
return "queryUserInfo".equals(method);
}
}
测试:ApiTest
package com.qf.design.structure.decorate.design;
import com.qf.design.structure.decorate.basic.SsoInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApiTest {
private static Logger logger= LoggerFactory.getLogger(ApiTest.class);
public static void main(String[] args) {
LoginSsoDecoder loginSsoDecoder = new LoginSsoDecoder(new SsoInterceptor());
String request="ssuccessshuahua";
boolean result = loginSsoDecoder.preHandler(request, "response", "");
logger.info("登录校验:"+request+(result?"通过":"失败"));
}
}
总结:
使⽤装饰器模式满⾜单⼀职责原则,你可以在⾃⼰的装饰类中完成功能逻辑的扩展,⽽不影响主类,同时可以按需在运⾏时添加和删除这部分逻辑。另外装饰器模式与继承⽗类重写⽅法,在某些时候需要需选择,并不⼀定某⼀个就是最好。
装饰器实现的重点是对抽象类继承接⼝⽅式的使⽤,同时设定被继承的接⼝可以通过构造函数传递其实现类,由此增加扩展性并重写⽅法⾥可以实现此部分⽗类实现的功能。
假设基础主类发生了重大修改(改了类名或者新写),我们只需要传递最终所需要的主类