简介
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
在很多博文里会引入Decorator角色,觉得把简单问题复杂化了。等下看一下Request的应用就知道实际用起来比类图简单的多。
源码中的应用
以RequestWrapper为例,这是一个十分有代表性的类。在debug的时候经常会发现,request套了一个又一个,因为他们一直在互相wrapper。点到最后才看到request的参数。每一层包装其实都是一次加强。
他的实际代码结构就是一个Request的子类,并在类里声明了一个对象存储Request,继承Request的接口都由这个对象来实际去执行。然后自己再去做增强的工作。
代码如下:
public class HttpServletRequestWrapper extends ServletRequestWrapper implements HttpServletRequest {
public HttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
private HttpServletRequest _getHttpServletRequest() {
return (HttpServletRequest) super.getRequest();
}
@Override
public String getAuthType() {
return this._getHttpServletRequest().getAuthType();
}
@Override
public Cookie[] getCookies() {
return this._getHttpServletRequest().getCookies();
}
@Override
public long getDateHeader(String name) {
return this._getHttpServletRequest().getDateHeader(name);
}
@Override
public String getHeader(String name) {
return this._getHttpServletRequest().getHeader(name);
}
@Override
public Enumeration<String> getHeaders(String name) {
return this._getHttpServletRequest().getHeaders(name);
}
// 后续代码类似,都是执行_getHttpServletRequest的方法,省略了。
}
开发中的应用
一般场景是用不到的,刚好碰到了并且想到了这个模式,一拍即合。
需求如下:
要整合Spring security框架,里面有个类叫UserDetails,是security中存储用户的地方,但是这个对象没有部门、公司等信息,在实际应用中数据不够。但是security所有的组件交互的对象都是他,所以又必须实现这个接口。
看到的一个代码他的实现方式是:写一个类继承UserDetails,然后在写deptId,companyId等属性。在交互的时候调一次构造函数,把这些参数在一个一个传进去(除了deptId,companyId以外,userDetails的参数也要拼进去),代码不美观。
public UserDetails loadUserByUsername(String username){
// 前面代码省略了,就看返回的一段
return new MyUser(user.getUserId(), user.getDeptId(), user.getPhone(),
user.getUsername(), user.getPassword(), enabled, true, true,
true, authorities);
}
如果用wrapper代码传递上就很简单:
public UserDetails loadUserByUsername(String username){
// 前面代码省略了,就看返回的一段
return new UserDetailsWrapper(user.getUserId(), user.getDeptId(), user.getPhone(),
userdetails);
}
另外,如果出现UserDetails在系统中需要多个维度叠加功能(基本不会出现)的话,这个模式也很好用。
类似的ClientDetails类,我们用数据库存储这个信息的话,他的类本身有很多属性是set或者map的,而且权限信息是需要从权限表去读,这些都无法直接从库里单表转化,所以需要有一个entity类与db做映射,然后用一个BO类二次封装。
即定义一个如下类
public class ClientDetailsBO implements ClientDetails{
private ClientDetailsEntity delegate;
private Set<GrantedAuthority> authorities;
@Override
public String getClientId() {
return delegate.getClientId();
}
@Override
public Set<String> getResourceIds() {
// 数据库里这个resourceId是存的逗号隔开的资源列表,是string方式,所以这个接口里要做个转化
return stringToSet(delegate.getResourceIds());
}
@Override
public boolean isSecretRequired() {
return delegate.isSecretRequired();
}
}
这样的话从entity转换成security自带的clientDetails就比较方便。不用一个一个读取再一个一个set。