设计模式之访问者模式
一、访问者模式简介
访问者设计模式(Visitor Design Pattern)是一种行为设计模式,它允许在不修改对象结构的前提下,增加作用于一组对象的新操作。该模式的核心在于将一个对象的集合与作用于这些对象上的操作分离,这样可以在不修改原有对象结构的情况下,增加新的操作。
二、访问者设计模式的结构
访问者模式类图:
访问者设计模式主要包含以下几个角色:
-
Visitor(访问者):为对象结构中每一个具体元素类(ConcreteElement)声明一个访问操作接口。这个接口使得该访问者可以访问每一个元素,并且可以不通过访问者接口的访问来修改元素的类。
-
ConcreteVisitor(具体访问者):实现Visitor接口,它给每个ConcreteElement一个访问操作。这个操作根据需要,可能会修改访问者状态,并导向对结构中元素的访问。
-
Element(元素):定义一个接受访问者的元素接口。
-
ConcreteElement(具体元素):实现Element接口,存储或定义元素的数据。它包含一个接受访问者对象的操作,这个操作通过调用访问者的访问方法来增加新的操作。
-
ObjectStructure(对象结构):这是一个元素的集合,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素。
三、访问者设计模式的优点
- 增加新的操作容易:在不修改原有类结构的情况下,增加新的操作(访问者)。
- 符合单一职责原则:将有关的行为集中到一个访问者对象中,而不是分散到多个类的层次结构中。
- 类的扩展性良好:访问者对象可以对不同的类实施不同的操作,而且新增操作较容易。
四、访问者设计模式的缺点
- 增加新的元素类困难:每增加一个新的元素类,都要在所有的具体访问者类中增加相应的具体操作,违反了开闭原则。
- 破坏封装:访问者需要访问并调用每一个元素类的accept操作,这意味着元素类中的一些操作细节会对访问者公开,违反了封装性。
- 具体元素变更较大:如果元素类的数量过多,那么访问者类的接口就会很大,增加访问者类也会很困难。
五、适用场景
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。
- 当你定义对象结构的类库时,类库的使用者不知道将会对对象进行哪些操作。
六、实现例子
下面是一个使用Java实现的访问者模式的基本例子。在这个例子中,我们将创建一个简单的对象结构,其中包含不同类型的元素(如书籍和电影),并且我们将定义一个访问者接口以及具体的访问者类来对这些元素执行不同的操作。
首先,我们定义Element接口和具体的元素类(Book和Movie):
// Element.java public interface Element { void accept(Visitor visitor); } // Book.java public class Book implements Element { private String title; public Book(String title) { this.title = title; } //这步比较关键,让访问者可以访问元素自己。 @Override public void accept(Visitor visitor) { visitor.visit(this); } public String getTitle() { return title; } } // Movie.java public class Movie implements Element { private String title; public Movie(String title) { this.title = title; } @Override public void accept(Visitor visitor) { visitor.visit(this); } public String getTitle() { return title; } }
接下来,我们定义Visitor接口和具体的访问者类(比如AuthorVisitor和LengthVisitor):
// Visitor.java public interface Visitor { void visit(Book book); void visit(Movie movie); } // AuthorVisitor.java public class AuthorVisitor implements Visitor { @Override public void visit(Book book) { System.out.println("Book: " + book.getTitle() + " (Author Visitor)"); } @Override public void visit(Movie movie) { System.out.println("Movie: " + movie.getTitle() + " (This visitor does not handle movies)"); } } // LengthVisitor.java public class LengthVisitor implements Visitor { @Override public void visit(Book book) { System.out.println("Book: " + book.getTitle() + " (Length: Average) (Length Visitor)"); } @Override public void visit(Movie movie) { System.out.println("Movie: " + movie.getTitle() + " (Length: Feature) (Length Visitor)"); } }
最后,我们创建一个ObjectStructure类来持有元素集合,并允许访问者访问这些元素:
// ObjectStructure.java import java.util.ArrayList; import java.util.List; public class ObjectStructure { private List elements = new ArrayList(); public void addElement(Element element) { elements.add(element); } public void accept(Visitor visitor) { for (Element element : elements) { element.accept(visitor); } } // 示例方法,用于添加书籍和电影 public void addBooksAndMovies() { addElement(new Book("Java Programming")); addElement(new Movie("Inception")); addElement(new Book("Design Patterns")); } } // Client.java public class Client { public static void main(String[] args) { ObjectStructure objectStructure = new ObjectStructure(); objectStructure.addBooksAndMovies(); objectStructure.accept(new AuthorVisitor()); System.out.println("----------"); objectStructure.accept(new LengthVisitor()); } }
在这个例子中,ObjectStructure类维护了一个Element对象的列表,并允许Visitor对象遍历这些元素。AuthorVisitor和LengthVisitor是两种不同类型的访问者,它们分别实现了Visitor接口,并对Book和Movie对象执行不同的操作。通过accept方法,ObjectStructure允许访问者访问其包含的元素。
简单的说,根据特点目标对元素列表进行特定行为操作,比较适合访问者模式,本例中类似提取元素标题、长度、时长等维度的操作,就是特定目标,书本及电影就是元素。
如果该设计模式对你有用,记得点赞收藏。