装饰者模式
一、概述
装饰者模式(装饰器模式)是一种结构型模式
定义: 在不改变现有对象结构的情况下,动态地给该对象增加一些额外职责(功能)的模式。
装饰者(Decorator)模式中的角色:
- 抽象构件(Component)角色: 定义一个抽象接口准备接收附加责任的对象。
- 具体构件(Concrete Component)角色: 实现抽象构件,通过装饰角色为其添加一些职责。
- 抽象装饰(Decorator)角色: 继承或实现抽象构件,聚合抽象构件,通过子类扩展具体构件的功能。
- 具体装饰(ConcreteDecorator)角色: 实现抽象装饰的相关方法,扩展具体构件的职责。
二、案例场景模拟
场景: 在业务发展初期SSO
单点登录只需要进行用户登录状态验证即可,但随着业务发展团队成员的扩充,比如专门的运营人员、营销人员、数据人员,不同的人员对数据的操作不同。为了保证数据的安全性,需要给不同的角色分配相应的权限,当用户访问自身权限外的业务数据时进行拦截。
在原有SSO
单点登录不受破坏的前提下,进行功能扩展。
1. 继承模拟实现
1.1 模拟Spring中的HandlerInterceptor
public interface HandlerInterceptor {
// 登录校验
boolean preHandle(String request, String response, Object handler);
}
1.2 模拟单点登录
public class SsoInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(String request, String response, Object handler) {
// 模拟获取cookie
String ticket = request.substring(1,3);
// 校验
return "ok".equals(ticket);
}
}
1.3 继承后重写方法添加模拟方法拦截
public class LoginSsoDecorator extends SsoInterceptor {
private static Map<String, String> authMap = new ConcurrentHashMap<>();
static {
authMap.put("liming", "findInfo");
authMap.put("xiaohua", "findInfo");
}
@Override
public boolean preHandle(String request, String response, Object handler) {
// 获取cookie 模拟登录校验
String ticket = request.substring(1, 3);
boolean success = "ok".equals(ticket);
if (!success) return false;
String userId = request.substring(3);
String method = authMap.get(userId);
return "findInfo".equals(method);
}
}
1.4 测试类:
public class Client {
public static void main(String[] args) {
LoginSsoDecorator ssoDecorator = new LoginSsoDecorator();
String request = "1okliming";
boolean success = ssoDecorator.preHandle(request, "200", "handler");
System.out.println("登录校验:" + request + (success ? " 放行": " 拦截"));
}
}
测试结果:
2. 装饰者模式重构
2.1 抽象构件角色 HandlerInterceptor
public interface HandlerInterceptor {
// 登录校验
boolean preHandle(String request, String response, Object handler);
}
2.2 具体构件角色 SsoInterceptor
public class SsoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(String request, String response, Object handler) {
// 模拟获取cookie
String ticket = request.substring(1,3);
// 校验
return "ok".equals(ticket);
}
}
2.3 抽象装饰类SsoDecorator
public abstract class SsoDecorator implements HandlerInterceptor {
private HandlerInterceptor handlerInterceptor;
public SsoDecorator(){
}
public SsoDecorator(HandlerInterceptor handlerInterceptor) {
this.handlerInterceptor = handlerInterceptor;
}
@Override
public boolean preHandle(String request, String response, Object handler) {
return handlerInterceptor.preHandle(request,response,handler);
}
}
抽象装饰者角色继承了prehandle
接口,聚合抽象构件handlerInterceptor
2.4 具体装饰类LoginSsoDecorator
public class LoginSsoDecorator extends SsoDecorator {
private static Map<String, String> authMap = new ConcurrentHashMap<>();
static {
authMap.put("liming", "findInfo");
authMap.put("xiaohua", "findInfo");
}
public LoginSsoDecorator(HandlerInterceptor handlerInterceptor) {
super(handlerInterceptor);
}
@Override
public boolean preHandle(String request, String response, Object handler) {
// 获取cookie 模拟登录校验
boolean success = super.preHandle(request, response, handler);
if (!success) return false;
String userId = request.substring(3);
String method = authMap.get(userId);
System.out.println("模拟单点登录方法拦截:" + userId + " " + method);
return "findInfo".equals(method);
}
}
在具体的装饰类*LoginSsoDecorator
中继承了抽象装饰类SsoDecorator
,而SsoDecorator
又实现并聚合了HandlerInterceptor
,因此LoginSsoDecorator
可以对接口perHandle
进行扩展并且不会影响原有类的实现,也不会因为继承导致多余子类,增加了整体的灵活性。
2.5 测试类:
public class Client {
public static void main(String[] args) {
LoginSsoDecorator ssoDecorator = new LoginSsoDecorator(new SsoInterceptor());
String request = "1okliming";
boolean success = ssoDecorator.preHandle(request, "200", "handler");
System.out.println("登录校验:" + request + (success ? " 放行": " 拦截"));
}
}
将单点登录类SsoInterceptor
传递给装饰器,装饰器可以进行功能扩展
测试结果:
三、优势与适用场景
1. 优势:
- 装饰者模式可以带来比继承更加灵活性的扩展,使用更加方便,可以通过组合不同的装饰者对象来获取具有不同行为状态的多样化结果。装饰者模式遵循开闭原则,动态的附加责任。
- 装饰类和被装饰类相互独立,可以分别扩展互不影响。
2. 使用场景:
- 当不能采用继承的方式对系统进行扩充或者采用继承造成子类过多不利于系统扩展和维护时。
- 在不影响接口现有功能下进行动态扩展。
例子: 比如spring
框架中的HandlerInterceptor
拦截器、JDK中的IO流BufferedInputStream
、BufferedWriter
是对InputStream
、Writer
子实现进行了功能扩展,加入了缓冲区提高了数据的读写的效率。
《参考资料》
- 黑马程序员——设计模式
- 《重学Java设计模式——小傅哥》