Command概述

Command - 图1

UML概述

Command - 图2

玩具代码案例1 - 模拟Restful框架的设计

手写一个简易的Web Controller MVC

命令注解

RestfulAnnotation

  1. package online.javabook.gof.behavioral.patterns2.command2.restful.commands;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.METHOD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface RestfulAnnotation {
  9. String path();
  10. HttpMethod httpMethod();
  11. }
  12. public enum HttpMethod {
  13. GET,
  14. POST,
  15. PUT,
  16. DELETE
  17. }

命令的实现类

GroupController

  1. package online.javabook.gof.behavioral.patterns2.command2.restful.controller;
  2. import online.javabook.gof.behavioral.patterns2.command2.restful.commands.HttpMethod;
  3. import online.javabook.gof.behavioral.patterns2.command2.restful.commands.RestfulAnnotation;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. public class GroupController {
  7. @RestfulAnnotation(path = "/api/groups", httpMethod = HttpMethod.POST)
  8. public void createUser(HttpServletRequest request, HttpServletResponse response) {
  9. System.out.println("create group");
  10. }
  11. @RestfulAnnotation(path = "/api/groups", httpMethod = HttpMethod.GET)
  12. public void readUser(HttpServletRequest request, HttpServletResponse response) {
  13. System.out.println("read group");
  14. }
  15. @RestfulAnnotation(path = "/api/groups", httpMethod = HttpMethod.PUT)
  16. public void updateUser(HttpServletRequest request, HttpServletResponse response) {
  17. System.out.println("update group");
  18. }
  19. @RestfulAnnotation(path = "/api/groups", httpMethod = HttpMethod.DELETE)
  20. public void deleteUser(HttpServletRequest request, HttpServletResponse response) {
  21. System.out.println("delete group");
  22. }
  23. }

UserController

  1. package online.javabook.gof.behavioral.patterns2.command2.restful.controller;
  2. import online.javabook.gof.behavioral.patterns2.command2.restful.commands.HttpMethod;
  3. import online.javabook.gof.behavioral.patterns2.command2.restful.commands.RestfulAnnotation;
  4. import javax.servlet.http.HttpServletRequest;
  5. import javax.servlet.http.HttpServletResponse;
  6. public class UserController {
  7. @RestfulAnnotation(path = "/api/users", httpMethod = HttpMethod.POST)
  8. public void createUser(HttpServletRequest request, HttpServletResponse response) {
  9. System.out.println("create user");
  10. }
  11. @RestfulAnnotation(path = "/api/users", httpMethod = HttpMethod.GET)
  12. public void readUser(HttpServletRequest request, HttpServletResponse response) {
  13. System.out.println("read user");
  14. }
  15. @RestfulAnnotation(path = "/api/users", httpMethod = HttpMethod.PUT)
  16. public void updateUser(HttpServletRequest request, HttpServletResponse response) {
  17. System.out.println("update user");
  18. }
  19. @RestfulAnnotation(path = "/api/users", httpMethod = HttpMethod.DELETE)
  20. public void deleteUser(HttpServletRequest request, HttpServletResponse response) {
  21. System.out.println("delete user");
  22. }
  23. }

命令的注册器和执行器

RestfulRegister

  1. package online.javabook.gof.behavioral.patterns2.command2.restful.register;
  2. import online.javabook.gof.behavioral.patterns2.command2.restful.commands.HttpMethod;
  3. import online.javabook.gof.behavioral.patterns2.command2.restful.commands.RestfulInstance;
  4. import online.javabook.gof.behavioral.patterns2.command2.restful.commands.RestfulAnnotation;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. import java.lang.reflect.InvocationTargetException;
  8. import java.lang.reflect.Method;
  9. import java.util.HashMap;
  10. import java.util.Map;
  11. public class RestfulRegister {
  12. private Map<String, RestfulInstance> restfulApiHashMap = new HashMap<>();
  13. public void register(Class controllerClass) throws IllegalAccessException, InstantiationException {
  14. Object controller = controllerClass.newInstance();
  15. Method[] methods = controllerClass.getMethods();
  16. for (Method method : methods) {
  17. RestfulAnnotation annotation = method.getAnnotation(RestfulAnnotation.class);
  18. if(annotation!= null) {
  19. String restfulApiPath = annotation.path() + ":" + annotation.httpMethod();
  20. RestfulInstance restfulApi = new RestfulInstance(controller, method);
  21. restfulApiHashMap.put(restfulApiPath, restfulApi);
  22. }
  23. }
  24. }
  25. private RestfulInstance discover(String path, HttpMethod httpMethod) {
  26. String restfulApiPath = path + ":" + httpMethod;
  27. return restfulApiHashMap.get(restfulApiPath);
  28. }
  29. public void doRestful(String path, HttpMethod httpMethod, HttpServletRequest request, HttpServletResponse response) throws InvocationTargetException, IllegalAccessException {
  30. discover(path, httpMethod).execute(request, response);
  31. }
  32. }

RestfulInstance

  1. package online.javabook.gof.behavioral.patterns2.command2.restful.commands;
  2. import javax.servlet.http.HttpServletRequest;
  3. import javax.servlet.http.HttpServletResponse;
  4. import java.lang.reflect.InvocationTargetException;
  5. import java.lang.reflect.Method;
  6. public class RestfulInstance {
  7. private Object controller;
  8. private Method method;
  9. public RestfulInstance(Object controller, Method method) {
  10. this.controller = controller;
  11. this.method = method;
  12. }
  13. public void execute(HttpServletRequest request, HttpServletResponse response) throws InvocationTargetException, IllegalAccessException {
  14. method.invoke(controller, request, response);
  15. }
  16. }

命令执行者与命令

Main

  1. package online.javabook.gof.behavioral.patterns2.command2.restful.app;
  2. import online.javabook.gof.behavioral.patterns2.command2.restful.commands.HttpMethod;
  3. import online.javabook.gof.behavioral.patterns2.command2.restful.controller.GroupController;
  4. import online.javabook.gof.behavioral.patterns2.command2.restful.controller.UserController;
  5. import online.javabook.gof.behavioral.patterns2.command2.restful.register.RestfulRegister;
  6. import org.mockito.Mockito;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.lang.reflect.InvocationTargetException;
  10. public class Main {
  11. public static void main(String[] args) throws InstantiationException, IllegalAccessException, InvocationTargetException {
  12. RestfulRegister restfulRegister = new RestfulRegister();
  13. restfulRegister.register(UserController.class);
  14. restfulRegister.register(GroupController.class);
  15. HttpServletRequest mockedRequest = Mockito.mock(HttpServletRequest.class);
  16. HttpServletResponse mockedResponse = Mockito.mock(HttpServletResponse.class);
  17. restfulRegister.doRestful("/api/users", HttpMethod.POST, mockedRequest, mockedResponse);
  18. restfulRegister.doRestful("/api/users", HttpMethod.GET, mockedRequest, mockedResponse);
  19. restfulRegister.doRestful("/api/users", HttpMethod.PUT, mockedRequest, mockedResponse);
  20. restfulRegister.doRestful("/api/users", HttpMethod.DELETE, mockedRequest, mockedResponse);
  21. restfulRegister.doRestful("/api/groups", HttpMethod.POST, mockedRequest, mockedResponse);
  22. restfulRegister.doRestful("/api/groups", HttpMethod.GET, mockedRequest, mockedResponse);
  23. restfulRegister.doRestful("/api/groups", HttpMethod.PUT, mockedRequest, mockedResponse);
  24. restfulRegister.doRestful("/api/groups", HttpMethod.DELETE, mockedRequest, mockedResponse);
  25. }
  26. }

Console

  1. create user
  2. read user
  3. update user
  4. delete user
  5. create group
  6. read group
  7. update group
  8. delete group

玩具代码案例2 - 模拟客户端编辑器的命令菜单

事实上你会发现GOF中的大多数设计模式都和客户端应用有着非常大的渊源,可能是设计模式提出的时候还是客户端应用的黄金时期。命令模式通常会在命令接口中提供可供操作的上下文数据,比如这个案例中的Editor。

命令接口

ICommand

  1. package online.javabook.gof.behavioral.patterns2.command1.editor.commands;
  2. import online.javabook.gof.behavioral.patterns2.command1.editor.context.Editor;
  3. public interface ICommand {
  4. void execute(Editor editor);
  5. }

命令的实现类

CommandAnnotation

命令注解类型,我们在传统的命令模式中增加了注解来动态处理命令。设计模式之初Java应该还没有提供注解的支持。注解融入到设计模式之中也是非常方便的。

  1. package online.javabook.gof.behavioral.patterns2.command1.editor.commands;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.TYPE)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface CommandAnnotation {
  9. public String name();
  10. }

CopyCommand

  1. package online.javabook.gof.behavioral.patterns2.command1.editor.commands;
  2. import online.javabook.gof.behavioral.patterns2.command1.editor.context.Editor;
  3. @CommandAnnotation(name="copy")
  4. public class CopyCommand implements ICommand {
  5. @Override
  6. public void execute(Editor editor) {
  7. System.out.println("click copy button");
  8. }
  9. }

CutCommand

  1. package online.javabook.gof.behavioral.patterns2.command1.editor.commands;
  2. import online.javabook.gof.behavioral.patterns2.command1.editor.context.Editor;
  3. @CommandAnnotation(name="cut")
  4. public class CutCommand implements ICommand {
  5. @Override
  6. public void execute(Editor editor) {
  7. System.out.println("click cut button");
  8. }
  9. }

NewCommand

package online.javabook.gof.behavioral.patterns2.command1.editor.commands;

import online.javabook.gof.behavioral.patterns2.command1.editor.context.Editor;

@CommandAnnotation(name="new")
public class NewCommand implements ICommand {
    @Override
    public void execute(Editor editor) {
        System.out.println("click new button");
    }
}

OpenCommand

package online.javabook.gof.behavioral.patterns2.command1.editor.commands;

import online.javabook.gof.behavioral.patterns2.command1.editor.context.Editor;

@CommandAnnotation(name="open")
public class OpenCommand implements ICommand {
    @Override
    public void execute(Editor editor) {
        System.out.println("click open button");
    }
}

PasteCommand

package online.javabook.gof.behavioral.patterns2.command1.editor.commands;

import online.javabook.gof.behavioral.patterns2.command1.editor.context.Editor;

@CommandAnnotation(name="paste")
public class PasteCommand implements ICommand {
    @Override
    public void execute(Editor editor) {
        System.out.println("click paste button");
    }
}

命令的执行器

Editor

这个案例中编辑器作为命令的容器,管理者和执行者。

package online.javabook.gof.behavioral.patterns2.command1.editor.context;

import online.javabook.gof.behavioral.patterns2.command1.editor.commands.CommandAnnotation;
import online.javabook.gof.behavioral.patterns2.command1.editor.commands.ICommand;

import java.util.HashMap;
import java.util.Map;

public class Editor {

    private Map<String, ICommand> commandRegister = new HashMap<>();

    public void registerCommand(Class<? extends ICommand> commandClass) throws IllegalAccessException, InstantiationException {
        CommandAnnotation commandAnnotation = commandClass.getAnnotation(CommandAnnotation.class);
        ICommand command = commandClass.newInstance();
        commandRegister.put(commandAnnotation.name(), command);
    }

    public ICommand discoverCommand(String commandName) {
        return commandRegister.get(commandName);
    }

    public void doCommand(String commandName) {
        discoverCommand(commandName).execute(this);
    }
}

命令执行者与命令

Main

package online.javabook.gof.behavioral.patterns2.command1.editor.app;

import online.javabook.gof.behavioral.patterns2.command1.editor.commands.*;
import online.javabook.gof.behavioral.patterns2.command1.editor.context.Editor;

public class Main {
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Editor editor = new Editor();
        editor.registerCommand(NewCommand.class);
        editor.registerCommand(OpenCommand.class);
        editor.registerCommand(CopyCommand.class);
        editor.registerCommand(CutCommand.class);
        editor.registerCommand(PasteCommand.class);

        editor.doCommand("new");
        editor.doCommand("open");
        editor.doCommand("copy");
        editor.doCommand("cut");
        editor.doCommand("paste");
    }
}

Console

click new button
click open button
click copy button
click cut button
click paste button