浅谈设计模式-visitor访问者模式

先看一个和visitor无关的案例.假设你现在有一个书架,这个书架有两种操作,1添加书籍2阅读每一本书籍的简介.

//书架
public
class Bookcase {   List<Book> structure = new ArrayList(); public void add(Book book) { structure.add(book); }   //查看书籍的简介 public void readIntroduction() { for (Book book : structure) { if (book.getClass() == BookA.class) { BookA bookA = (BookA) book; bookA.IntroductionA(); } else if (book.getClass() == BookB.class) { BookB bookB = (BookB) book; bookB.IntroductionB(); } else { BookC bookC = (BookC) book; bookC.IntroductionC(); } } } }
//书籍接口
public interface Book {
}
public class BookA implements Book{

    //A书的简介
    public void IntroductionA(){
        System.out.println("我是一本神奇的书AAA");
    }
}
public class BookB implements Book{

    //B书的简介
    public void IntroductionB(){
        System.out.println("我是一本神奇的书BBB");
    }
}
public class BookC implements Book{

    //C书的简介
    public void IntroductionC(){
        System.out.println("我是一本神奇的书CCC");
    }
}
//客户端
public class Client {
    public static void main(String[] args) {
        Bookcase bookcase = new Bookcase();

        bookcase.add(new BookA());
        bookcase.add(new BookB());
        bookcase.add(new BookC());

        bookcase.readIntroduction();

//我是一本神奇的书AAA
//我是一本神奇的书BBB
//我是一本神奇的书CCC  

} }

每一本书籍的介绍都是不一样的方法.为了查看书籍的介绍,我们不得不在循环中做类型的判断书籍的类型,然后做强制类型转换.(注意!!!以上代码为了演示出现的问题,所以将每本书的介绍

放到了不同的方法内)

下面引入visitor设计模式解决这个问题.

首先引入一个抽象的访问者接口,该接口有已知需要访问的书籍的访问方式.

//访问者接口
public interface Visitor {

    public void read(BookA bookA);
    public void read(BookB bookB);
    public void read(BookC bookC);

}

在引入一个具体访问者类,该类需要实现访问者接口中的所有方法.

public class VisitorImpl implements Visitor {
    public void read(BookA bookA){
        bookA.IntroductionA();
    }
    public void read(BookB bookB){
        bookB.IntroductionB();
    }
    public void read(BookC bookC){
        bookC.IntroductionC();
    }
}

在原有的书籍接口中抽象一个方法,该方法接收一个具体访问者对象.

//书籍接口
public interface Book {
    public void accept(Visitor visitor);
}

接下来让每本具体的书都重写接口中的方法.

public class BookA implements Book{

    public void accept(Visitor visitor){
        visitor.read(this);
    }
    //A书的简介
    public void IntroductionA(){
        System.out.println("我是一本神奇的书AAA");
    }
}
public class BookB implements Book{

    public void accept(Visitor visitor){
        visitor.read(this);
    }
    //B书的简介
    public void IntroductionB(){
        System.out.println("我是一本神奇的书BBB");
    }
}
public class BookC implements Book{

    public void accept(Visitor visitor){
        visitor.read(this);
    }
    //C书的简介
    public void IntroductionC(){
        System.out.println("我是一本神奇的书CCC");
    }
}

可以看出,每一本书都实现了accept方法,每个方法内调用visitor的read方法,并且把自身传入.接下来看书架类做了哪些修改.

public class Bookcase {

    List<Book> structure = new ArrayList();
    
    private Visitor visitor;
    
    //创建书架,需要给一个访问者
    public Bookcase(Visitor visitor) {
        this.visitor = visitor;
    }

    public void add(Book book) {
        structure.add(book);
    }
    
    //调用每本书的分派方法.
    public void readIntroduction() {
        for (Book book : structure) {
            book.accept(visitor);
        }
    }
}

最终使用客户端调用书架方法.

//客户端
public class Client {
    public static void main(String[] args) {
        //具体访问者对象
        Visitor visitor = new VisitorImpl();
        
        Bookcase bookcase = new Bookcase(visitor);

        bookcase.add(new BookA());
        bookcase.add(new BookB());
        bookcase.add(new BookC());

        bookcase.readIntroduction();
//我是一本神奇的书AAA
//我是一本神奇的书BBB
//我是一本神奇的书CCC } }

大概缕一下调用过程.首先创建一个书架需要传入一个具体访问者对象.当书架调用查询所有书籍介绍时,首先是A书籍,A书籍调用自身的委托功能,将自身传递给具体访问者对象,并且调用具体

访问者对象的read()方法.如此具体方法着触发 参数为BookA的重载方法,调用A书籍自身的介绍方法.这是一个双重分派过程.

访问者模式中的角色

1 抽象访问者角色:抽象访问者角色必须定义出所有已知节点.(Visitor)

2 具体访问者角色:实现所有抽象角色的方法,调用每个方法调用节点自身的访问功能.(VisitorImpl)

3 抽象节点角色:抽象节点角色定义出一个分配功能,将书籍自身的访问方法,委托给访问者角色.(Book)

4 具体节点角色:定义自身的业务逻辑,实现接口中的委托方法,将自身传递给访问者角色(BookA,BookB,BookC)

5 结构对象角色:定义了存放操作具体角色的类,对具体角色进行增加,查询.(Bookcase)

 

访问者模式中的优点和缺点

首先谈一下自己的拙见,然后咱们在看书中的标准描述:

  优点:对把数据和操作分开了,例子中的书籍其实就是数据,每本书都有自己的介绍功能,而具体访问者则负责把所有书籍的介绍功能统统拿过来,由自己管理.

    一个访问者可以访问全部的节点书籍的功能.

  缺点:如果增加一个新的节点,意味着要在抽象访问者角色中添加新的访问方式,还要重写所有的具体访问节点.

书中的总结(摘抄自--Java与模式)

  优点:

    1 访问者模式使得增加新的操作变得很容易.如果一些操作依赖于一个负责的结

     构对象的话,那么一般而言,新增的操作会很复杂.而使用访问者,增加新的操作

     就意味着新增加一个新的访问者类,因此,变得很容易.

          2 访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节

        点类中.

     3 访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类.

     4 积累状态.每一个单独的访问者对象都集中了相关的行为,从而也就可以在访

      的过程中将执行操作的状态积累在自己的内部,而不是分散到很多节点对象中.

      这是有利于系统维护的优点.

  缺点:

      1 增加新的节点类变得很困难.每增加一个新的节点都意味着要在抽象访问角色

       中增加一个新的抽象操作,并在每一个具体访问者类中增加相应的具体操作.

      2 破坏封装.访问者模式要求访问者对象访问并调用每一个节点对象的操作,这

     隐含了一个对所有节点的要求:它们必须暴露一些自己的操作和内部状态.不然

     访问者的访问将变得没有意义.由于访问者对象会自己基类操作所需的转头,从而

     这些状态不在存储在节点对象中,这也是破坏了封装.

由于显然的缺点,访问者模式成为一个有争议的设计模式.有些设计师反对使用访问者模式,

而一些设计师则强调访问者模式的优点,还有一些访问者千方百计的设法修改访问者模式,

克服其不足.事实上,尽管有人反对使用这一模式,访问者模式仍然在很多重要系统中使用.

----------------------------------------------------------------------------------------------------------------------

以上就是visitor访问者模式.如有不足请补充

 

    

 

 

posted @ 2019-08-06 23:51  顶风少年  阅读(271)  评论(0编辑  收藏  举报
返回顶部