# 七大软件规划准则
# 规划形式-工厂形式
# 规划形式-单例形式
# 规划形式-原型形式
# 规划形式-策略形式
# 规划形式-责任链形式
# 规划形式-建造者形式
# 规划形式-深度分析代理形式
# 规划形式-门面形式
# 规划形式-装饰器形式
# 规划形式-享元形式
# 规划形式-组合形式
# 规划形式-适配器形式
# 规划形式-桥接形式
# 规划形式-派遣形式
# 规划形式-模板办法形式
# 规划形式-迭代器形式
# 规划形式-指令形式
# 规划形式-备忘录形式
# 规划形式-状况形式
# 规划形式-中介者形式
# 规划形式-解释器形式

拜访者形式(Visitor Pattern)是一种将数据结构与数据操作别离的规划形式。是指封装一 些作用于某种数据结构中的各元素的操作,它能够在不改动数据结构的条件下定义作用于这些 元素的新的操作。归于行为型形式。

拜访者形式的基本思想是,针对系统中具有固定类型数的目标结构(元素),在其内供给一个accept()办法用来接受拜访者目标的拜访。不同的拜访者对同一元素的拜访内容不同,使得相同的元素调集能够发生不同的数据成果。accept()办法能够接收不同的拜访者目标,然后在内部将自己(元素)转发到接收到的拜访者目标的visit()办法内。拜访者内部对应类型的 visit()办法就会得到回调履行,对元素进行操作。也便是经过两次动态分发(第一次是对拜访者的分发 accept()办法,第二次是对元素的分发 visit()办法,才最终将一个详细的元素传递到一个详细的拜访者。如此一来,就解耦了数据结构与操作,且数据操作不会改动元素状况。拜访者形式的中心是,解耦数据结构与数据操作,使得对元素的操作具有优秀的扩展性。能够经过扩展不同的数据操作类型(拜访者)完成对相同元素集的不同的操作。

拜访者形式在现实中的场景

经过上文的描述或许某些人仍是很模糊,这里咱们拿一个日子中的例子来说明。
比方咱们去动物园,动物园中有许多场馆、熊猫馆、企鹅馆等等,这些馆项基本上都是固定的很少改变的,然后每个馆的门票又不相同,针对不同的拜访者比方学生、军人、大众又有不同的扣头,如果这个票价写在 “馆” 这个类中那咱们岂不是要写许多ifelse,针对不同的人回来不同的价格,假设又要添加一种拜访者又要修正原办法不契合开闭准则,或许有人说咱们能够经过添加类承继的方式来完成,那你要多少个类啊是吧,所以此时就比较适用于拜访者形式, 所以当系统中存在类型数目安稳(固定)的一类数据结构时,能够经过拜访者形式方便地完成对该类型所有数据结构的不同操作,而又不会数据发生任何副作用(脏数据)。

总结一下拜访者形式的适用场景如下:

  1. 数据结构安稳,作用于数据结构的操作常常改变的场景;
  2. 需求数据结构与数据操作别离的场景;
  3. 需求对不同数据类型(元素)进行操作,而不使用分支判别详细类型的场景。

UML类图

设计模式-访问者模式
咱们只看UML类图就知道这是一个比较复杂的规划形式。

示例代码

针对上文中动物园的例子咱们能够提取一下几种人物:

  1. 动物园:其实就相当于接口目标,里面存着许多场馆供拜访者拜访
  2. 场馆:便是元素目标,不同场馆票价不同
  3. 游客:对应这拜访者,不同游客响应的扣头也不同

首要创立拜访者:
依据上文咱们之前到拜访者笼统类只需供给拜访的办法

public interface Tourist {
    void visit(PandaHouse pandaHouse);
    void visit(PenguinHouse penguinHouse);
}

拜访者便是游客,游客能够拜访不同的场馆,有些人或许异或为什么重载visit函数而不是只需求一个就能够,下文中详细说明

详细拜访者

public class Student implements Tourist{
    @Override
    public void visit(PandaHouse pandaHouse) {
        System.out.println("学生观看熊猫馆的票价是:"+pandaHouse.getPandaHouseTicket() * 0.8);
    }
    @Override
    public void visit(PenguinHouse penguinHouse) {
        System.out.println("学生观看企鹅馆的票价是:"+penguinHouse.getPenguinHouseTicket() * 0.8);
    }
}
public class Serviceman implements Tourist{
    @Override
    public void visit(PandaHouse pandaHouse) {
        System.out.println("军人观看熊猫馆的票价是:"+pandaHouse.getPandaHouseTicket() * 0.5);
    }
    @Override
    public void visit(PenguinHouse penguinHouse) {
        System.out.println("军人观看企鹅馆的票价是:"+penguinHouse.getPenguinHouseTicket() * 0.5);
    }
}

接着上文说为什么visit重载这里主要是是因为每个场馆或许都有不同的功能,在完成visit办法的时分如果仅仅是个接口咱们是不是要添加类型判别,多添加一种场馆就需求多加一个判别相似下面这种代码

public void visit(IHouse house) {
    if(house instanceof PenguinHouse){
        ...
    }else if(house instanceof PandaHouse){
        ...
    }
}

这样的话加一个场馆就要修正一下这个办法,如果咱们吧visit办法责任单一化,是不是更好

再创立笼统元素
依据上文的UML图咱们能够看出笼统人物主要就供给一个accept办法,用来统一拜访者拜访

public interface IHouse {
    void accept(Tourist people);
}

详细元素:

public class PandaHouse implements IHouse{
    @Override
    public void accept(Tourist people) {
        people.visit(this);
    }
    public int getPandaHouseTicket() {
        return 100;
    }
}
public class PenguinHouse implements IHouse{
    @Override
    public void accept(Tourist people) {
        people.visit(this);
    }
    public int getPenguinHouseTicket() {
        return 80;
    }
}

最后便是结构目标:动物园

public class Zoo {
    private List<IHouse> houseList = new ArrayList<IHouse>();
    {
        this.houseList.add(new PandaHouse());
        this.houseList.add(new PenguinHouse());
    }
    public void accept(Tourist tourist) {
        for (IHouse house : this.houseList) {
            house.accept(tourist);
        }
    }
}

houseList便是一个固定的结构(一般情况下动物园的场馆不会随便添加削减)

咱们能够看一下这个例子的UML类图:

设计模式-访问者模式
和上文中的类图相同
履行成果如下:

public class Client {
   public static void main(String[] args) {
       Zoo zoo = new Zoo();
       zoo.accept(new Student());
       zoo.accept(new Serviceman());
   }
}

设计模式-访问者模式

拜访者形式的优缺点

长处:

  1. 解耦了数据结构与数据操作,使得操作调集能够独立改变;
  2. 扩展性好:能够经过扩展拜访者人物,完成对数据集的不同操作;
  3. 元素详细类型并非单一,拜访者均可操作;
  4. 各人物责任别离,契合单一责任准则。

缺点:

  1. 无法添加元素类型:若系统数据结构目标易于改变,常常有新的数据目标添加进来,则拜访者类有必要添加对应元素类型的操作,违背了开闭准则;

敞开成长之旅!这是我参加「日新计划 2 月更文挑战」的第 3 天,点击查看活动概况