设计模式之装饰者模式

Published: 25 Feb 2014 Category: DesignPattern

设计模式:装饰者模式

装饰者模式是不太常用的一种设计模式。我不太明白为什么它没能流行起来,虽然它用起来很方便。装饰者模式让你能够在可控的范围内往一个对象添加功能。这个在运行时也是靠谱的,甚至在静态类型的语言里也是如此。装饰者模式可以当作子类的一个替代品。子类化在编译期的时候添加新的特性,这个改动影响到原始类的所有实例;装饰者模式能在运行时给指定的对象提供新的行为。它是坚持开闭原则的一个很好的工具。

下面的一些例子将让它看到这种模式的魅力:

示例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) {
    // ...
  }
  
}

这个代码和第一个例子很像,这也是为啥叫它装饰者模式的原因。

从这两个例子里相信你也猜到了,装饰者模式非常适合用在横切关注点,比如认证的安全特性,授权,审计,当然它能派上用场的地方远不止这些。

如果你上点心的话,我相信你一定会发现更多适合这个模式的场景。

原文链接