A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

命令模式

这篇文章描述的第一个行为设计模式是命令。它允许将请求封装在一个对象内并附加一个回调动作(每次遇到所所谓的回调大家就只需要理解为一个函数方法就好,省的去浪费那么多脑子)。请求被封装在命令对象之下,而请求的结果被发送到接收者。命令本身不是由调用者执行。为了直白了解其中的主要思想,想象一下管理服务器的情况(远程通过 ssh操作 Linux服务器)。管理员( invoker)在命令行( commands)中启动一些操作,将结果发送到服务器(接收器)。在这里,所有这一切都是由客户端的终端(也就是我们用的 xshell)来完成的。搞个 Demo来说明一下(对于命令,它的动作就是执行,对于管理员来讲,我们的动作其实就是一个回车,执不执行当然是管理员说的算了,执行交给命令对象了,服务器最后就是一个展示结果):

  • 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

命令模式不仅允许封装请求(ServerCommand)并将其传输到接收器(Server),而且还可以更好地处理给定的请求。在这里,这种更好的处理是通过存储命令的执行历史。在Spring中,我们在beanFactory后置处理器的特性中来找到指令设计模式的原理。要通过快速对它们进行定义,应用程序上下文会启动后置处理器,并可以用来对创建的bean进行一些操作(这里不打算细说了,具体的我后面会专门写一篇这方面的文章,来分析其中的源码细节)。

当我们将先前Demo里呈现的命令逻辑转换并对比到 Springbean工厂后处理器时,我们可以区分以下 actors后置处理器bean(是指实现 BeanFactoryPostProcessor接口)是命令,org.springframework.context.support.PostProcessorRegistrationDelegate是调用者(它执行 postProcessBeanFactory方法注册所有的后置处理器bean,此处看下面第二段代码)和接收器org.springframework.beans.factory.config.ConfigurableListableBeanFactory可以在元素(bean)构造初始化之前修改它们(例如:在初始化bean之前可以更改属性)。

另外,回顾下上面的那个Demo,和我们的Demo中的命令历史管理一样。 PostProcessorRegistrationDelegate包含一个内部类 BeanPostProcessorChecker,它可以记录当一个bean不符合处理条件的情况。

可以观察 PostProcessorRegistrationDelegate中的两段代码:

  • /**
  •      * BeanPostProcessor that logs an info message when a bean is created during
  •      * BeanPostProcessor instantiation, i.e. when a bean is not eligible for
  •      * getting processed by all BeanPostProcessors.
  •      */
  •     private static class BeanPostProcessorChecker implements BeanPostProcessor {
  •         private static final Log logger = LogFactory.getLog(BeanPostProcessorChecker.class);
  •         private final ConfigurableListableBeanFactory beanFactory;
  •         private final int beanPostProcessorTargetCount;
  •         public BeanPostProcessorChecker(ConfigurableListableBeanFactory beanFactory, int beanPostProcessorTargetCount) {
  •             this.beanFactory = beanFactory;
  •             this.beanPostProcessorTargetCount = beanPostProcessorTargetCount;
  •         }
  •         @Override
  •         public Object postProcessBeforeInitialization(Object bean, String beanName) {
  •             return bean;
  •         }
  •         @Override
  •         public Object postProcessAfterInitialization(Object bean, String beanName) {
  •             if (bean != null && !(bean instanceof BeanPostProcessor) && !isInfrastructureBean(beanName) &&
  •                     this.beanFactory.getBeanPostProcessorCount() < this.beanPostProcessorTargetCount) {
  •                 if (logger.isInfoEnabled()) {
  •                     logger.info("Bean '" + beanName + "' of type [" + bean.getClass() +
  •                             "] is not eligible for getting processed by all BeanPostProcessors " +
  •                             "(for example: not eligible for auto-proxying)");
  •                 }
  •             }
  •             return bean;
  •         }
  •         private boolean isInfrastructureBean(String beanName) {
  •             if (beanName != null && this.beanFactory.containsBeanDefinition(beanName)) {
  •                 BeanDefinition bd = this.beanFactory.getBeanDefinition(beanName);
  •                 return RootBeanDefinition.ROLE_INFRASTRUCTURE == bd.getRole();
  •             }
  •             return false;
  •         }
  •     }

定义后的调用,用的就是 ConfigurableListableBeanFactory的实例(看 BeanPostProcessorChecker注释):

  • public static void registerBeanPostProcessors(
  •             ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
  •         String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
  •         // Register BeanPostProcessorChecker that logs an info message when
  •         // a bean is created during BeanPostProcessor instantiation, i.e. when
  •         // a bean is not eligible for getting processed by all BeanPostProcessors.
  •         int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
  •   //BeanPostProcessorChecker
  •         beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
  •         // Separate between BeanPostProcessors that implement PriorityOrdered,
  •         // Ordered, and the rest.
  •         List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
  •         List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
  •         List<String> orderedPostProcessorNames = new ArrayList<>();
  •         List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  •         for (String ppName : postProcessorNames) {
  •             if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
  •                 BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  •                 priorityOrderedPostProcessors.add(pp);
  •                 if (pp instanceof MergedBeanDefinitionPostProcessor) {
  •                     internalPostProcessors.add(pp);
  •                 }
  •             }
  •             else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
  •                 orderedPostProcessorNames.add(ppName);
  •             }
  •             else {
  •                 nonOrderedPostProcessorNames.add(ppName);
  •             }
  •         }
  •         // First, register the BeanPostProcessors that implement PriorityOrdered.
  •         sortPostProcessors(beanFactory, priorityOrderedPostProcessors);
  •         registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
  •         // Next, register the BeanPostProcessors that implement Ordered.
  •         List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
  •         for (String ppName : orderedPostProcessorNames) {
  •             BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  •             orderedPostProcessors.add(pp);
  •             if (pp instanceof MergedBeanDefinitionPostProcessor) {
  •                 internalPostProcessors.add(pp);
  •             }
  •         }
  •         sortPostProcessors(beanFactory, orderedPostProcessors);
  •         registerBeanPostProcessors(beanFactory, orderedPostProcessors);
  •         // Now, register all regular BeanPostProcessors.
  •         List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
  •         for (String ppName : nonOrderedPostProcessorNames) {
  •             BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  •             nonOrderedPostProcessors.add(pp);
  •             if (pp instanceof MergedBeanDefinitionPostProcessor) {
  •                 internalPostProcessors.add(pp);
  •             }
  •         }
  •         registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
  •         // Finally, re-register all internal BeanPostProcessors.
  •         sortPostProcessors(beanFactory, internalPostProcessors);
  •         registerBeanPostProcessors(beanFactory, internalPostProcessors);
  •         // Re-register post-processor for detecting inner beans as ApplicationListeners,
  •         // moving it to the end of the processor chain (for picking up proxies etc).
  •         beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
  •     }

总结一个过程就是,我要BeanFactory里面得到对象(也就是为了得到一个命令的执行结果),那么,想要在得到对象的时候就已经实现了一些对其修改的想法,那么就通过后置处理器,也是就实现了后置处理器接口的beans(命令里可以通过传入不同的参数来得到不同结果,或者对命令的脚本进行修改),然后还需要一个执行者(我们在做自动化运维的时候,不止操作一个脚本,这里的 PostProcessorRegistrationDelegate就是集中来管理这些的),最后得到的结果就由 BeanFactory来展示咯。

访问者模式

接下来要介绍的一个行为设计模式是Visitor:抽象一点就是通过另一种类型的对象来使一个对象访问。在这个简短定义中,使用这个设计模式中的对象将被视为访问者或对象可被访问。第一个访问者要有可访问支持。这个模式的一个现实的例子可以是一个汽车质检员,他们检查一些汽车零件,比如轮子,制动器和发动机,以判断汽车质量是否合格。我们来做个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;
  •         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.getName().equals("qualified")来判断)。当访问者合格时,Car让他分析所有组件。如果访问者不合格,Car认为其干预是无用的,并且在方法 isBroken()中直接返回 false(其实就是为了达到一个免检的效果)。 Spring在beans配置中实现了访问者设计模式。为了观察,我们可以看看org.springframework.beans.factory.config.BeanDefinitionVisitor对象,该对象用于 解析bean元数据并将其解析为 String(例如:具有作用域或工厂方法名称的XML属性)或 Object(例如:构造函数定义中的参数)。已解析的值在与分析的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);
  •     }
  •   }
  • }

在这种情况下,他们只是访问方式,没有对访问者做任何补充的控制(在Demo里对car的质检员做了控制)。这里访问包括分析给定 BeanDefinition的参数,并将其替换为已解析对象。

在最后一篇关于Spring中设计模式的文章中,我们发现了2种行为模式: 用于处理bean工厂的后置处理的命令模式用于将定义的bean参数转换为面向对象(String或Object的实例)参数的访问者模式



0 个回复

您需要登录后才可以回帖 登录 | 加入黑马