设计模式(五)行为型模式介绍及实例(下)

文章目录

      • 六、中介者模式
        • 6.1 中介者模式定义
        • 6.2 中介者模式主要角色
        • 6.3 中介者模式特点
        • 6.4 中介者模式实现方式
        • 6.5 中介者模式应用场景
      • 七、迭代器模式
        • 7.1 迭代器模式定义
        • 7.2 迭代器模式主要角色
        • 7.3 迭代器模式特点
        • 7.4 迭代器模式实现方式
        • 7.5 迭代器模式应用场景
      • 八、解释器模式
        • 8.1 解释器模式定义
        • 8.2 解释器模式特点
        • 8.3 解释器模式主要角色
        • 8.4 解释器模式实现方式
        • 8.5 解释器模式应用场景
      • 九、命令模式
        • 9.1 命令模式定义
        • 9.2 命令模式主要角色
        • 9.3 命令模式特点
        • 9.4 命令模式实现方式
        • 9.5 命令模式应用场景
      • 十、责任链模式
        • 10.1 责任链模式定义
        • 10.2 责任链模式主要角色
        • 10.3 责任链模式特点
        • 10.4 责任链模式实现方式
        • 10.5 责任链模式应用场景
      • 十一、访问者模式
        • 11.1 访问者模式定义
        • 11.2 访问者模式主要角色
        • 11.3 访问者模式特点
        • 11.4 访问者模式实现方式
        • 11.5 访问者模式应用场景

  本系列文章共分为六篇:
    设计模式(一)设计模式的分类与区别
    设计模式(二)创建型模式介绍及实例
    设计模式(三)结构型模式介绍及实例
    设计模式(四)行为型模式介绍及实例(上)
    设计模式(五)行为型模式介绍及实例(下)
    设计模式(六)设计模式的常见应用

六、中介者模式

6.1 中介者模式定义

  定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变它们之间的交互。

一句话总结:禁止多个对象之间相互"通信",交给中介者去进行,所有对象和交互者"通信"即可。

6.2 中介者模式主要角色

  中介者模式UML图示如下:
设计模式(五)行为型模式介绍及实例(下)_第1张图片
  Colleague叫抽象同事类,而ConcreteColleague是具体同事类,每个具体同事类只知道自己的行为,而不了解其他同事类的情况,他们都认识中介者对象;Mediator抽象中介者,定义了同事对象到中介者对象的接口,ConcreteMediator是具体中介者对象,实现抽象类的方法,它需要知道所有具体同事类,并从具体同事接收消息,向具体同事对象发出命令。

6.3 中介者模式特点

优点

  1. 降低了对象之间的耦合性,使得对象易于独立地被复用。
  2. 将对象间的一对多关联转变为一对一的关联,提高系统的灵活性,使得系统易于维护和扩展。

缺点

  1. 当同事类太多时,中介者的职责将很大,它会变得复杂而庞大,以至于系统难以维护。
6.4 中介者模式实现方式

  此处以在班级里收作业为例,一个班级里有不同的课代表,他们会发起收作业的动作,同时,他们又要交作业,这恰好符合中介者模式。示例代码如下:

/*抽象中介者*/
public abstract  class Mediator {
    public abstract void register(Student stu); //将stu加入通知列表
    public abstract void relay(Student stu); //转发消息
}
/*具体中介者*/
public class RealMediator extends Mediator{
    private List<Student> stus=new ArrayList<Student>();
    public void register(Student stu){
        if(!stus.contains(stu)){
        	stus.add(stu);
        	stu.setMedium(this);
        }
    }
    public void relay(Student stu1){
        for(Student stu:stus){
            if(!stu.equals(stu1)){
                ((Student)stu).receive();
            }   
        }
    }
}
/*抽象同事类*/
public abstract  class Student {
    protected Mediator mediator;
    public void setMedium(Mediator mediator){
        this.mediator=mediator;
    }   
    public abstract void receive();   
    public abstract void send();
}
/*具体同事类:物理课代表*/
public class PhysicsClassrepresentative extends Student{
	@Override
	public void receive() {
	    System.out.println("物理课代表说:收到,马上交化学作业");		
	}
	@Override
	public void send() {
        System.out.println("物理课代表说:收物理作业了");
        mediator.relay(this); //请中介者转发
	}
}
/*具体同事类:化学课代表*/
public class ChemistryClassrepresentative extends Student{
	@Override
	public void receive() {
	    System.out.println("化学课代表说:收到,马上交物理作业");		
	}
	@Override
	public void send() {
        System.out.println("化学课代表说:收化学作业了");
        mediator.relay(this); //请中介者转发
	}
}
/*测试类*/
public class MediatorTest {
    public static void main(String[] args) {
        Mediator md=new RealMediator();
        Student stu1,stu2;
        stu1=new PhysicsClassrepresentative();
        stu2=new ChemistryClassrepresentative();
        md.register(stu1);
        md.register(stu2);
        stu1.send();
        System.out.println("-------------");
        stu2.send();
    }
}

  结果如下:

物理课代表说:收物理作业了
化学课代表说:收到,马上交物理作业
化学课代表说:收化学作业了
物理课代表说:收到,马上交化学作业

6.5 中介者模式应用场景
  1. 对象之间存在复杂的网状结构关系而导致依赖关系混乱且难以复用时。
  2. 当想创建一个运行于多个类之间的对象,又不想生成新的子类时。

七、迭代器模式

7.1 迭代器模式定义

  提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示

一句话总结:如何遍历对象。

7.2 迭代器模式主要角色

  迭代器模式UML图示如下:
设计模式(五)行为型模式介绍及实例(下)_第2张图片

  1. 抽象聚合角色
     定义存储、添加、删除聚合对象以及创建迭代器对象的接口。
  2. 具体聚合角色
     实现抽象聚合类,返回一个具体迭代器的实例。
  3. 抽象迭代器角色
     定义访问和遍历聚合元素的接口,通常包含hasNext()、first()、next()等方法。
  4. 具体迭代器角色
     实现抽象迭代器接口中所定义的方法,完成对聚合对象的遍历,记录遍历的当前位置。
7.3 迭代器模式特点

优点

  1. 访问一个聚合对象的内容而无须暴露它的内部表示
  2. 遍历任务交由迭代器完成,这简化了聚合类。
  3. 它支持以不同方式遍历一个集合,甚至可以自定义迭代器的子类以支持新的遍历。
  4. 增加新的聚合类和迭代器类都很方便,无须修改原有代码。
  5. 封装性良好,为遍历不同的聚合结构提供一个统一的接口。

缺点

  1. 增加了类的个数,这在一定程度上增加了系统的复杂性。
7.4 迭代器模式实现方式

  此处以中国知名的手机品牌为例,这些品牌有华为、小米、OPPO、VIVO、锤子等。示例代码如下:

/*抽象聚合*/
public interface Aggregate {
    public void add(Phone phone); 
    public void remove(Phone phone); 
    public Iterator getIterator(); 
}
/*具体聚合*/
public class ConcreteAggregate implements Aggregate{
    private List<Phone> phoneList=new ArrayList<Phone>(); 
    public void add(Phone phone){ 
    	phoneList.add(phone); 
    }
    public void remove(Phone phone){ 
    	phoneList.remove(phone); 
    }
    public Iterator getIterator(){    return(new ConcreteIterator(phoneList));   }   
}
/*抽象迭代器*/
public interface  Iterator {
    Phone first();
    Phone next();
    boolean hasNext();
}
/*具体迭代器*/
public class ConcreteIterator  implements Iterator{ 
	 private List<Phone> list=null; 
	    private int index=-1; 
	    public ConcreteIterator(List<Phone> list){ 
	        this.list=list; 
	    } 
	    public boolean hasNext(){ 
	        if(index<list.size()-1)
	             return true;
	        else    
	             return false;  
	    }
	    public Phone first(){
	        index=0;
	        Phone phone=list.get(index);;
	        return phone;
	    }
	    public Phone next(){ 
	    	Phone phone=null; 
	        if(this.hasNext()){ 
	        	phone=list.get(++index); 
	        } 
	        return phone; 
	    }   
}
/*实体类:手机*/
public class Phone {
	private String name;
	public Phone(String name){
		this.name = name;
	}
	public String getName(){	return this.name;    }
}
/*测试类*/
public class IteratorTest {
    public static void main(String[] args) {
        Aggregate ag=new ConcreteAggregate(); 
        ag.add(new Phone("华为")); 
        ag.add(new Phone("小米")); 
        ag.add(new Phone("OPPO"));
        ag.add(new Phone("VIVO"));
        ag.add(new Phone("锤子"));
        System.out.print("聚合的手机品牌有:\n");
        Iterator it=ag.getIterator(); 
        while(it.hasNext()){ 
            Phone phone=it.next(); 
            System.out.print(phone.getName()+"\n"); 
        }
        Phone firstPhone=it.first();
        System.out.println("\n第一是:"+firstPhone.getName());
    }
}

  测试结果如下:

聚合的手机品牌有:
华为
小米
OPPO
VIVO
锤子
第一是:华为

7.5 迭代器模式应用场景
  1. 当需要为聚合对象提供多种遍历方式时。
  2. 当需要为遍历不同的聚合结构提供一个统一的接口时。
  3. 访问一个聚合对象的内容而无须暴露其内部细节的表示时。

八、解释器模式

8.1 解释器模式定义

  给分析对象定义一个语言,并定义该语言的文法表示,再设计一个解析器来解释语言中的句子。

一句话总结:解释带有特定语法的句子。

8.2 解释器模式特点

优点

  1. 扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
  2. 容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。

缺点

  1. 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
  2. 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
  3. 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。
8.3 解释器模式主要角色

  解释器模式UML图示:
设计模式(五)行为型模式介绍及实例(下)_第3张图片
  AbstractException(抽象表达式),声明一个抽象的解释操作,这个接口为抽象语法树中所有节点所共享。
  TerminalException(终结符表达式),实现与文法中的终结符相关联的解释操作。实现抽象表达式中所要求的接口。
  NotTerminalException(非终结符表达式),为文法中的非终结符实现解释操作。
  Context,包含解释器之外的一些全局信息。

8.4 解释器模式实现方式

  此处以员工是否需要参加新人培训为例,在无锡和北京两地工作的3年以下的员工需要参加,其他的则不需要。代码示例如下:

/*抽象表达式*/
public interface  Expression {
	public boolean interpret(String info);
}
/*终结表达式*/
public class TerminalExpression implements Expression{
    private Set<String> set= new HashSet<String>();
    public TerminalExpression(String[] data){
        for(int i=0;i<data.length;i++)set.add(data[i]);
    }
    public boolean interpret(String info){
        if(set.contains(info)){
            return true;
        }
        return false;
    }
}
/*非终结符表达式*/
public class AndExpression implements Expression{
    private Expression city=null;    
    private Expression person=null;
    public AndExpression(Expression city,Expression person){
        this.city=city;
        this.person=person;
    }
    public boolean interpret(String info){
        String s[]=info.split(",");       
        return city.interpret(s[0])&&person.interpret(s[1]);
    }
}
/*环境*/
public class Context {
    private String[] citys={"无锡","北京"};
    private String[] persons={"1年","2年","3年"};
    private Expression cityPerson;
    public Context(){
        Expression city=new TerminalExpression(citys);
        Expression person=new TerminalExpression(persons);
        cityPerson=new AndExpression(city,person);
    }
    public void train(String info){
        boolean ok=cityPerson.interpret(info);
        if(ok) System.out.println("您入职年份较少,需要参加新人培训");
        else System.out.println("您入职年份较多或工作地不在无锡、北京,不用再参加新人培训");   
    }
}
/*测试类*/
public class InterpreterTest {
	public static void main(String[] args) { 
        Context personnel=new Context();
        personnel.train("无锡,2年");
        personnel.train("北京,5年");
        personnel.train("无锡,3年");
        personnel.train("广州,2年");
        personnel.train("北京,2年");
	}
}

  测试结果如下:

您入职年份较少,需要参加新人培训
您入职年份较多或工作地不在无锡、北京,不用再参加新人培训
您入职年份较少,需要参加新人培训
您入职年份较多或工作地不在无锡、北京,不用再参加新人培训
您入职年份较少,需要参加新人培训

8.5 解释器模式应用场景
  1. 语言的文法较为简单,且执行效率不是关键问题时。
  2. 当问题重复出现,且可以用一种简单的语言来进行表达时。
  3. 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候,如 XML 文档解释。

  注意:尽量不要在重要的模块中使用解释器模式,否则维护会是一个很大的问题。在项目中可以使用shell、JRuby、Groovy等脚本语言来代替解释器模式,弥补Java编译型语言的不足。

九、命令模式

9.1 命令模式定义

  将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。

一句话总结:将命令/请求封装为对象,在对象与对象之间传递信息,降低对象之间的耦合度。

  Command模式将操作的执行逻辑封装到一个个Command对象中,解耦了操作发起者和操作执行逻辑之间的耦合关系:操作发起者要进行一个操作,不用关心具体的执行逻辑,只需创建一个相应的Command实例,调用它的执行接口即可。

9.2 命令模式主要角色

  命令模式UML图示如下:
设计模式(五)行为型模式介绍及实例(下)_第4张图片
  Command类,用来声明执行操作的接口。
  ConcreteCommand类,将一个接收者对象绑定于一个动作,调用接收者相应的操作,以实现Execute。
  Invoker类,要求该命令执行这个请求。
  Receiver类,知道如何实施与执行一个与请求相关的操作,任何类都可能成为一个接收者。

9.3 命令模式特点

优点

  1. 降低系统的耦合度
     命令模式能将调用操作的对象与实现该操作的对象解耦。
  2. 增加或删除命令非常方便
     采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
  3. 可以实现宏命令
     命令模式可以与组合模式结合,将多个命令装配成一个组合命令,即宏命令。
  4. 方便实现Undo(撤销)和Redo(恢复)操作
     命令模式可以与后面介绍的备忘录模式结合,实现命令的撤销与恢复。

缺点

  1. 可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。
9.4 命令模式实现方式

  此处以项目经理追问各研发小组进度为例,项目经理为调用者,查询项目进度为命令,各研发小组Leader为接收者。示例代码如下:

/*抽象命令*/
public abstract class Command {
	public abstract void execute();
}
/*具体命令:查询项目进度*/
public class QueryProjectCommand extends Command{
    private GroupLeaderA groupLeaderA;
    private GroupLeaderB groupLeaderB;
    QueryProjectCommand(){
    	groupLeaderA = new GroupLeaderA();
    	groupLeaderB = new GroupLeaderB();
    }
    public void execute(){
    	groupLeaderA.action();
    	groupLeaderB.action();
    }
}
/*接收者A:A研发组Leader*/
public class GroupLeaderA {
    public void action(){
        System.out.println("A项目组进度为:已完成70%功能开发");
    }
}
/*接收者B:B研发组Leader*/
public class GroupLeaderB {
    public void action(){
        System.out.println("B项目组进度为:已完成50%功能开发");
    }
}
/*调用者:项目经理*/
public class ProjectManager {
    private Command command;
    public ProjectManager(Command command){
        this.command=command;
    }
    public void setCommand(Command command){
        this.command=command;
    }
    public void call(){
        System.out.println("项目经理查询各小组研发进度");
        command.execute();
    }
}
/*测试类*/
public class CommandTest {
    public static void main(String[] args){
        Command cmd = new QueryProjectCommand();
        ProjectManager projectManager = new ProjectManager(cmd);
        System.out.println("客户访问调用者(项目经理)的call()方法...");
        projectManager.call();
    }
}

  输出结果如下:

客户访问调用者(项目经理)的call()方法…
项目经理查询各小组研发进度
A项目组进度为:已完成70%功能开发
B项目组进度为:已完成50%功能开发

9.5 命令模式应用场景
  1. 当系统需要将请求调用者与请求接收者解耦时,命令模式使得调用者和接收者不直接交互。
  2. 当系统需要随机请求命令或经常增加或删除命令时,命令模式比较方便实现这些功能。
  3. 当系统需要执行一组操作时,命令模式可以定义宏命令来实现该功能。
  4. 当系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作时,可以将命令对象存储起来,采用备忘录模式来实现。

十、责任链模式

10.1 责任链模式定义

  为了避免请求发送者与多个请求处理者耦合在一起,将所有请求的处理者通过前一对象记住其下一个对象的引用而连成一条链;当有请求发生时,可将请求沿着这条链传递,直到有对象处理它为止。

一句话总结:多个请求处理者记录对下一个处理者的引用(形成一条链),降低请求发起者和多个处理者之间的耦合。

10.2 责任链模式主要角色

   责任链模式UML图示如下:
设计模式(五)行为型模式介绍及实例(下)_第5张图片
  Handler类,定一个处理请示的接口。
  ConcreteHandler类,具体处理者,处理它所负责的请求,可访问它的后继者,如果可处理该请求,就处理之,否则就将该请求转发给它的后继者。

  注意事项:链中节点数量需要控制,避免出现超长链的情况,一般的做法是在Handler中设置一个最大节点数量,在setNext方法中判断是否已经是超过其阈值,超过则不允许该链建立,避免无意识地破坏系统性能。

10.3 责任链模式特点

优点

  1. 降低了对象之间的耦合度
     该模式使得一个对象无须知道到底是哪一个对象处理其请求以及链的结构,发送者和接收者也无须拥有对方的明确信息。
  2. 增强了系统的可扩展性
     可以根据需要增加新的请求处理类,满足开闭原则。
  3. 增强了给对象指派职责的灵活性
     当工作流程发生变化,可以动态地改变链内的成员或者调动它们的次序,也可动态地新增或者删除责任。
  4. 责任链简化了对象之间的连接
     每个对象只需保持一个指向其后继者的引用,不需保持其他所有处理者的引用,这避免了使用众多的if或者if···else语句。
  5. 责任分担
     每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合类的单一职责原则。

缺点

  1. 不能保证每个请求一定被处理
     由于一个请求没有明确的接收者,所以不能保证它一定会被处理,该请求可能一直传到链的末端都得不到处理。
  2. 对比较长的职责链,请求的处理可能涉及多个处理对象,系统性能将受到一定影响
  3. 职责链建立的合理性要靠客户端来保证,增加了客户端的复杂性,可能会由于职责链的错误设置而导致系统出错,如可能会造成循环调用。
10.4 责任链模式实现方式

  此处以代购为例,不同的代购者可以购买不同价位的产品,代购者A可以购买0-300元的产品,代购者B可以购买300-600元的产品,代购者C可以购买600元以上的产品。示例代码如下:

/*抽象处理者:代购者*/
public abstract class AgentBuyer {
    private AgentBuyer next;
    public void setNext(AgentBuyer next){
        this.next=next; 
    }
    public AgentBuyer getNext(){     return next;  }   
    //处理请求的方法,此处实现的方法没有返回值。其实在实现处理请求的方法上,可以设定一个方法返回值,用来决定是否还将责任链上的处理继续进行下去。
    public abstract void buyGoods(int money);    
}
/*具体处理者:代购者A*/
public class AgentBuyerA extends AgentBuyer{
	@Override
	public void buyGoods(int money) {
        if(money>0 && money <= 300) {
            System.out.println("您可以通过代购者A购买"+money+"元的商品");       
        }
        else{
            if(getNext() != null) {
                getNext().buyGoods(money);             
            }
        } 
	}
}
/*具体处理者:代购者B*/
public class AgentBuyerB extends AgentBuyer{
	@Override
	public void buyGoods(int money) {
        if(money>300 && money <= 600) {
            System.out.println("您可以通过代购者B购买"+money+"元的商品");       
        }else{
            if(getNext() != null) {
                getNext().buyGoods(money);             
            }
        } 
	}
}
/*具体处理者:代购者C*/
public class AgentBuyerC extends AgentBuyer{
	@Override
	public void buyGoods(int money) {
       System.out.println("您可以通过代购者C购买"+money+"元的商品");       
	}
}
/*测试类*/
public class ResponsibilityTest {
    public static void main(String[] args){
        //组装责任链 
    	AgentBuyer gentBuyerA =new AgentBuyerA();
    	AgentBuyer gentBuyerB =new AgentBuyerB();
    	AgentBuyer gentBuyerC =new AgentBuyerC();
    	gentBuyerA.setNext(gentBuyerB);
    	gentBuyerB.setNext(gentBuyerC);
    	gentBuyerA.buyGoods(500);
    	gentBuyerA.buyGoods(1000);
    }
}

  输出结果如下:

您可以通过代购者B购买500元的商品
您可以通过代购者C购买1000元的商品

10.5 责任链模式应用场景
  1. 多个对象可以处理一个请求,哪个对象处理该请求由运行时刻自动确定。
  2. 动态指定一组对象处理请求,或添加新的处理者。
  3. 不明确指定请求处理者的情况下,向多个处理者中的一个提交请求。

  责任链的一个典型应用是拦截器/过滤器模式

十一、访问者模式

11.1 访问者模式定义

  将作用于某种数据结构中的各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下可以添加作用于这些元素的新的操作,为数据结构中的每个元素提供多种访问方式
  它将对数据的操作与数据结构进行分离,是行为类模式中最复杂的一种模式。

一句话总结:定义一个对象,可以在不改变复杂数据对象结构的前提下,访问数据结构中的各元素。

11.2 访问者模式主要角色

  访问者模式UML图示如下:
设计模式(五)行为型模式介绍及实例(下)_第6张图片
  Visitor类,为对象结构中ConcreteElement的每一个类声明一个Visit操作。
  ConcreteVisitor1和ConcreteVisitor2类,具体访问者,实现每个由Visitor声明的操作。
  Element类,定义一个Accept操作,它以一个访问者为参数。
  ConcreteElementA和ConcreteElementB类,具体元素,实现accept操作。

11.3 访问者模式特点

优点

  1. 扩展性好
     能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能
  2. 复用性好
     可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
  3. 灵活性好
     访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
  4. 符合单一职责原则
     访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

缺点

  1. 增加新的元素类很困难
     在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作(也就是说访问者模式适合元素结构比较固定的场景),这违背了“开闭原则”。
  2. 破坏封装
     访问者模式中具体元素对访问者公布细节,这破坏了对象的封装性。
  3. 违反了依赖倒置原则
     访问者模式依赖了具体类,而没有依赖抽象类。
11.4 访问者模式实现方式

  此处以老师查询学生成绩为例,一个学生有在不同科目上有不同的成绩,老师除了询问本科目的成绩之外,有时也会询问一下其他科目的成绩,来看下学生是否在其他科目投入精力过多,导致自己教授的科目成绩下降。示例代码如下:

/*抽象访问者:老师*/
public interface  Teacher {
    void query(PhysicsAchievement physicsAchievement);
    void query(ChemistryAchievement chemistryAchievement);
}
/*具体访问者1:物理老师*/
public class PhysicsTeacher implements Teacher{
    public void query(PhysicsAchievement element){
        System.out.println("物理老师查询物理课成绩,"+element.operationA());
    }
    public void query(ChemistryAchievement element){
        System.out.println("物理老师查询化学课成绩,"+element.operationB());
    }
}
/*具体访问者2:化学老师*/
public class ChemistryTeacher implements Teacher{
    public void query(PhysicsAchievement element){
        System.out.println("化学老师查询物理课成绩,"+element.operationA());
    }
    public void query(ChemistryAchievement element){
        System.out.println("化学老师查询化学课成绩,"+element.operationB());
    }
}
/*抽象元素:成绩*/
public interface  Achievement {
	void accept(Teacher teacher);
}
/*具体元素1:物理成绩*/
public class PhysicsAchievement implements Achievement{
    public void accept(Teacher teacher){
    	teacher.query(this);
    }
    public String operationA(){   return "物理成绩85分";   }
}
/*具体成绩2:化学成绩*/
public class ChemistryAchievement implements Achievement{
    public void accept(Teacher teacher){
    	teacher.query(this);
    }
    public String operationB(){   return "化学成绩90分";   }
}
/*对象结构*/
public class ObjectStructure {
    private List<Achievement> list=new ArrayList<Achievement>();   
    public void accept(Teacher visitor){
        Iterator<Achievement> i=list.iterator();
        while(i.hasNext()){
            ((Achievement) i.next()).accept(visitor);
        }      
    }
    public void add(Achievement element){
        list.add(element);
    }
    public void remove(Achievement element){
        list.remove(element);
    }
}
/*测试类*/
public class VisitorTest {
    public static void main(String[] args){
        ObjectStructure os=new ObjectStructure();
        os.add(new PhysicsAchievement());
        os.add(new ChemistryAchievement());
        Teacher visitor=new PhysicsTeacher();
        os.accept(visitor);
        visitor=new ChemistryTeacher();
        os.accept(visitor);
    }
}

  结果如下:

物理老师查询物理课成绩,物理成绩85分
物理老师查询化学课成绩,化学成绩90分
化学老师查询物理课成绩,物理成绩85分
化学老师查询化学课成绩,化学成绩90分

11.5 访问者模式应用场景
  1. 对象结构相对稳定,但其操作算法经常变化的程序。
  2. 对象结构中的对象需要提供多种不同且不相关的操作,而且要避免让这些操作的变化影响对象的结构。
  3. 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。
  4. 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作,也就说是用迭代器模式已经不能胜任的情景。
  5. 需要对一个对象结构中的对象进行很多不同并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。

你可能感兴趣的:(【设计模式】,java,设计模式,解释器模式)