设计模式之访问者模式

一、什么是访问者模式

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

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

二、访问者模式UML类图

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

三、访问者模式角色

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

四、访问者模式优缺点

优点:

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

缺点:

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

五、访问者模式demo示例

1、Demo需求

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

2、抽象类定义

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
  * @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);
 }
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
/**
  * @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、实现类定义

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
  * @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));
     }
 }
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
  * @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));
     }
 }
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
  * @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));
     }
 }
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
  * @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));
     }
 }
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
  * @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("----------------------------------------------");
     }
 }
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
 * @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("----------------------------------------------");
    }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
  * @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("----------------------------------------------");
     }
 }
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
/**
  * @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("----------------------------------------------");
     }
 }
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
  * @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、测试

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
  * @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("唐僧"));
     }
 }

控制台日志:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
孙悟空, 参加了跳远比赛
孙悟空, 跳远比赛成绩= 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
----------------------------------------------

发表评论

欢迎阅读『设计模式之访问者模式|Java、设计模式|Nick Tan-梓潼Blog』