设计模式中的第三类是行为型模式,共11种,分别为:
策略、模板方法、观察者、责任链、 迭代子、备忘录、状态、命令、解释器、访问者、调停者。
本篇介绍其最后3种:解释器、访问者、调停者。
Interpreter
解释器模式
1 解释器模式是类的行为模式,它给定一个语言和此语言文法表达的解释器。客户端可以使用这个解释器来解释语言中的句子。
解释器模式与编译器的发展历史有关。1972年历史上第一个被广泛使用的计算机语言--c语言诞生了,与其一同诞生的还有第一款c语言
的编译器,java语言的诞生也得益于c语言,因为它的第一款编译器就是由c开发的。
2 解释器通过一种简单的文法表达式解释一门新的语言。 我们熟知的解释型语言:javascript、 phthon、ruby、php
包括网页html、css、以及正则表达式等都是解释器模式的应用案例,就其语言本身来说就可以理解为一个强大的语法"解释器"。
3 解释器模式中涉及的角色
(1)抽象表达式角色(Express): 声明一个所有的具体表达式角色都需要实现的抽象接口,这个接口主要有一个interpret()方法,称为解释操作。
(2)终结表达式(Terminal Expression):这是一个具体角色,它实现了抽象表达式角色所要求的接口,主要是一个interpret()方法。文法中的每一个终结符都有一个具体终结表达式与之对应
(3)非终结表达式(Nonterminal Expression): 这是一个具体角色。文法中的每一条规则R=R1R2...Rn中的符号都持有一个具体的非终结符的表达式类。对于每一个R1R2...Rn中的符号都持有一个静态类型的Expression实例变量。实现解释操作,即interpret()方法。解释操作已递归方式调用所提到的代表R1R2...Rn中的个符号的实例变量。
(4)客户端角色(Client) :代表模式的客户单 ,建造一个抽象语法树 ,调用解释操作。
(5)环境上下文角色(Context): 提供解释器之外的一些全局信息,比如变量的真实量值等。
下面的例子,是运用解释器模式实现java中对布尔表达式的操作运算和求值,它源自GOF原著中的经典例子。
java示例代码:
Expression 解释器接口
package com.jelly.mypattern.interpreter; /** * 解释器 接口 * @author jelly * 解释器的文法表示 * * Expression ::= Expression AND Expression | Expression OR Expression | NOT Expression | Variable | Constant * */ public interface Expression { /** * 解释操作 * @param ctx 上下文环境 * @return */ public boolean interpret(Context ctx); public boolean equals(Object o); public int hashCode(); public String toString(); }
Constant 布尔常量类
package com.jelly.mypattern.interpreter; /** * 布尔 常量 * 例如 * Constant c=new Constant(true) * @author jelly * */ public class Constant implements Expression { private boolean value; public Constant(boolean value){ this.value=value; } /** * 常量 解释运算 * 与上下文环境无关,返回其value属性值即可 */ @Override public boolean interpret(Context ctx) { return this.value; } //重写hashCode方法 @Override public int hashCode() { return (this.toString()).hashCode(); } //重写 equals 方法 @Override public boolean equals(Object o) { if(o!=null&& o instanceof Constant){ return this.value==((Constant)o).value; } return false; } //重写toString 方法 @Override public String toString() { return new Boolean(value).toString(); } }
Variable 布尔变量类
package com.jelly.mypattern.interpreter; /** * 布尔 变量类 * @author jelly * */ public class Variable implements Expression { private String name; public Variable(String name) { this.name = name; } @Override public boolean interpret(Context ctx) { return ctx.lookup(this); } @Override public int hashCode() { return (this.toString()).hashCode(); } @Override public boolean equals(Object o) { if (o != null && o instanceof Variable) { return this.name.equals(((Variable) o).name); } return false; } @Override public String toString() { return name; } }布尔运算上下文类 Context
package com.jelly.mypattern.interpreter; import java.util.HashMap; import java.util.Map; /** *布尔运算 上下文类 * @author jelly * */ public class Context { private Mapmap=new HashMap (); public void assign(Variable var,boolean value){ map.put(var, new Boolean(value)); } public boolean lookup(Variable var)throws IllegalArgumentException { Boolean value= map.get(var) ; if(value==null){ throw new IllegalArgumentException(); } return value.booleanValue(); } }
布尔运算 And
package com.jelly.mypattern.interpreter; /** * 布尔运算 and * @author jelly * */ public class And implements Expression { private Expression left; private Expression right; public And(Expression left,Expression right) { this.left=left; this.right=right; } /** * 解释运算 */ @Override public boolean interpret(Context ctx) { return left.interpret(ctx)&&right.interpret(ctx); } @Override public int hashCode() { return (this.toString()).hashCode(); } @Override public boolean equals(Object o) { if(o!=null&&o instanceof And){ return this.left.equals(((And)o).left) && this.right.equals(((And)o).right); } return false; } @Override public String toString() { return "("+left.toString()+" AND "+right.toString()+")"; } }
布尔运算 Or
package com.jelly.mypattern.interpreter; /** * 布尔运算 or * @author jelly * */ public class Or implements Expression{ private Expression left; private Expression right; public Or(Expression left,Expression right) { this.left=left; this.right=right; } /** * 解释运算 */ @Override public boolean interpret(Context ctx) { return left.interpret(ctx)||right.interpret(ctx); } @Override public int hashCode() { return (this.toString()).hashCode(); } @Override public boolean equals(Object o) { if(o!=null&&o instanceof Or){ return this.left.equals(((Or)o).left) && this.right.equals(((Or)o).right); } return false; } @Override public String toString() { return "("+left.toString()+" OR "+right.toString()+")"; } }
布尔运算 Not
package com.jelly.mypattern.interpreter; /** * 布尔运算 not * @author jelly * */ public class Not implements Expression{ private Expression exp; public Not(Expression exp) { this.exp=exp; } @Override public boolean interpret(Context ctx) { return !exp.interpret(ctx); } @Override public int hashCode() { return (this.toString()).hashCode(); } @Override public boolean equals(Object o) { if(o!=null&&o instanceof Not){ return this.exp.equals(((Not)o).exp); } return false; } @Override public String toString() { return "(NOT "+exp.toString()+")"; } }
测试代码
package com.jelly.mypattern.interpreter; /** * 解释器模式 客户端测试代码 * @author jelly * */ public class InterpreterTest { public static void main(String[] args) { Context ctx=new Context(); //x y 表示2个布尔变量对象 c表示一个布尔常量对象 Variable x=new Variable("x"); Variable y=new Variable("y"); Constant c=new Constant(true); //ctx 指定变量x的值为false 变量y为true,并存放到上下文对象map中 ctx.assign(x, false); ctx.assign(y, true); Expression exp=new Or(new And(c,x),new And(y,new Not(x))); System.out.println("执行布尔表达式运算: "+exp.toString()); System.out.println("运算结果为: "+exp.interpret(ctx)); } }
控制台输出:
执行布尔表达式运算: ((true AND x) OR (y AND (NOT x)))
运算结果为: true
Visitor
访问者模式
1 访问者模式是对象的行为模式。访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构则可 保持不变。适用于对数据结构复杂又相对稳定的对象访问。
2 访问者模式中涉及的角色
抽象访问者角色(Visitor):声明了一个或多个访问操作,形成所有的具体元素角色必须实现的接口
具体访问者(ConcreteVisitor):实现抽象访问者角色所声明的接口。
抽象节点(Node):声明一个接受操作,接受一个访问者对象作为一个参量。
具体节点(ConcreteNode):实现抽象元素节点定义的接受操作。
3 单分派与多分派。
由于java中类、接口之间存在继承和实现的关系,多态由此而产生。java中对象的多态性体现在,在程序的运行期,java会动态判断对象的真实类型,从而准确地 调用对象真实类型的方法。(相对地,我们编译期的对象类型为静态类型)。然而java中的方法接收的参数(称为方法参量,方法接收者与方法参量统称为方法的宗量) 在方法执行期间却不能动态判断出参量的真实类型。也就是说java是"单分派"的语言(至少目前如此), 然而我们可以通过设计模式达到"双分派"的目的。这就是访 问者模式,它通过两次次"单分派"达到"双重分派"的目标,但是从本质上来说"双重分派"仍不等同于"双分派"。
下面是java示例代码:
ObjectStructure 聚集类/模拟一个复杂的数据结构
package com.jelly.mypattern.visitor; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * 数据结构对象,模拟 一个复杂的数据结构聚集对象 * @author jelly * */ public class ObjectStructure { private ListnodeList; public ObjectStructure() { nodeList= new ArrayList (); } public void add(Node node){ nodeList.add(node); } /** * 运行visitor 访问者访问 自身 * 暴露给visitor 的访问方法 * @param visitor */ public void action(Visitor visitor){ Iterator it= nodeList.iterator(); while( it.hasNext()){ Node node= it.next(); node.accept(visitor); } } }
Node 抽象节点
package com.jelly.mypattern.visitor; /** * 抽象节点 * @author jelly * */ public abstract class Node { public abstract void accept(Visitor visitor); }
NodeA 具体节点A
package com.jelly.mypattern.visitor; /** * 具体节点 a * @author jelly * */ public class NodeA extends Node{ //访问nodeA 节点 public String operationA(){ return "nodeA 的operationA方法执行..."; } //访问nodeB 节点 public String operationA2(){ return "nodeA 的operationA2方法执行..."; } @Override public void accept(Visitor visitor) { visitor.visit(this); } }
NodeB 具体节点B
package com.jelly.mypattern.visitor; /** * 具体节点 b * @author jelly * */ public class NodeB extends Node{ public String operationB(){ return "nodeB 的operationB方法执行..."; } public String operationB2(){ return "nodeB 的operationB2方法执行..."; } @Override public void accept(Visitor visitor) { visitor.visit(this); } }
抽象访问者 Visitor
package com.jelly.mypattern.visitor; /** * 抽象访问者角色 * @author jelly * */ public interface Visitor { public void visit(NodeA node); public void visit(NodeB node); }
具体访问者A VisitorA
package com.jelly.mypattern.visitor; /** * 具体访问者A * @author jelly * */ public class VisitorA implements Visitor{ @Override public void visit(NodeA node) { System.out.println(node.operationA()); } @Override public void visit(NodeB node) { System.out.println(node.operationB()); } }
具体访问者B VisitorB
package com.jelly.mypattern.visitor; /** * 具体访问者B * @author jelly * */ public class VisitorB implements Visitor{ //访问nodeA 节点 @Override public void visit(NodeA node) { System.out.println(node.operationA()); System.out.println(node.operationA2()); } //访问nodeB 节点 @Override public void visit(NodeB node) { System.out.println(node.operationB()); System.out.println(node.operationB2()); } }
测试代码:
package com.jelly.mypattern.visitor; /** * 访问者模式 测试代码 * @author jelly * */ public class VisitorTest { public static void main(String[] args) { ObjectStructure objstruct=new ObjectStructure(); Node node1=new NodeA(); Node node2=new NodeB(); Node node3=new NodeA(); Node node4=new NodeB(); objstruct.add(node1); objstruct.add(node2); objstruct.add(node3); objstruct.add(node4); Visitor visitor1=new VisitorA(); objstruct.action(visitor1); System.out.println("---------"); Visitor visitor2=new VisitorB(); objstruct.action(visitor2); } }
控制台输出:
nodeA 的operationA方法执行...
nodeB 的operationB方法执行...
nodeA 的operationA方法执行...
nodeB 的operationB方法执行...
---------
nodeA 的operationA方法执行...
nodeA 的operationA2方法执行...
nodeB 的operationB方法执行...
nodeB 的operationB2方法执行...
nodeA 的operationA方法执行...
nodeA 的operationA2方法执行...
nodeB 的operationB方法执行...
nodeB 的operationB2方法执行...
Mediator
调停者模式 ,也叫中介模式
调停者模式是对象的行为模式,它包装了一系列对象相互作用的方式,使得这些对象不必显示地相互引用。从而可以使它们可以松散地耦合,保证这些相互作用可以彼此独立地变化。
如下图所示,这个示意图中有大量的对象,这些对象既会影响到别的对象,又会被别的对象所影响,因此常常叫做同事对象(Colleague)。
这些同事对象通过彼此的相互作用形成系统的行为。从图中可以看出,几乎每一个对象都需要与其他对象发生相互的作用,而这种相互作用表现为一个对象与另一个对象的直接耦合。这是一个过度耦合的系统。
通过引入调停者(Mediator),可以将系统的网状结构变为以中介为中心的星形结构。
如图所示,在这个星形状结构中,同事对象不再通过直接的联系与另一个对象发生相互作用;相反地,它通过调停者对象与另外一个对象发生作用。
调停者对象的存在保证了对象结构上的稳定,也就是说,系统的结构不会因为新对象的引入造成大量的修改工作。
调停者模式中的角色:
抽象调停者: 定义出同事对象到调停者模式对象的接口,其主要方法是一个(或多个)事件方法。在有些情况下,这个抽象对象可以省略。一般而言,这个角色由一个java抽象类或java对象实现。
具体调停者:从抽象调停者继承而来,实现类的抽象超类的事件方法。具体调停者知晓所有的具体同事类,它从一个具体同事类对象接收消息、向具体同事对象发出命令。
抽象同事角色: 定义出调停者到同事对象的结果。同事对象只知道调停者而不知道其余同事对象。
具体同事角色:具体同事类都很清楚自己在小范围内的行为,而不知道它在大范围内的目的。
java 示例代码
Mediator 抽象调停者
package com.jelly.mypattern.mediator; /** * 调停者 * @author jelly * */ public abstract class Mediator { public abstract void colleagueChanged(Colleague c); }
ConcreteMediator 具体调停者
package com.jelly.mypattern.mediator; /** * 具体调停者 类 * @author jelly * */ public class ConcreteMediator extends Mediator { private ColleagueA colleagueA; private ColleagueB colleagueB; @Override public void colleagueChanged(Colleague c) { System.out.println(c+" 对象发生变化"); colleagueA.action(); colleagueB.action(); } public void createConcreteMediator(){ colleagueA=new ColleagueA(this); colleagueB=new ColleagueB(this); } public ColleagueA getColleagueA() { return colleagueA; } public ColleagueB getColleagueB() { return colleagueB; } }
抽象同事类 Colleague
package com.jelly.mypattern.mediator; /** * 同事 抽象类 * @author jelly * */ public abstract class Colleague { private Mediator mediator; public Colleague( Mediator mediator) { this.mediator=mediator; } public Mediator getMediator() { return mediator; } //抽象行动方法 public abstract void action(); public void change(){ mediator.colleagueChanged(this); } }
同事类A
package com.jelly.mypattern.mediator; /** * 同事类A * @author jelly * */ public class ColleagueA extends Colleague{ public ColleagueA(Mediator mediator) { super(mediator); } /** * colleagueA 中的具体行动方法 */ @Override public void action() { System.out.println("colleagueA do something ..."); } }
同事类B
package com.jelly.mypattern.mediator; /** * 同事类B * @author jelly * */ public class ColleagueB extends Colleague{ public ColleagueB(Mediator mediator) { super(mediator); } /** * colleagueB 中的具体行动方法 */ @Override public void action() { System.out.println("colleagueB do something ..."); } }
测试代码:
package com.jelly.mypattern.mediator; /** * 测试代码 * @author jelly * */ public class MediatorTest { public static void main(String[] args) { ConcreteMediator mediator=new ConcreteMediator(); mediator.createConcreteMediator(); Colleague c=new ColleagueA(mediator); mediator.colleagueChanged(c); } }
com.jelly.mypattern.mediator.ColleagueA@4be8b8 对象发生变化
colleagueA do something ...
colleagueB do something ...
至此,笔者已将GOF中的23种设计模式全部介绍完毕,然而现实中模式并非仅仅只有这些。我们天天说的MVC模式,它是一种模式,又因为它太大,贯穿于整个项目之中,所以又是一种面向对象的架构。时代变迁,模式也在不断地发展,现今各种各样的模式层出不穷,早已超出了GOF原著中的23种,它们中的绝大多数越来越多地被开发者所采纳。
然而模式万万不可被滥用,不要为用模式而使用模式。恰当的模式运用能够成就一个优秀的项目,不当的模式运用也能毁掉一个项目。在我们的开发过程中,必须要结合需求场景,分清场合地使用模式,望读者谨记!
(完)
上一篇:java设计模式(六)
下一篇:js中创建表单post提交
Copyright © 叮叮声的奶酪 版权所有
备案号:鄂ICP备17018671号-1