设计模式中的第三类是行为型模式,共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 Map<Variable,Boolean> map=new HashMap<Variable,Boolean>();
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 List<Node> nodeList;
public ObjectStructure() {
nodeList= new ArrayList<Node>();
}
public void add(Node node){
nodeList.add(node);
}
/**
* 运行visitor 访问者访问 自身
* 暴露给visitor 的访问方法
* @param visitor
*/
public void action(Visitor visitor){
Iterator<Node> 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种,它们中的绝大多数越来越多地被开发者所采纳。
然而模式万万不可被滥用,不要为用模式而使用模式。恰当的模式运用能够成就一个优秀的项目,不当的模式运用也能毁掉一个项目。在我们的开发过程中,必须要结合需求场景,分清场合地使用模式