• 经典伴读_GOF设计模式_行为模式(上)


    经典伴读系列文章,不是读书笔记,自己的理解加上实际项目中运用,旨在5天读懂这本书。如果这篇文章对您有些用处,请点赞告诉我O(∩_∩)O。
    在这里插入图片描述

    行为模式

    GOF中23种设计模式从用途上分为三类,第三类是行为模式,描述的是算法和对象间职责的分配,主要是对象或类之间的通信模式。

    Chain Of Responsibility责任链

    使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这 些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

    1、GOF从UI页面的事件冒泡入手解释责任链,一个终端应用(Application),点击弹窗(Dialog)中的按钮(Button),按照事件冒泡规则(从内往外),请求传导应该是Button->Dialog->Application。这就是一种责任链。天塌了也有个高的顶,差不多也是这个意思,那么这样的一条责任链应该怎么构造。
    在这里插入图片描述

    	public static abstract class Handler {
            private Handler successor; //后继者,是不是叫next更合适些
    
            public void handleRequest(Request request) { //处理请求
                if (successor != null) {
                    successor.handleRequest(request);
                }
            }
    
            public Handler getSuccessor() {
                return successor;
            }
    
            public void setSuccessor(Handler successor) {
                this.successor = successor;
            }
        }
    
        public static class Request {} //封装请求参数
    
        public static class ConcreteHandler1 extends Handler {
            @Override
            public void handleRequest(Request request) {
                System.out.println("ConcreteHandler1处理请求");
                super.handleRequest(request); //往后继续请求
            }
        }
    
        public static class ConcreteHandler2 extends Handler {
            @Override
            public void handleRequest(Request request) {
                System.out.println("ConcreteHandler2处理请求");
                super.handleRequest(request); //往后继续请求
            }
        }
    
        public static void main(String[] args) {
            Handler handler1 = new ConcreteHandler1();
            Handler handler2 = new ConcreteHandler2();
            handler1.setSuccessor(handler2); //构建责任链:handler1->handler2
    
            Request request = new Request();
            handler1.handleRequest(request); //传导请求handler1->handler2
            handler2.handleRequest(request); //只请求handler2
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    输出:
    ConcreteHandler1处理请求
    ConcreteHandler2处理请求
    ConcreteHandler2处理请求

    2、实际开发中链式处理的场景不少,如:过滤器,拦截器。看看它们又是如何实现责任链。

    • 过滤器Filter核心方法:void doFilter(ServletRequest var1, ServletResponse var2, FilterChain var3)
    • doFilter三个入参分别表示:请求参数,响应结果,过滤链,我们重点看FilterChain。
    • 带filter的servlet请求链为:filter1 -> filter2 -> … filtern -> servlet ,这样一个请求链如何实现呢?

    ApplicationFilterChain是FilterChain的具体实现:

    	......
    	private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];
    	private int pos = 0;
        private int n = 0;
        private Servlet servlet = null;
        ......
    	private void internalDoFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
            if (this.pos < this.n) {
                ApplicationFilterConfig filterConfig = this.filters[this.pos++];
    			......
                    Filter filter = filterConfig.getFilter();
                    ......
                        filter.doFilter(request, response, this);
                     ......
            } else {
                  	......
                        this.servlet.service(request, response);
                   ,,,,,,
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    其中的精髓学到了么,我们给起个名字责任链2.0,将责任链从处理类中单独抽象出来,如:HandlerChain。
    在这里插入图片描述

    	public static class Request {} //封装请求参数
    
        interface Handler {
            void handleRequest(Request request, HandlerChain handlerChain);
        }
    
        public static class HandlerChain {
            private List<Handler> handlers = new ArrayList<>();
            private int pos = 0;
    
            public void handleRequest(Request request) { //关键不同
                if (pos < handlers.size()) {
                    Handler handler = handlers.get(pos++);
                    handler.handleRequest(request, this);
                }
            }
    
            public void addHandler(Handler handler) {
                handlers.add(handler);
            }
        }
    
        public static class ConcreteHandler1 implements Handler {
            @Override
            public void handleRequest(Request request, HandlerChain handlerChain) {
                System.out.println("ConcreteHandler1处理请求");
                handlerChain.handleRequest(request);
            }
        }
    
        public static class ConcreteHandler2 implements Handler {
            @Override
            public void handleRequest(Request request, HandlerChain handlerChain) {
                System.out.println("ConcreteHandler2处理请求");
                handlerChain.handleRequest(request);
            }
        }
    
        public static void main(String[] args) {
            Handler handler1 = new ConcreteHandler1();
            Handler handler2 = new ConcreteHandler2();
    
            HandlerChain handlerChain = new HandlerChain();
            handlerChain.addHandler(handler1);
            handlerChain.addHandler(handler2); //构建责任链:handler1->handler2
            handlerChain.handleRequest(new Request());
    
            handlerChain = new HandlerChain();
            handlerChain.addHandler(handler2); //只请求handler2
            handlerChain.handleRequest(new Request());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    输出:
    ConcreteHandler1处理请求
    ConcreteHandler2处理请求
    ConcreteHandler2处理请求

    Command命令

    将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。

    定义很难理解,先看示例,如果说责任链模式可以实现事件的冒泡,那么命令模式则可以实现事件的响应。android等终端开发中最为常见。GOF也是以一个客户端程序引入,假设需要开发一个最简单的文本编辑器,左上角“文件”菜单中只有一个“新建”菜单项,点击可以新建并打开文档。
    在这里插入图片描述

    1、要开发这个客户端程序,得先选择一个漂亮的UI库,如果你是UI库的开发者,如何提供菜单与菜单项控件。

    	interface Command { //可以理解为监听器,如OnClickListener
            void execute(); //可以理解为onclick()
        }
        //菜单
        public static class Menu {
            private String label; //菜单标签
            private List<MenuItem> menuItems = new ArrayList<>();
    
            public Menu(String label) {
                this.label = label;
            }
    
            public void addItem(MenuItem menuItem) {
                menuItems.add(menuItem);
            }
        }
        //菜单项
        public static class MenuItem {
            private String label; //菜单项标签
            private Command command; //类似于监听器OnClickListener
    
            public MenuItem(String label) {
                this.label = label;
            }
    
            public void setCommand(Command command) {
                this.command = command;
            }
    
            public void click() { //点击菜单项
                command.execute(); //类似调用监听器OnClickListener的onclick方法
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    从MenuItem控件的click方法看出,这里并没有处理点击菜单项的逻辑代码,而是调用command.execute(),将点击事件传递给Command的实现者。接下来看下客户端开发人员,如何使用这个MenuItem控件。

        //最外层系统应用
        public static class Application {
            private List<Document> documents = new ArrayList<>();
    
            public void addDocument(Document document) {
                documents.add(document);
            }
    
            public void removeDocument(Document document) {
                documents.remove(document);
            }
        }
    
        //文档
        public static class Document {
            public Document() {
                System.out.println("创建文档");
            }
    
            public void open() {
                System.out.println("打开文档");
            }
        }
    
        //新建文档命令
        public static class CreateCommand implements Command {
            private Application application;
    
            public CreateCommand(Application application) {
                this.application = application;
            }
    
            @Override
            public void execute() { //点击新建文档,真正的逻辑代码在这里
                Document document = new Document();//创建文档
                application.addDocument(document);
                document.open(); //打开文档
            }
        }
    
        public static void main(String[] args) {
            //创建应用
            Application application = new Application();
    
            //创建菜单与菜单项
            Menu menu = new Menu("文件");
            MenuItem createMenuItem = new MenuItem("新建");
            menu.addItem(createMenuItem);
    
            //绑定新建命令
            CreateCommand createCommand = new CreateCommand(application);
            createMenuItem.setCommand(createCommand);
    
            //触发新建菜单项点击
            createMenuItem.click();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    输出:
    创建文档
    打开文档

    2、命令模式支持回滚,撤销
    接上例,新建的文档需要回滚,自然是要把它删除。因此创建命令中需要保留下一些创建时的数据(也称为状态status)

    	//新建文档命令
        public static class CreateCommand implements Command {
            private Application application;
            private Document document; //保留创建的文档
    
            public CreateCommand(Application application) {
                this.application = application;
            }
    
            @Override
            public void execute() {
                document = new Document();
                application.addDocument(document);
                document.open();
            }
            //回滚,撤销
            public void unexecute() {
                if (document != null) {
                    application.removeDocument(document);
                }
            }
        }
    
    	public static void main(String[] args) {
            //创建应用
            Application application = new Application();
    
            //创建菜单与菜单项
            Menu menu = new Menu("文件");
            MenuItem createMenuItem = new MenuItem("新建");
            menu.addItem(createMenuItem);
    
            //绑定新建命令
            CreateCommand createCommand = new CreateCommand(application);
            createMenuItem.setCommand(createCommand);
            createMenuItem.click();//触发新建菜单项点击
    
            //新建命令撤销
            createCommand.unexecute();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    3、命令模式支持组合命令并排队执行

    	public static class MacroCommand implements Command {
            private List<Command> commands = new ArrayList<>();
            @Override
            public void execute() {
                for (Command command : commands) { //排队执行
                    command.execute();
                }
            }
    
            public void addCommand(Command command) {
                commands.add(command);
            }
    
            public void removeCommand(Command command) {
                commands.remove(command);
            }
        }
    
        public static void main(String[] args) {
            //创建应用
            Application application = new Application();
    
            //创建菜单与菜单项
            Menu menu = new Menu("文件");
            MenuItem createMenuItem = new MenuItem("新建");
            menu.addItem(createMenuItem);
    
            //绑定新建命令
            CreateCommand createCommand1 = new CreateCommand(application);
            CreateCommand createCommand2 = new CreateCommand(application);
    		//点击一次创建两个文件(排队执行)
            MacroCommand macroCommand = new MacroCommand(); 
            macroCommand.addCommand(createCommand1);
            macroCommand.addCommand(createCommand2);
            createMenuItem.setCommand(macroCommand);
    
            //触发新建菜单项点击
            createMenuItem.click();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    输出:
    创建文档
    打开文档
    创建文档
    打开文档

    4、理解示例之后,再看下命令模式结构图(修改了下,更容易理解)。
    在这里插入图片描述
    对比的示例部分
    在这里插入图片描述
    对比之下一目了然,不用多说,ConcreteCommand中status用于回滚前保存状态,Invoke调用者是触发事件的人,Receiver接受者是实际处理事件的人。就像电视遥控器和电视的关系,点击电视遥控器,实际是电视开了。

    Interpreter解释器

    给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示 来解释语言中的句子。

    解释器模式应用场景最为固定,当一个语言需要解释执行,并且其中的语句可以表示为一个抽象语法树(AST)时,就可以使用解释器模式。
    1、先看示例,如执行“x 加 1 减 y”,首先这个语句可以表示为抽象语法树。
    在这里插入图片描述
    接下来使用解释器模式,其实就是构建语法树,以及遍历执行的过程。(注意是从根节点开始前序遍历)
    在这里插入图片描述

    	//全局上下文
        public static class Context extends HashMap<String, Integer> {
        }
    
        interface AbstractExpression {
            int interpret(Context context);
        }
    
        //变量表达式
        public static class VarExpression implements AbstractExpression {
            private String name;
    
            public VarExpression(String name) {
                this.name = name;
            }
    
            @Override
            public int interpret(Context context) {
                return context.get(name);
            }
        }
    
        //常量表达式
        public static class Constant implements AbstractExpression {
            private int value;
    
            public Constant(int value) {
                this.value = value;
            }
    
            @Override
            public int interpret(Context context) {
                return value;
            }
        }
    
        //加法表达式
        public static class AddExpression implements AbstractExpression {
            private AbstractExpression left;
            private AbstractExpression right;
    
            public AddExpression(AbstractExpression left, AbstractExpression right) {
                this.left = left;
                this.right = right;
            }
    
            @Override
            public int interpret(Context context) {
                return left.interpret(context) + right.interpret(context);
            }
        }
    
        //减法表达式
        public static class SubExpression implements AbstractExpression{
            private AbstractExpression left;
            private AbstractExpression right;
    
            public SubExpression(AbstractExpression left, AbstractExpression right) {
                this.left = left;
                this.right = right;
            }
    
            @Override
            public int interpret(Context context) {
                return left.interpret(context) - right.interpret(context);
            }
        }
    
        public static void main(String[] args) {
            //解释执行x 加 2 减 y
            Context context = new Context();
            context.put("x", 1);
            context.put("y", 3);
    
            //为了方便理解,这里先参照语法树图,从根节点开始手动构建,后面有扩展
            AbstractExpression root = new SubExpression(
                    new AddExpression(
                            new VarExpression("x"), new Constant(2)),
                    new VarExpression("y")
            );
    
            //递归解释执行
            int result = root.interpret(context);
            System.out.println("result:" + result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85

    输出:
    result:0

    2、再来理解解释器模式结构
    在这里插入图片描述
    TerminalExpression终结符表达式,即不能再被分解的元素,如x,1等。
    NonterminalExpression非终结符表达式,即可以被再分解的元素,如x+1等。
    Context存放全局信息,如变量x,y的值,需要提前设置在Context中,以供递归时使用。
    对比示例,其中VarExpression变量表达式如x,y,Constant常量如1,都是TerminalExpression,而AddExpression加法表达式和SubExpression减法表达式,则是NonterminalExpression。

    3、最后扩展一下,根据字符串构建简单的语法树。

    	//判断是不是非终结符表达式
        public static boolean isNonterminalExpression(String token) {
            return token.equals("加") || token.equals("减");
        }
    
        //创建终结符表达式
        public static AbstractExpression createTerminalExpression(String token) {
            AbstractExpression terminalExpression = null;
            if (token.chars().allMatch(Character::isDigit)) { //数字
                terminalExpression = new Constant(Integer.parseInt(token));
            } else { //字母
                terminalExpression = new VarExpression(token);
            }
            return terminalExpression;
        }
    
        //创建非终结符表达式
        public static AbstractExpression createNonterminalExpression(String token, AbstractExpression left, AbstractExpression right) {
            AbstractExpression nonterminalExpression = null;
            if ("加".equals(token)) {
                nonterminalExpression = new AddExpression(left, right);
            } else if ("减".equals(token)) {
                nonterminalExpression = new SubExpression(left, right);
            }
            return nonterminalExpression;
        }
    
        //构建抽象语法树
        public static AbstractExpression buildSyntaxTree(String text) {
            Stack<AbstractExpression> stack = new Stack<>();
            StringTokenizer tokenizer = new StringTokenizer(text);
            AbstractExpression current, left, right;
            while (tokenizer.hasMoreTokens()) {
                String token = tokenizer.nextToken();
                if (isNonterminalExpression(token)) { //创建非终结符表达式
                    left = stack.pop();
                    right = createTerminalExpression(tokenizer.nextToken());
                    current = createNonterminalExpression(token, left, right);
                } else { //创建终结符表达式
                    current = createTerminalExpression(token);
                }
                stack.push(current);
            }
            return stack.pop();
        }
    
        public static void main(String[] args) {
            //解释执行x 加 2 减 y
            Context context = new Context();
            context.put("x", 1);
            context.put("y", 3);
    
            //根据字符串文本构建抽象语法树
            AbstractExpression root = buildSyntaxTree("x 加 2 减 y");
    
            //递归解释执行
            int result = root.interpret(context);
            System.out.println("result:" + result);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    Iterator迭代器

    提供一种方法顺序访问一个聚合对象中各个元素 , 而又不需暴露该对象的内部表示。

    当你想要迭代查询时,可以使用迭代器(听起来像废话),如迭代集合中的元素,迭代学校里的班级,迭代班级里的学生等,不限于集合对象都可以使用迭代器模式。
    1、将迭代查询这件事从集合各种操作中统一抽象出来就是GOF对迭代器模式的初衷。
    在这里插入图片描述
    ConcreteIterator是迭代器即迭代方式,多种迭代方式就有多种迭代器。ConcreteAggregate是聚合类加入了迭代器工厂。在实际代码中,迭代器只是聚合类的查询功能,它们生命周期相同,因此常以内部类形式出现。如:

    	//迭代器
        interface Iterator {
            Object first();
            Object next();
            boolean isDone();
            Object currentItem();
        }
    
        //聚合类(不如叫迭代器工厂)
        interface Aggregate {
            Iterator createIterator();
        }
    
        //具体聚合类
        public static class Array implements Aggregate{
            private int[] datas;
    
            public Array(int[] datas) {
                this.datas = datas;
            }
    
            @Override
            public Iterator createIterator() {
                return new Iterator() { //具体的迭代器(匿名内部类实现)
                    private int index = 0;
    
                    @Override
                    public Object first() {
                        return Array.this.datas[0];
                    }
    
                    @Override
                    public Object next() {
                        return Array.this.datas[index++];
                    }
    
                    @Override
                    public boolean isDone() {
                        return index == Array.this.datas.length;
                    }
    
                    @Override
                    public Object currentItem() {
                        return Array.this.datas[index];
                    }
                };
            }
        }
    
        public static void main(String[] args) {
            Array arr = new Array(new int[]{1,2,3,4,5});
            Iterator iterator = arr.createIterator();
            while (!iterator.isDone()) {
                System.out.println(iterator.next());
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    2、JDK中对迭代模式已有支持,同样有Iterator和Iterable接口(对应Aggregate),它们名字相似,相信现在你不会再搞混,前者是迭代器,后者是迭代器工厂。

    Mediator中介者

    用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。

    GOF讲到的中介者,以客户端程序的页面为例,他们认为页面上多种控件交互,必会造成依赖关系混乱,你中有我,我中有你,于是引入中介者,当所有的控件都只依赖中介者,就解决了依赖混乱的问题。千万不要和命令模式的事件监听搞混,准确说,中介者其实是一种事件总线的思想。
    示例,假设页面上有文本框TextView,下拉选框Spinner,清空按钮Button,用户选择下拉选框,文本框及时显示选择信息,用户点击清空按钮,则清空文本框。那么不用事件监听,而改用事件总线如何设计。
    在这里插入图片描述
    Dialog是中介者,Spinner,TextView,Button等控件都是同事类,Mediator和所有Colleague相互引用,Colleague之间却互不相识,这就是中介者模式的特点。

        //中介者
        interface Mediator {
            void changeEvent(Object event);
        }
    
        //具体中介者
        public static class Dialog implements Mediator{
            private TextView mTextView;
            private Button mCleanBtn;
            private Spinner mSpinner;
    
            public Dialog() {
                mTextView = new TextView(this);
                mCleanBtn = new Button(this);
                mSpinner = new Spinner(this);
            }
    
            @Override
            public void changeEvent(Object event) { //这里是中介者模式的核心,响应事件
                if (event instanceof SpinnerSelectEvent) {
                    String selectValue = mSpinner.getValue();
                    mTextView.setText(selectValue);
                } else if (event instanceof CleanTextViewEvent) {
                    mTextView.setText("");
                }
    
            }
    
            //模拟页面测试
            public static void main(String[] args) {
                Dialog dialog = new Dialog();
                dialog.mSpinner.select("武汉"); //选择武汉
                System.out.println("TextView显示:" + dialog.mTextView.getText());
                dialog.mCleanBtn.click(); //清空按钮点击
                System.out.println("TextView显示:" + dialog.mTextView.getText());
            }
        }
    
        //同事类
        public static abstract class Colleague {
            private Mediator mediator;
    
            public Colleague(Mediator mediator) {
                this.mediator = mediator;
            }
    
            public Mediator getMediator() {
                return mediator;
            }
        }
    
        //下拉选框选中事件
        public static class SpinnerSelectEvent{}
        //下拉选框
        public static class Spinner extends Colleague{
            private String value;
    
            public Spinner(Mediator mediator) {
                super(mediator);
            }
    
            public String getValue() {
                return value;
            }
    
            //模拟页面选中
            public void select(String value) {
                this.value = value;
                getMediator().changeEvent(new SpinnerSelectEvent()); //向总线发送事件
            }
        }
    
        //清空文本框事件
        public static class CleanTextViewEvent{}
        //清空按钮
        public static class Button extends Colleague {
            public Button(Mediator mediator) {
                super(mediator);
            }
    
            //模拟页面点击
            public void click() {
                getMediator().changeEvent(new CleanTextViewEvent());//向总线发送事件
            }
        }
    
        //文本
        public static class TextView extends Colleague {
            private String text = "";
    
            public String getText() {
                return text;
            }
    
            public void setText(String text) {
                this.text = text;
            }
    
            public TextView(Mediator mediator) {
                super(mediator);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    输出:
    TextView显示:武汉
    TextView显示:

    Memento备忘录

    在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。 这样以后就可将该对象恢复到原先保存的状态。

    GOF依然以客户端程序为例(稍有改动),当一个图形从A点移动到B点,此时撤销,图形则回到A点,当需要先保存,再还原到原始状态的场景,可以使用备忘录模式。
    在这里插入图片描述
    1、备忘录模式结合命令模式,实现客户端操作:
    (1)当发出移动命令MoveCommand,图形控件View开始移动,先保存原始位置point到备忘录Memento,再改变位置到targetPoint。
    (2)当发出撤销命令,图形控件View从备忘录中获取原始位置point,然后改变位置。

    	public static class Point {
            private int x = 0;
            private int y = 0;
    
            public Point(int x, int y) {
                this.x = x;
                this.y = y;
            }
    
            @Override
            public String toString() {
                return "("+x + ","+ y + ")";
            }
        }
    
        //备忘录
        public static class Memento {
            private Point status;
    
            public Point getStatus() {
                return status;
            }
    
            public void setStatus(Point status) {
                this.status = status;
            }
        }
    
        //图形,作为原发器Originator
        public static class View {
            private Point point;
    
            public void setPoint(Point point) {
                this.point = point;
            }
    
            //模拟图形渲染
            public void render() {
                System.out.println("图形位置" + point);
            }
    
            //备忘录保存当前状态
            public Memento createMemento() {
                Memento memento = new Memento();
                memento.setStatus(point);
                return memento;
            }
    
            //从备忘录当中恢复状态
            public void setMemento(Memento memento) {
                point = memento.getStatus();
            }
        }
    
        //移动命令,作为负责人Caretaker
        public static class MoveCommand {
            private View view;
            private Point targetPoint;
            private Memento memento;
    
            public MoveCommand(View view, Point targetPoint) {
                this.view = view;
                this.targetPoint = targetPoint;
            }
    
            public void execute() {
                memento = view.createMemento(); //移动之前存储位置
                view.setPoint(targetPoint);//移动到指定位置
                view.render(); //渲染图形
            }
    
            public void unexecute() {
                view.setMemento(memento); //恢复状态
                view.render(); //重新渲染;
            }
        }
    
        public static void main(String[] args) {
            View view = new View();
            view.setPoint(new Point(50, 50)); //图形初始位置50, 50
    
            //模拟用户移动图形到100, 100
            MoveCommand moveCommand2 = new MoveCommand(view, new Point(100, 100));
            moveCommand2.execute();
    
            //模拟用户撤销回到上一步50, 50
            moveCommand2.unexecute();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    输出:
    图形位置(100,100)
    图形位置(50,50)

    2、理解示例后,对比理解备忘录模式结构图
    在这里插入图片描述
    (1)备忘录Memento用来保存状态.
    (2)当一个对象需要在业务操作中需要保存状态,则在原有的的属性方法上加入创建和还原备忘录的方法,就变成了原发器Originator,如View。
    (3)保存出来的状态存放的位置就是负责人Caretaker,如MoveCommand。这个角色很灵活,可以用客户端代替。
    (4)当需要还原的不止一步时,Caretaker中就需要使用memento集合保存状态列表。上例中MoveCommand不适用,一个命令只能改变一次状态。

    未完待续

  • 相关阅读:
    C 语言 二分法 查找。
    [杂货铺系列]SpringBoot集成ElasticJob遇到的版本不兼容问题
    BP新增修改通信或独立地址通讯
    MySQL常用字符串函数
    【RT-Thread】nxp rt10xx 设备驱动框架之--adc搭建和使用
    ros入门之一,发布者,订阅者的python文件创建以及最后的终端执行(含python文件代码)
    数据链路层的七七八八
    如何看待程序员领域的“内卷”现象?
    使用Postman并发测试接口&关于RedisTemplate线程安全的一些理解
    Linux中Tomcat发布war包后无法正常访问非静态资源
  • 原文地址:https://blog.csdn.net/flyzing/article/details/126366926