Spring框架中的设计模式(五)

通过之前四篇文章,我们看到Spring采用了大量对象创建和结构设计模式。这些大都不是行为设计模式,作为该系列最后一篇关于Spring设计模式的文章,本文再讲两个行为设计模式。

这篇文章我们会讲两个行为设计模式:命令模式和访问者模式。

Spring 设计模式 - 命令模式command

本文第一个我们要讲述的模式是命令模式。它允许将一个请求(request)封装到一个对象然后将其跟一个回调动作(callback action)关联。请求(request)封装到命令(command)对象中,而请求的结果会被转发给接收者(receiver)。命令并不自己执行而是由一个调用者(invoker)执行。为了更好地理解这一思路的要点,想象一下管理一台服务器的一个场景。管理员(invoker)在以命令行(command)方式启动了某些操作,而这些命令行的结果被传输到了被管理的服务器上(receiver)。这里要感谢的是终端,它在这里可以被认为是一个客户端。让我们把这个例子写成一个JUnit测试用例:

public class CommandTest {

  // This test method is a client
  @Test
  public void test() {
    Administrator admin = new Administrator();
    Server server = new Server();

    // start Apache
    admin.setCommand(new StartApache(server));
    admin.typeEnter();

    // start Tomcat
    admin.setCommand(new StartTomcat(server));
    admin.typeEnter();

    // check executed commands
    int executed = server.getExecutedCommands().size();
    assertTrue("Two commands should be executed but only "+
      executed+ " were", executed == 2);
  }

}

// commands
abstract class ServerCommand {

  protected Server server;

  public ServerCommand(Server server) {
    this.server = server;
  }

  public abstract void execute();
}

class StartTomcat extends ServerCommand {

  public StartTomcat(Server server) {
    super(server);
  }

  @Override
  public void execute() {
    server.launchCommand("sudo service tomcat7 start");
  }
}

class StartApache extends ServerCommand {

  public StartApache(Server server) {
    super(server);
  }

  @Override
  public void execute() {
    server.launchCommand("sudo service apache2 start");
  }
}

// invoker
class Administrator {

  private ServerCommand command;

  public void setCommand(ServerCommand command) {
    this.command = command;
  }

  public void typeEnter() {
    this.command.execute();
  }

}

// receiver
class Server {

  // as in common terminals, we store executed commands in history
  private List<String> executedCommands = new ArrayList<String>();

  public void launchCommand(String command) {
    System.out.println("Executing: "+command+" on server");
    this.executedCommands.add(command);
  }

  public List<String> getExecutedCommands() {
    return this.executedCommands;
  }

}

这个测试用例应该能够通过并且输出两个命令:

Executing: sudo service apache2 start on server
Executing: sudo service tomcat7 start on server

命令模式不仅能够封装请求request(ServerCommand)发送给接收者receiver(Server),而且能够更好地处理请求。在上面的例子中,将命令执行做一个历史记录可以认为是对请求的一个更好的处理。Spring中,我们把命令设计模式抽取到了bean工厂的后置处理器(beans factory post processor)中。这些后置处理器会被应用上下文在bean创建时执行用来对bean做一些修改(如果你想了解关于后置处理器更多的内容,请看一下关于bean工厂后置处理器的文章)。

当我们从上面的命令模式逻辑切换到Spring的bean工厂后置处理器时,我们可以观察到如下角色扮演:后置处理器bean,也就是BeanFactoryPostProcessor接口的实现类,扮演了命令command,org.springframework.context.support.PostProcessorRegistrationDelegate是调用者invoker
(他会执行所有注册的后置处理器bean的postProcessBeanFactory方法),接收者receiverorg.springframework.beans.factory.config.ConfigurableListableBeanFactory,其元素,也就是各个bean会在它们被构造被修改(比如: bean的属性可以在bean实例初始化之前被修改)。

另外,在我们的JUnit例子中,我们也演示了”更好地处理”请求这一点,也就是做一个命令执行历史记录。类似地,PostProcessorRegistrationDelegate里面包含一个内部类BeanPostProcessorChecker,它会在一个bean不符合被处理的条件时日志输出该情况。

Spring 设计模式 - 访问者模式visitor

第二种模式我们来讲访问者模式visitor。该模式背后的思想在于它使一个对象变成其他类型的对象可访问(visitable)。在这个简短的定义之后,你可能可以想到,使用此模式的对象会被堪称访问者(visitor)或者时可访问对象(object visitable),前者(访问者)访问后者(可访问对象)。,这个模式真实世界中的一个例子是机修工,他会检查汽车部件,比如轮胎,刹车和引擎,然后下结论说这车还能不能用。我们使用JUnit看一下这个例子:

public class VisitorTest {

  @Test
  public void test() {
    CarComponent car = new Car();
    Mechanic mechanic = new QualifiedMechanic();
    car.accept(mechanic);
    assertTrue("After qualified mechanics visit, the car should be broken",
      car.isBroken());
    Mechanic nonqualifiedMechanic = new NonQualifiedMechanic();
    car.accept(nonqualifiedMechanic);
    assertFalse("Car shouldn't be broken becase non qualified mechanic " +
      " can't see breakdowns", car.isBroken());
  }

}

// visitor
interface Mechanic {
  public void visit(CarComponent element);
  public String getName();
}

class QualifiedMechanic implements Mechanic {

  @Override
  public void visit(CarComponent element) {
    element.setBroken(true);
  }

  @Override
  public String getName() {
    return "qualified";
  }
}

class NonQualifiedMechanic implements Mechanic {

  @Override
  public void visit(CarComponent element) {
    element.setBroken(true);
  }

  @Override
  public String getName() {
    return "unqualified";
  }
}

// visitable
abstract class CarComponent {
  protected boolean broken;

  public abstract void accept(Mechanic mechanic);

  public void setBroken(boolean broken) {
    this.broken = broken;
  }

  public boolean isBroken() {
    return this.broken;
  }
}

class Car extends CarComponent {

  private boolean broken = false;
  private CarComponent[] components;

  public Car() {
    components = new CarComponent[] {
      new Wheels(), new Engine(), new Brake()
    };
  }

  @Override
  public void accept(Mechanic mechanic) {
    this.broken = false;
    if (mechanic.getName().equals("qualified")) {
      int i = 0;
      while (i < components.length && this.broken == false) {
        CarComponent component = components[i];
        mechanic.visit(component);
        this.broken = component.isBroken();
        i++;
      }
    }
    // if mechanic isn't qualified, we suppose that 
    // he isn't able to see breakdowns and so 
    // he considers the car as no broken 
    // (even if the car is broken)
  }

  @Override
  public boolean isBroken() {
          return this.broken;
  }
}

class Wheels extends CarComponent {

  @Override
  public void accept(Mechanic mechanic) {
    mechanic.visit(this);
  }
}

class Engine extends CarComponent {

  @Override
  public void accept(Mechanic mechanic) {
    mechanic.visit(this);
  }
}

class Brake extends CarComponent {

  @Override
  public void accept(Mechanic mechanic) {
    mechanic.visit(this);
  }
}

这个例子里,我们看到有两个机修工Mechanic(visitor):一个合格的机修工和一个不合格的机修工。他们看到的可访问对象是车Car。通过车的accept方法来决定对访问者visitor应用哪种策略。当访问者合格时,Car允许他分析所有部件。而如果访问者不合格,Car会认为访问者的介入无效,拒绝它分析各个部件,而最终通过isBroken()方法返回一个false。Spring在bean配置中使用了访问者设计模式。想看到这一点,我们可以看看用来分析bean元数据并将它们变成字符串(例子:XML属性中的作用域或者工厂方法名字)或者对象(例子:构造函数定义中参数)的org.springframework.beans.factory.config.BeanDefinitionVisitor对象。而所处理后的值会被设置到所分析的bean对应的BeanDefinition实例中去。想知道它怎么工作的,看下面BeanDefinitionVisitor的的一个代码片段:

/**
 * Traverse the given BeanDefinition object and the MutablePropertyValues
 * and ConstructorArgumentValues contained in them.
 * @param beanDefinition the BeanDefinition object to traverse
 * @see #resolveStringValue(String)
 */
public void visitBeanDefinition(BeanDefinition beanDefinition) {
  visitParentName(beanDefinition);
  visitBeanClassName(beanDefinition);
  visitFactoryBeanName(beanDefinition);
  visitFactoryMethodName(beanDefinition);
  visitScope(beanDefinition);
  visitPropertyValues(beanDefinition.getPropertyValues());
  ConstructorArgumentValues cas = beanDefinition.
    getConstructorArgumentValues();
  visitIndexedArgumentValues(cas.
    getIndexedArgumentValues());
  visitGenericArgumentValues(cas.
    getGenericArgumentValues());
}

protected void visitParentName(BeanDefinition beanDefinition) {
  String parentName = beanDefinition.getParentName();
  if (parentName != null) {
    String resolvedName = resolveStringValue(parentName);
    if (!parentName.equals(resolvedName)) {
      beanDefinition.setParentName(resolvedName);
    }
  }
}

这个例子中,只有访问方法,没有跟我们上面机修工例子里面类似的合格/不合格机修工的访问者控制机制。这里访问者的访问分析给定bean定义BeanDefinition对象的参数并将其替换成解决之后的对象。

总结

这篇文章,该系列关于Spring设计模式的的最后一篇,我们讲了两种行为设计模式:命令设计模式,用于对bean工厂进行后置处理;和访问者模式,用于将bean定义中的参数转变成对象领域的参数(字符串或者对象)。

英文原文

该系列文章目录

Spring框架中的设计模式(五)
Spring框架中的设计模式(四)
Spring框架中的设计模式(三)
Spring框架中的设计模式(二)
Spring框架中的设计模式(一)

猜你喜欢

转载自blog.csdn.net/andy_zhang2007/article/details/79807097
今日推荐