Re-learning design patterns (3. Design patterns-visitor patterns)

1. Visitor mode

    The key to the visitor pattern is the word visit, so what is visit? In fact, when learning the iterator pattern, traversal is the general form of access.

    If there is such a requirement:

    1) At the beginning, it was Xiao Huang's appeal - to know the names of all files and folders under a certain folder;

    2) Next, there is Xiaohong's request - to know the number of files and folders in a certain directory;

    3) Then, there is Xiaolan's appeal - to delete a certain file in a certain directory;

    For the above requirements, ours is easy to implement. For Xiao Huang’s request, we can usually print it out by traversing the file tree; for Xiao Hong’s request, we can quickly think of adding a counter to count, which is also our most common method; for Xiao Lan’s request, when we traverse, we judge that the obtained file is Xiao Lan’s need to delete, and execute the delete method.

    For the above, we all have the same operation, which is to traverse the file tree. The most common and easiest method we can think of is method 1: implement in three methods; method 2: share one method and add judgment. What if there are still people who continue to make demands at this time? Also modify the original code.

    In real life, there are actually many such examples. For example, for the same movie, different audiences have different evaluations of them.

    The processed data is relatively stable and the visitor's operations are diversified, so it is more convenient to use the visitor mode to process.

1.1, what is the visitor mode

  • definition

    The visitor pattern is a behavioral design pattern, which is defined in GoF's "Design Pattern" as: representing an operation that acts on each element in an object structure. It allows you to define new operations on elements without changing their class.

    Simply put, the visitor pattern is to separate and encapsulate the operations acting on elements (data structures/algorithms) into independent classes, so that the operation set can be realized relatively freely, and the new operation does not violate the principle of opening and closing.

The structure of the visitor pattern:

    1) Abstract visitor (Visitor) role: define an interface to visit specific elements, and correspond to a visit operation visit() for each specific element class, and its parameter is the specific element of the visitor;

    2) Specific visitor (ConcreteVisitor) role: it needs to give specific behaviors when visiting each element class;

    3) Abstract element (Element) role: Declare an interface that includes the accept operation accept(), and the accepted visitor object is used as a parameter of the accept() method;

    4) The concrete element (ConcreteElement) role: implement the accept() operation provided by the abstract element role, and its method body is usually visitor.visit(this), and the concrete element may also contain related operations of its own business logic;

    5) Object structure (Object Structure) role: Object structure is an abstract expression. Specifically, it can be understood as a class with container properties or composite object characteristics. It will contain a set of elements and can iterate these elements for visitors to visit.

    The purpose of the visitor pattern is to decouple the data structure from the operations on the structure, so that you can define new operations on these elements without changing the class of each element.

3.22.2. Advantages and disadvantages of visitor mode

  • advantage

    1) The scalability is good, and new behaviors can be added to the elements in the object structure without modifying the elements in the object structure;

    2) In line with the principle of single responsibility, multiple operations of the same behavior can be separated into different classes;

    3) Good flexibility, the visitor mode decouples the data structure from the operations on the structure, and is easy to expand.

  • shortcoming

    The details of the operation of specific elements in the visitor pattern are given to the visitor, which breaks the encapsulation of the object.

3.22.3. Creation method

    Let's take shopping as an example. Ordinary users get the total price of the product after adding it to the shopping cart; VIP members get the total price of the product after adding it to the shopping cart.

1) Abstract visitor (Visitor) role

//抽象访问者,定义访问者可以做的事情
public interface CustomerVisitor {

	String browseProducts(Goods goods); //浏览商品
	
}

2) Abstract element (Element) role

//抽象元素,包含接受操作,被访问者作为accept
public abstract class Goods {

	private String name; //商品名称
	
	private Integer price; //商品价格
	
	abstract Integer accept(CustomerVisitor visitor); //接收访问者
	
	public Goods(){
		
	}
	
	public Goods(String name,Integer price){
		this.name = name;
		this.price = price;
	}
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the price
	 */
	public Integer getPrice() {
		return price;
	}

	/**
	 * @param price the price to set
	 */
	public void setPrice(Integer price) {
		this.price = price;
	}

	@Override
	public String toString() {
		return this.name+"价格:"+this.price;
	}
	
}

3) Specific visitor (ConcreteVisitor) role

//具体访问者-普通会员
public class OrdinaryVisitor implements CustomerVisitor{

	@Override
	public String browseProducts(Goods goods) {
		String goodsMessage = goods.toString();
		return goodsMessage;
	}

}

//具体访问者-VIP会员
public class VipVisitor implements CustomerVisitor{

	@Override
	public String browseProducts(Goods goods) {
		goods.setPrice(goods.getPrice()-5);
		String goodsMessage = goods.toString();
		return goodsMessage;
	}

}

4) ConcreteElement role

public class Book extends Goods{

	public Book(String name,Integer price){
		super(name, price);
	}
	
	/**
	 * 接受访问者,并且把自已(商品)传给访问者
	 */
	@Override
	Integer accept(CustomerVisitor visitor) {
		String bp = visitor.browseProducts(this); //把一些处理逻辑(计算费用)的  【操作权限】  给访问者
		System.out.println(bp); //打印浏览价格
		return this.getPrice();
	}

}

5) Role of Object Structure

//对象结构角色
public class ShoppingCartStructure {

	private List<Goods> list = new ArrayList<Goods>();
	
	/**
	 * 计算总价
	 */
	public String accept(CustomerVisitor visitor){
		Iterator<Goods> i = list.iterator();
        Integer price = 0;
        while (i.hasNext()) {
        	price += ((Goods) i.next()).accept(visitor);
        }
		return "商品总价"+price;
		
	}
	
	/**
	 * 添加商品
	 */
	public void add(Goods goods) {
        list.add(goods);
    }
    public void remove(Goods goods) {
        list.remove(goods);
    }
}

6) client

public class Client {

	public static void main(String[] args) {
		Goods hlm = new Book("红楼梦",120);
		Goods sgyy = new Book("三国演义",105);
		Goods xyj = new Book("西游记",80);
		
		//创建访问者-普通会员
		OrdinaryVisitor ov = new OrdinaryVisitor();
		//创建结构对象-购物车
		ShoppingCartStructure scs = new ShoppingCartStructure();
		System.out.println("普通会员浏览商品");
		scs.add(hlm); //添加商品
		scs.add(sgyy); //添加商品
		scs.add(xyj); //添加商品
		System.out.println(scs.accept(ov));
		
		//创建访问者-VIP会员
		VipVisitor vip = new VipVisitor();
		//创建结构对象-购物车
		ShoppingCartStructure vscs = new ShoppingCartStructure();
		System.out.println("VIP会员浏览商品");
		vscs.add(hlm);
		vscs.add(sgyy);
		vscs.add(xyj);
		System.out.println(vscs.accept(vip));
		
	}
}

Case effect:

    In order to understand the visitor pattern more effectively, let's look at the implementation in the JDK. In fact, an interface called FileVisitor was introduced in JDK1.7, and I guessed half of it by looking at the name. It uses the visitor mode.

//抽象访问者(Visitor)角色
public interface FileVisitor<T> {
    FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
        throws IOException;
    FileVisitResult visitFile(T file, BasicFileAttributes attrs)
        throws IOException;
    FileVisitResult visitFileFailed(T file, IOException exc)
        throws IOException;
    FileVisitResult postVisitDirectory(T dir, IOException exc)
        throws IOException;
}
//具体访问者(ConcreteVisitor)角色,在SimpleFileVisitor中只是对FileVisitor的简单实现,并没有什么特别的控制
public class SimpleFileVisitor<T> implements FileVisitor<T> {
    @Override
    public FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
        throws IOException
    {
        Objects.requireNonNull(dir);
        Objects.requireNonNull(attrs);
        return FileVisitResult.CONTINUE;
    }
    @Override
    public FileVisitResult visitFile(T file, BasicFileAttributes attrs)
        throws IOException
    {
        Objects.requireNonNull(file);
        Objects.requireNonNull(attrs);
        return FileVisitResult.CONTINUE;
    }
    @Override
    public FileVisitResult visitFileFailed(T file, IOException exc)
        throws IOException
    {
        Objects.requireNonNull(file);
        throw exc;
    }
    @Override
    public FileVisitResult postVisitDirectory(T dir, IOException exc)
        throws IOException
    {
        Objects.requireNonNull(dir);
        if (exc != null)
            throw exc;
        return FileVisitResult.CONTINUE;
    }
}

//在java.nio.file中的Files类中有个walkFileTree方法,通过FileTreeWalker方法实现
public static Path walkFileTree(Path start,
                                    Set<FileVisitOption> options,
                                    int maxDepth,
                                    FileVisitor<? super Path> visitor)
        throws IOException
    {
        if (maxDepth < 0)
            throw new IllegalArgumentException("'maxDepth' is negative");
        new FileTreeWalker(options, visitor, maxDepth).walk(start);
        return start;
    }

//在walkFileTree中实现了文件树的遍历,且遍历到文件的时候,仍然调用了visitor.visitFile(file, attrs)方法,由遍历者决定是否要继续遍历或做一些其他的操作。
//省略其他部分...
// unable to get attributes of file
if (exc != null) {
    return visitor.visitFileFailed(file, exc);
}

// at maximum depth or file is not a directory
if (depth >= maxDepth || !attrs.isDirectory()) {
    return visitor.visitFile(file, attrs);
}

1.4. Summary and Suggestions

    Do you think that the visitor mode is a bit like the template method mode, which encapsulates fixed things and opens up variable things. The template method pattern reserves extension points for the variable parts, while the visitor pattern separates the variable points and the visitor decides to do some processing, but they still have a little difference. The visitor pattern is a combination relationship between change (visitor) and fixation (visited), while the change and fixation of the template method pattern is an inheritance relationship.

    The visitor mode is further separated from the iterator mode. It is an extension of the iterator mode. It can traverse different objects and perform some other operations while traversing.

Application scenario:

    1) If you need to be more stable with complex object structures or object structures, but you need to perform operations on all elements, you can use the visitor pattern;

    2) All other behavior can be extracted into a set of visitor classes, making your application's main classes more focused on their main job;

    3) Can act as an interceptor.

Application of visitor pattern in JDK:

    javax.lang.model.element.AnnotationValue-AnnotationValueVisitor

    javax.lang.model.element.Element-ElementVisitor

    java.nio.file.FileVisitor-SimpleFileVisitor

    javax.faces.component.visit.VisitContext-VisitCallback

Guess you like

Origin blog.csdn.net/xiaoxianer321/article/details/125472340