您的位置:首页 > 其它

设计模式之禅笔记5--访问者模式

2017-01-22 11:14 169 查看

访问者模式

定义:封装一些作用于某种数据结构的各元素的操作。它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

角色:

1. Visitor抽象访问者:声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。

2. ConcreteVisitor:具体访问者。

3. Element抽象元素:声明接收哪一类访问者访问。程序上是通过accept方法中的参数来定义的。

4. ConcreteElement:具体元素。

5. ObjectStructure:结构对象。元素产生着,一般容纳在多个不用类、不同接口的容器。如List,Set,Map等。

通用源码:

public abstract class Element {

public abstract void doSomething();
public abstract void accept(IVisitor visitor);
}
public class ConcreteElement1 extends Element{

public void doSomething(){
//业务处理
}

//允许那个访问者访问
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
public class ConcreteElement2 extends Element{

//完善业务逻辑
public void doSomething(){
//业务管理
}

//允许那个访问者访问
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
public interface IVisitor {

public void visit(ConcreteElement1 el1);

public void visit(ConcreteElement2 el2);
}
public class Visitor implements IVisitor {

public void visit(ConcreteElement1 el1) {
el1.doSomething();
}

public void visit(ConcreteElement2 el2) {
el2.doSomething();
}
}
public class ObjectStruture {

//对象生成器,这里通过一个工厂方法模式模拟
public static Element createElement(){
Random rand = new Random();
if(rand.nextInt(100) > 50){
return new ConcreteElement1();
}else{
return new ConcreteElement2();
}
}
}
public class Client {

public static void main(String[] args) {
for(int i=0;i<10;i++){
Element el = ObjectStruture.createElement();
el.accept(new Visitor());
}
}


优点:

符合单一职责原则。

优秀的扩展性。由于职责的分开,继续增加对数据的操作是非常快捷的。

灵活性非常高。

缺点:

1,具体元素对访问者公布细节。

具体元素变更比较困难。

违背了依赖倒置原则。访问者访问的是具体元素,而不是抽象元素。

使用场景:

一个对象结构包含很多类对象。他们有不同的接口,相对这些对象实施一些依赖于其具体类的操作。

需要对一个对象结构中的对象进行很多不同并且不相关的操作。而你想避免让这些操作污染这些对象的类。

访问者模式扩展:

统计功能。

多个访问者。增加一个接口继承原来的抽象访问者接口。再实现对应的逻辑。

双分派。单分派语言处理一个操作是根据请求者的名称和接收到的参数决定的。在java中又静态绑定和动态绑定之说,他的实现是重载和覆写。

状态模式

定义:当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其类。

状态模式的核心就是封装,状态的变更引起了行为的变更。角色如下:

1. State:抽象状态角色:负责对象状态定义,并且封装环境角色以实现状态切换。

2. ConcreteState:具体状态角色。完成两个职责:本状态下要做的事情以及本状态如何过渡到其他状态。

3. Context环境角色:定义客户端需要的接口,并且负责具体状态的切换。

通用源码:

public abstract class State {

//定义一个环境角色,提供子类访问
protected Context context;

//设置环境角色
public void setContext(Context _context){
this.context = _context;
}

//行为1
public abstract void handle1();

//行为2
public abstract void handle2();
}
public class ConcreteState1 extends State {

@Override
public void handle1() {
//本状态下必须处理的逻辑
}

@Override
public void handle2() {
//设置当前状态为stat2
super.context.setCurrentState(Context.STATE2);
//过渡到state2状态,有context实现
super.context.handle2();
}

}
public class ConcreteState2 extends State {

@Override
public void handle1() {

super.context.setCurrentState(Context.STATE1);
super.context.handle1();
}
@Override
public void handle2() {
//实现业务逻辑
}

}
public class Context {
//定义状态
public final static State STATE1 = new ConcreteState1();
public final static State STATE2 = new ConcreteState2();

//当前状态
private State CurrentState;

//获得当前状态
public State getCurrentState() {
return CurrentState;
}

//设置当前状态
public void setCurrentState(State currentState) {
this.CurrentState = currentState;
//切换状态
this.CurrentState.setContext(this);
}

//行为委托
public void handle1(){
this.CurrentState.handle1();
}

public void handle2(){
this.CurrentState.handle2();
}
}
public class Client {

public static void main(String[] args) {
Context context = new Context();
context.setCurrentState(new ConcreteState1());

context.handle1();
context.handle2();
}
}


优点:

1. 结构清晰,避免了程序的复杂性。提高系统的可靠维护性。

2. 很好的体现了开闭原则和单一职责原则。

3. 封装性好。状态变更放置到类的内部来实现,外部的调用不用知道类内部如何实现状态和行为的变更。

缺点:可能子类太多而导致类膨胀。

使用场景:

1. 行为随状态改变而改变的场景。例如权限设计,人员的状态不同即使执行相同的行为结果也会不同。

2. 条件、分支判断语句的替代者。在程序中大量使用switch语句或者if判断语句会导致程序结构不清晰。逻辑混乱,使用状态模式可以很好的避免这个问题。

解释器模式

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

角色如下:

1. AbstractExpression:抽象解释器。

2. TerminalExpression:终结符表达式。实现与文字中的元素相关联的解释操作。

3. NonterminalExpression:非终结符表达式。文字中的每条规则对应于一个非终结表达式。

4. Context:环境角色。

通用源码:

public abstract class Expression {

//每个表达式必须有一个解析任务
public abstract Object interpreter(Context  ctx);
}
public class TerminalExpression extends Expression {

//通常终结表达式只有一个,但是有多个对象。
public Object interpreter(Context ctx) {
return null;
}

}
public class NonterminalExpression extends Expression {

//每个非终结表达式都会对其他表达式产生依赖
public NonterminalExpression(Expression... expression){

}

public Object interpreter(Context ctx) {
//进行文法处理
return null;
}

}
public class Context {

}
public class Client {

public static void main(String[] args) {
Context ctx = new Context();
//通常定一个语法容器,容纳一个具体的表达式,通常为ListArray,LinkedList,Stack等类型
Stack<Expression> stack = null;
/*
for(;;){
//进行语法判断并产生递归调用
}
*/
//产生一个完整的语法树,由各个具体的语法分析进行解析
Expression exp = stack.pop();

//具体元素进入场景
exp.interpreter(ctx);
}
}


每个非终结符表达式都代表了一个文法规则,并且每个文法规则都只关心自己周边的文法规则的结果。每个终结符表达式调用自己周边的非终结符表达式。

通常Client是一个封装类。封装的结果就是传递进来一个规范语法文件。解析器分析后产生结果并返回。避免了调用者与语法解析器的耦合关系。

优点:良好的扩展性。修改语法规则只要修改相应的非终结符表达式就可以了。若扩展语法则只要增加非终结符类就可以了。

缺点:

1. 解析器模式可能导致类膨胀。

2. 解释器模式采用递归调用方法。

3. 效率问题。解析器模式采用了大量的循环和递归,效率是一个不容忽视的问题。

使用场景:

1. 重复发生的问题可以使用解析器模式。例如,分析处理多个应用服务器每天产生的大量日志。

2. 一个简单语法需要解释的场景。例如SQL语法分析。

最佳实践:解释器模式在实际的系统开发使用的非常少,因为他会引起效率,性能以及维护等问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  设计模式