设计模式之访问者模式

一、什么是访问者模式

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

访问者模式,属于对象行为型模式。

二、访问者模式UML类图

设计模式之访问者模式插图

三、访问者模式角色

  • 抽象访问者角色(Vistor): 接口或抽象类,它定义了对每一个元素访问的行为,它的参数就是可以访问的元素,它的方法数理论上来说与元素个数一致。
  • 具体访问者角色(ConcreteVistor): 具体的访问者实现类,它需要给出对每一个元素类访问时所产生的具体实现。
  • 抽象元素角色(Element): 接口或抽象类,它定义了一个接受访问者参数的方法,表明每一个元素都可以被访问者访问。
  • 具体元素角色(ConcreteElement): 具体的元素类,提供了对抽象元素类的实现,通常情况下是使用访问者提供的访问该元素类的方法。
  • 对象结构角色(ObjectStructure):是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

四、访问者模式优缺点

优点:

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

缺点:

  1. 违背了开闭原则。每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作。
  2. 破坏封装。具体元素对访问者公布细节,这破坏了对象的封装性。
  3. 违反了依赖倒置原则。依赖了具体类,而没有依赖抽象类。

五、访问者模式demo示例

1、Demo需求

通过访问者模式:实现参赛者参加运动会,并可以查看比赛成绩。比赛项目有跳高、跳远、铅球和标枪,参赛者有孙悟空、沙悟净、猪八戒和唐僧。

2、抽象类定义

/**
  * @Description: 抽象访问者角色:参赛者类
  **/ public abstract class Contestant {

     protected String name;
 
     public Contestant(String name) {
         this.name = name;
     }
 
     /**
      * 参加跳远比赛
      *
      * @param longJump
      */     public abstract void joinLongJump(LongJump longJump);
 
     /**
      * 參加投掷标枪比赛
      *
      * @param javelin
      */     public abstract void joinJavelin(Javelin javelin);
 
     /**
      * 参加跳高比赛
      *
      * @param highJump
      */     public abstract void joinHighJump(HighJump highJump);
 
     /**
      * 参加投掷铅球比赛
      *
      * @param shot
      */     public abstract void joinShot(Shot shot);
 }
/**
  * @Description: 抽象元素角色:体育运动类
  **/ public abstract class Sports {

     // 比赛项目名称
     protected String name;
 
     public Sports(String name) {
         this.name = name;
     }
 
     /**
      * 获取比赛成绩
      *
      * @param contestant
      * @return
      */     public abstract void showScore(Contestant contestant);
 }

3、实现类定义

/**
  * @Description: 具体访问者角色: 沙悟净类
  **/ public class ShaWuJing extends Contestant {

     public ShaWuJing(String name) {
         super(name);
     }
 
     /**
      * 沙悟净参加跳远比赛成绩
      *
      * @param longJump
      */     @Override
     public void joinLongJump(LongJump longJump) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, longJump.name));
     }
 
     /**
      * 沙悟净参加投掷标枪成绩
      *
      * @param javelin
      */     @Override
     public void joinJavelin(Javelin javelin) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, javelin.name));
     }
 
     /**
      * 沙悟净参加跳高比赛成绩
      *
      * @param highJump
      */     @Override
     public void joinHighJump(HighJump highJump) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, highJump.name));
     }
 
     /**
      * 沙悟净参加投掷铅球成绩
      *
      * @param shot
      */     @Override
     public void joinShot(Shot shot) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, shot.name));
     }
 }
/**
  * @Description: 具体访问者角色: 孙悟空类
  **/ public class SunWuKong extends Contestant {

     public SunWuKong(String name) {
         super(name);
     }
 
     /**
      * 孙悟空参加跳远比赛成绩
      *
      * @param longJump
      */     @Override
     public void joinLongJump(LongJump longJump) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, longJump.name));
     }
 
     /**
      * 孙悟空参加投掷标枪成绩
      *
      * @param javelin
      */     @Override
     public void joinJavelin(Javelin javelin) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, javelin.name));
     }
 
     /**
      * 孙悟空参加跳高比赛成绩
      *
      * @param highJump
      */     @Override
     public void joinHighJump(HighJump highJump) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, highJump.name));
     }
 
     /**
      * 孙悟空参加投掷铅球成绩
      *
      * @param shot
      */     @Override
     public void joinShot(Shot shot) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, shot.name));
     }
 }
/**
  * @Description: 具体访问者角色: 猪八戒类
  **/ public class ZhuBaJie extends Contestant {

     public ZhuBaJie(String name) {
         super(name);
     }
 
     /**
      * 猪八戒参加跳远比赛成绩
      *
      * @param longJump
      */     @Override
     public void joinLongJump(LongJump longJump) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, longJump.name));
     }
 
     /**
      * 猪八戒参加投掷标枪成绩
      *
      * @param javelin
      */     @Override
     public void joinJavelin(Javelin javelin) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, javelin.name));
     }
 
     /**
      * 猪八戒参加跳高比赛成绩
      *
      * @param highJump
      */     @Override
     public void joinHighJump(HighJump highJump) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, highJump.name));
     }
 
     /**
      * 猪八戒参加投掷铅球成绩
      *
      * @param shot
      */     @Override
     public void joinShot(Shot shot) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, shot.name));
     }
 }
/**
  * @Description: 具体访问者角色: 唐僧类
  **/ public class TangSeng extends Contestant {

     public TangSeng(String name) {
         super(name);
     }
 
     /**
      * 唐僧参加跳远比赛成绩
      *
      * @param longJump
      */     @Override
     public void joinLongJump(LongJump longJump) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, longJump.name));
     }
 
     /**
      * 唐僧参加投掷标枪成绩
      *
      * @param javelin
      */     @Override
     public void joinJavelin(Javelin javelin) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, javelin.name));
     }
 
     /**
      * 唐僧参加跳高比赛成绩
      *
      * @param highJump
      */     @Override
     public void joinHighJump(HighJump highJump) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, highJump.name));
     }
 
     /**
      * 唐僧参加投掷铅球成绩
      *
      * @param shot
      */     @Override
     public void joinShot(Shot shot) {
         System.out.println(String.format("%s, 参加了 %s 比赛",name, shot.name));
     }
 }
/**
  * @Description: 具体元素角色: 跳高类
  **/ public class HighJump extends Sports {

     public HighJump(String name) {
         super(name);
     }
 
     @Override
     public void showScore(Contestant contestant) {
         contestant.joinHighJump(this);
         double v = new Random().nextInt(8);
         System.out.println(contestant.name+", "+name+" 比赛成绩= "+v);
         System.out.println("----------------------------------------------");
     }
 }
 /**
  * @Description: 具体元素角色:标枪类
  **/ public class Javelin extends Sports {

     public Javelin(String name) {
         super(name);
     }
 
     @Override
     public void showScore(Contestant contestant) {
         contestant.joinJavelin(this);
         double v = new Random().nextInt(20);
         System.out.println(contestant.name+", "+name+" 比赛成绩= "+v);
         System.out.println("----------------------------------------------");
     }
 }
/**
  * @Description: 具体元素角色:跳远类
  **/ public class LongJump extends Sports {

     public LongJump(String name) {
         super(name);
     }
 
     @Override
     public void showScore(Contestant contestant) {
         contestant.joinLongJump(this);
         double v = new Random().nextInt(10);
         System.out.println(contestant.name+", "+name+" 比赛成绩= "+v);
         System.out.println("----------------------------------------------");
     }
 }
/**
  * @Description: 具体元素角色: 铅球类
  **/ public class Shot extends Sports {

     public Shot(String name) {
         super(name);
     }
 
     @Override
     public void showScore(Contestant contestant) {
         contestant.joinShot(this);
         double v = new Random().nextInt(15);
         System.out.println(contestant.name+", "+name+" 比赛成绩= "+v);
         System.out.println("----------------------------------------------");
     }
 }
/**
  * @Description: 对象结构角色: 操场
  **/ public class SportsGround {

     // 比赛项目集合列表
     private List<Sports> sportsList = new ArrayList<>();
 
     /**
      * 参赛者报名加入比赛
      *
      * @param sports
      */     public void add(Sports sports){
         sportsList.add(sports);
     }
 
     /**
      * 参赛者退出比赛
      *
      * @param sports
      */     public void remove(Sports sports){
         sportsList.remove(sports);
     }
 
     /**
      * 查看参赛者成绩
      *
      * @param contestant
      */     public void showScore(Contestant contestant){
         sportsList.forEach(s->{
             s.showScore(contestant);
         });
     }
 }

4、测试

/**
  * @Description: 访问者模式客户端测试类
  **/ public class Client {

     public static void main(String[] args) {
         // 构建对象结构实例
         SportsGround sportsGround = new SportsGround();
         // 加入比赛项目
         sportsGround.add(new LongJump("跳远"));
         sportsGround.add(new HighJump("跳高"));
         sportsGround.add(new Javelin("标枪"));
         sportsGround.add(new Shot("铅球"));
 
         // 孙悟空参加比赛,看看比赛成绩
         sportsGround.showScore(new SunWuKong("孙悟空"));
         System.out.println("==================================");
         // 猪八戒参加比赛,看看比赛成绩
         sportsGround.showScore(new SunWuKong("猪八戒"));
         System.out.println("==================================");
         // 沙悟净参加比赛,看看比赛成绩
         sportsGround.showScore(new SunWuKong("沙悟净"));
         System.out.println("==================================");
         // 唐僧参加比赛,看看比赛成绩
         sportsGround.showScore(new SunWuKong("唐僧"));
     }
 }

控制台日志:

孙悟空, 参加了跳远比赛
孙悟空, 跳远比赛成绩= 1.0
----------------------------------------------
孙悟空, 参加了跳高比赛
孙悟空, 跳高比赛成绩= 0.0
----------------------------------------------
孙悟空, 参加了标枪比赛
孙悟空, 标枪比赛成绩= 4.0
----------------------------------------------
孙悟空, 参加了铅球比赛
孙悟空, 铅球比赛成绩= 5.0
----------------------------------------------
==================================
猪八戒, 参加了跳远比赛
猪八戒, 跳远比赛成绩= 1.0
----------------------------------------------
猪八戒, 参加了跳高比赛
猪八戒, 跳高比赛成绩= 1.0
----------------------------------------------
猪八戒, 参加了标枪比赛
猪八戒, 标枪比赛成绩= 5.0
----------------------------------------------
猪八戒, 参加了铅球比赛
猪八戒, 铅球比赛成绩= 5.0
----------------------------------------------
==================================
沙悟净, 参加了跳远比赛
沙悟净, 跳远比赛成绩= 8.0
----------------------------------------------
沙悟净, 参加了跳高比赛
沙悟净, 跳高比赛成绩= 7.0
----------------------------------------------
沙悟净, 参加了标枪比赛
沙悟净, 标枪比赛成绩= 12.0
----------------------------------------------
沙悟净, 参加了铅球比赛
沙悟净, 铅球比赛成绩= 3.0
----------------------------------------------
==================================
唐僧, 参加了跳远比赛
唐僧, 跳远比赛成绩= 6.0
----------------------------------------------
唐僧, 参加了跳高比赛
唐僧, 跳高比赛成绩= 4.0
----------------------------------------------
唐僧, 参加了标枪比赛
唐僧, 标枪比赛成绩= 16.0
----------------------------------------------
唐僧, 参加了铅球比赛
唐僧, 铅球比赛成绩= 6.0
----------------------------------------------

发表评论