Design patterns in Spring Framework - part 5

Through 4 previous articles, we saw that Spring adopts a big number of creational and structural design patterns. They're not so much behavioral ones and it's why we discover two of them in this article, the last one about design patterns used in Spring.

This article will describe two design patterns belonging to behavioral family: command and visitor.

Spring design pattern - command

The first one behavioral design pattern described here is command. It allows to encapsulate the request within an object and attach to it a callback action. The request is encapsulated under command object while the result of the request is transmitted to the receiver. The command isn't executed by itself but by an invoker. To better understand the main idea, imagine the situation of administrating a server. The administrator (invoker) launches some operations in command line (commands) which result is transmitted to the server (receiver). All is done thanks to terminal which can be considered here as a client. Let's write it into JUnit case:

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;
  }
        
}

The test should pass and print two commands:

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

Command pattern allows not only encapsulate request (ServerCommand) and transmit it into receiver (Server), but also handle given request better. In our case, this better handling is translated by the possibility to store history of typed commands. In Spring we retreive the principle of command design pattern in the feature of beans factory post processors. To define them quickly, post processors are launched by application context to make some of manipulations on created beans (if you want to discover this subject, please read the article about beans factory post processors).

When we switch previously presented command logic to Spring bean factory post processors, we can distinguish following actors: post processors beans (implementing BeanFactoryPostProcessor) are the commands, org.springframework.context.support.PostProcessorRegistrationDelegate is the invoker (it executes the postProcessBeanFactory method of all registered post processor beans) and the receiver org.springframework.beans.factory.config.ConfigurableListableBeanFactory which elements (beans) can be modified before theirs construction (for example: property can be changed before initialization of the bean).

In additionaly, we retreive also the aspect of "better handling", exposed as history management in our JUnit case. PostProcessorRegistrationDelegate contains a inner class, BeanPostProcessorChecker which can log the situation when one bean isn't eligible for getting processed.

Spring design pattern - visitor

The second behavioral design pattern used in Spring is Visitor. The idea hidden behind it consists on making one objects visitables by another type of objects. As you can deduct after this short definition, objects using this design pattern will be considered as visitors or as objects visitables. The first ones visit the seconds. A real world example of this pattern can be a mechanic who inspects some car parts as wheels, brake and engine to judge if the car can be used. Let's take a look on this situation programmed as JUnit test case:

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);
  }
}

In this example we can see that they're two mechanics (visitors): qualified and non-qualified. Visitable object exposed to them is Car. Through its accept method it decides which politic should be applied to the visitor. When the visitor is qualified, Car let's him to analyze all components. If the visitor isn't qualified, Car considers that its intervention is useless and returns directly false in method isBroken(). Spring implements visitor design pattern in beans configuration. To observe that, we can take a look on org.springframework.beans.factory.config.BeanDefinitionVisitor object which is used to parse bean metadata and resolve them into String (for example: XML attributes with scope or factory method name) or Object (for example: parameters in constructor definition). Resolved values are after set into BeanDefinition instance associated with analyzed bean. To see it in action, they're a code fragment from 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);
    }
  }
}

In this case, they're only visit approach, without any supplementary control on visitor as in our case of qualified and non-qualified mechanic. The visit consists on analyze a parameter of given BeanDefinition and replace it by resolved object.

In this last article about design patterns in Spring we discovered 2 behavioral patterns: command used to handle post process for bean factory and visitor used to convert defined bean parameters into object-oriented (instances of String or Object) parameters.


If you liked it, you should read:

📚 Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!