设计模式:装饰者模式
装饰者模式是不太常用的一种设计模式。我不太明白为什么它没能流行起来,虽然它用起来很方便。装饰者模式让你能够在可控的范围内往一个对象添加功能。这个在运行时也是靠谱的,甚至在静态类型的语言里也是如此。装饰者模式可以当作子类的一个替代品。子类化在编译期的时候添加新的特性,这个改动影响到原始类的所有实例;装饰者模式能在运行时给指定的对象提供新的行为。它是坚持开闭原则的一个很好的工具。
下面的一些例子将让它看到这种模式的魅力:
示例1:HTTP认证
假设这里有个HTTP客户端 ,它访问的是一个RESTful的服务。
服务有一部分是能直接访问的,有一些则需要用户进行登录。当客户端试图访问一个受限资源的时候,这个RESTful服务会返回一个401未授权的响应码。
修改客户端让它处理401的话会带来浪费,因为每个调用都有可能需要认证。我们应该把认证代码剥离出来。剥离到哪儿呢?
现在是装饰者模式出场的时候了:
public intpublic class AuthenticatingHttpClient
implements HttpClient {
private final HttpClient wrapped;
public AuthenticatingHttpClient(HttpClient wrapped) {
this.wrapped = wrapped;
}
@Override
public Response execute(Request request) {
Response response = wrapped.execute(request);
if (response.getStatusCode() == 401) {
authenticate();
response = wrapped.execute(request);
}
return response;
}
protected void authenticate() {
// ...
}
}
现在REST客户端不用再操心认证的事了,这个由AuthenticatingHttpClient来擦屁股。
示例2:缓存授权决策
好的,现在用户登录进来了,REST服务器也知道了他的身份。它来决定谁能访问某个资源,谁不能。
也就是说,它得实现用户认证,比如使用XACML。这样的话,需要有一个策略决策点(PDP,Policy Decision Point)来处理请求。
权限检查通常开销都很大,尤其当权限粒度变得很细并且访问策略很复杂的时候。访问策略通常不会频繁变更,因此很适合缓存起来。
这是装饰者模式另一个很适合的场景:
public class CachingPdp implements Pdp {
private final Pdp wrapped;
public CachingPdp(Pdp wrapped) {
this.wrapped = wrapped;
}
@Override
public ResponseContext decide(
RequestContext request) {
ResponseContext response = getCached(request);
if (response == null) {
response = wrapped.decide(request);
cache(request, response);
}
return response;
}
protected ResponseContext getCached(
RequestContext request) {
// ...
}
protected void cache(RequestContext request,
ResponseContext response) {
// ...
}
}
这个代码和第一个例子很像,这也是为啥叫它装饰者模式的原因。
从这两个例子里相信你也猜到了,装饰者模式非常适合用在横切关注点,比如认证的安全特性,授权,审计,当然它能派上用场的地方远不止这些。
如果你上点心的话,我相信你一定会发现更多适合这个模式的场景。