This is the 18th day of my participation in the August Genwen Challenge.More challenges in August

In his book JAVA and Patterns, Dr. Yan Hong describes the Visitor pattern at the beginning: Visitor pattern is the behavior pattern of objects. The purpose of the visitor pattern is to encapsulate operations that are imposed on some data structure element. If the operation needs to be modified, the data structure that receives the operation can remain unchanged.

Before moving on to the visitor pattern, let’s look at the concept of dispatch.

What is dispatch?

The types of variables declared are called Static types or Apparent types. The actual Type of the object referenced by a variable at runtime is called the actual Type of the variable (Actural Type). Take the following example:

List<String> list = new ArrayList<String>();
Copy the code

In this case, the display type of list is list, while the actual type is ArrayList.

The choice of method based on the type of object is Dispatch, which is divided into two types: static Dispatch and dynamic Dispatch.

  • Static Dispatch occurs at compile time, and Dispatch occurs based on Static type information. Static dispatch is not new to us, and method overloading is static dispatch.
  • Dynamic Dispatch occurs at run time and dynamically swaps out a method. Method overwriting is dynamic dispatch.

Object-oriented languages can be divided into uni-Dispatch and multi-Dispatch based on how many cases dispatching can be based on. Single-dispatch languages select methods based on the type of one cell, and multi-dispatch languages select methods based on the type of more than one cell.

Java is a dynamic single-dispatch language, which only takes into account the type of the method’s receiver, and a static multi-dispatch language, which takes into account the type of the method’s receiver and the types of all its parameters.

In a language that supports dynamic single dispatch, there are two conditions that determine which operation a request invokes. The second condition is the name of the request, but the actual type of the receiver. Single dispatch limits the method selection process so that only one case can be considered, and that case is usually the receiver of the method.

Visitor pattern

Visitor pattern is a more complex behavior pattern, which is mainly composed of visitors and visitors. Usually, the elements visited have different type attributes, and generally, different visitors have different operations on their access priorities. So the purpose of the visitor pattern is to encapsulate operations on elements that operate on a data structure, and then define new operations on those elements without changing the data structure.

Visitor mode is similar to decorator mode in that both are new features. Decoration mode is more about enhancing, modifying or completely reimplementing existing functions. The visitor pattern is more about adding new functionality to the object structure.

Visitor pattern structure

UML diagrams

The constituent roles of the visitor pattern

  • Abstract Visitor Role: In general, the number of methods is the same as the number of elements. Therefore, the Visitor pattern requires that the type of elements be stable. Do not frequently add or delete Element classes, otherwise it will cause frequent modification of the Visitor interface.
  • ConcreteVisitor: ConcreteVisitor implements an abstract visitor method that defines the concrete behavior of each element when it is visited.
  • Abstract Element role (Element) : Typically an abstract class or interface that defines an Accept method, usually taking an abstract visitor as an argument.
  • ConcreteElement roles: concrete elements implement the Accept method, which calls the visitor’s access method to provide a concrete access implementation.
  • ObjectStructure: an ObjectStructure is a collection of elements that hold element objects and provide ways to facilitate their internal elements.
Case presentation

The Visitor pattern is a complex structure and concept pattern that is not used very often, but can be very flexible in specific scenarios.

Here I use an example from the Android Source Design Pattern to demonstrate the use of the visitor pattern. In our performance appraisal, the CTO only cares about the number of employees’ achievements, while the CEO only needs to know the kpi value of employees. In this scenario, the visitor pattern can be used in situations where different employees in the company have different priorities for leaders to visit.

Define an abstract employee class

abstract class Staff {
	private String name;
	private int kpi;
	public Staff(String name, int kpi) {
		super(a);this.name = name;
		this.kpi = kpi;
	}
	
	/** * defines the accept interface to access the specific internal member *@param visitor
	 */
	public abstract void accept(IVisitor visitor);

	public String getName(a) {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getKpi(a) {
		return kpi;
	}

	public void setKpi(int kpi) {
		this.kpi = kpi; }}Copy the code

In the abstract employee class, we define common attributes: name, KPI, and the Accept interface to receive visitors.

Statement of specific Employees

/** * Software development engineer *@author Iflytek_dsw
 *
 */
class DeveloperStaff extends Staff{
	/** * Software engineer specific characteristics, coding amount. * /
	private int codes;
	public DeveloperStaff(String name, int kpi, int codes) {
		super(name, kpi);
		this.codes = codes;
	}

	@Override
	public void accept(IVisitor visitor) {
		visitor.visit(this);
	}
	
	/** * get the programmer's code amount *@return* /
	public int getCodesLines(a){
		return this.codes; }}/** * Product Manager *@author Iflytek_dsw
 *
 */
class ProductManager extends Staff{
	/** * Product manager's unique attributes, how many products have been designed */
	private int products;
	public ProductManager(String name, int kpi, int products) {
		super(name, kpi);
		this.products = products;
	}

	@Override
	public void accept(IVisitor visitor) {
		visitor.visit(this);
	}
	
	/** * Get the number of products designed by the product manager *@return* /
	public int getProductsCount(a){
		return this.products; }}Copy the code

Above, we define two roles: software development engineer and product manager, and the two roles have different emphases, including code and product design.

Declare abstract Visitors

/** * Declare the abstract visitor interface *@author Iflytek_dsw
 *
 */
interface IVisitor {
	/** ** Interview development engineer *@param developerStaff
	 */
	void visit(DeveloperStaff developerStaff);
	
	/** * Visit product manager *@param deProductManager
	 */
	void visit(ProductManager deProductManager);
}
Copy the code

Here we declare the specific role visitor interface based on the corresponding element type (employee type).

Declare a specific visitor interface

/** * THE CEO is concerned about *@author Iflytek_dsw
 *
 */
class CEOVisitor implements IVisitor{

	@Override
	public void visit(DeveloperStaff developerStaff) {
		System.out.println("CEO only cares about kpi value of employee, name:" + developerStaff.getName()
				 + "--kpi:" + developerStaff.getKpi());
	}

	@Override
	public void visit(ProductManager deProductManager) {
		System.out.println("CEO only cares about kpi value of employee, name:" + deProductManager.getName()
				 + "--kpi:"+ deProductManager.getKpi()); }}/** * CTO visitors, focus on employee success *@author Iflytek_dsw
 *
 */
class CTOVisitor implements IVisitor{

	@Override
	public void visit(DeveloperStaff developerStaff) {
		System.out.println("CTO only focuses on employee's results. Name:" + developerStaff.getName()
				 + "-- Code quantity :" + developerStaff.getCodesLines());
	}

	@Override
	public void visit(ProductManager deProductManager) {
		System.out.println("CTO only focuses on employee's results. Name:" + deProductManager.getName()
				 + "-- Product number :"+ deProductManager.getProductsCount()); }}Copy the code

Here we declare the CEO visitor and CTO visitor interfaces, targeting them for accessing different key information.

Declare object structure roles

public class StaffManager {
	private List<Staff> staffList;
	
	public StaffManager(a){
		staffList = new ArrayList<>();
		staffList.add(new DeveloperStaff("Android Learning Notes".2.23000));
		staffList.add(new DeveloperStaff("Andoter learning".3.25000));
		staffList.add(new ProductManager("Product Designer no. 1 Technician".2.5));
		staffList.add(new ProductManager("Product Designer technician No. 2".2.5));
	}

	/** * Access all employee information *@param visitor
	 */
	public void showStaffInfo(IVisitor visitor){
		for(Staff staff : staffList){ staff.accept(visitor); }}/** * Add a single employee *@param staff
	 */
	public void addStaff(Staff staff){
		staffList.add(staff);
	}
	
	/** * Add all employees *@param staffList
	 */
	public void addStaffAll(List<Staff> staffList){
		this.staffList.addAll(staffList); }}Copy the code

Declare the StaffManager class to manage the Staff.

The client

public class Client {

	/ * * *@param args
	 */
	public static void main(String[] args) {
		StaffManager staffManager = new StaffManager();
		/ / create CEOVisitor
		IVisitor ceoVisitor = new CEOVisitor();
		staffManager.showStaffInfo(ceoVisitor);
		
		/ / create CTOVisitor
		IVisitor ctoVisitor = newCTOVisitor(); staffManager.showStaffInfo(ctoVisitor); }}Copy the code

The results of

The CEO only pays attention to the kpi value of the employee.2The CEO only focuses on the kpi value of the employee, name: Andoter Learning -- KPI:3The CEO only pays attention to the KPI value of the employee, name: Product designer1Technician - kpi:2The CEO only pays attention to the KPI value of the employee, name: Product designer2Technician - kpi:2CTO only focuses on the value of the employee's results.23000CTO only focuses on employee's results, name: Andoter Learning -- Code volume:25000CTO only focuses on the value of the employee's results. Name: Product Designer1No. Technician -- Product number:5CTO only focuses on the value of the employee's results. Name: Product Designer2No. Technician -- Product number:5
Copy the code
Advantages and disadvantages of the Visitor pattern

advantages

  • Good scalability, can add new functions for objects in the structure without modifying the elements of the object structure;
  • Good reuse, through visitors to define the whole object structure of the common function, so as to improve the reuse program;
  • Separate irrelevant behaviors. You can separate unrelated behaviors from visitors and encapsulate related behaviors into a single visitor, so that each visitor has a single function.

disadvantages

  • Object structure changes are difficult. Adding a new Element subclass causes the Visitor class to change, and the Visitor class gets bigger and bigger as more Element subclasses are added.
  • Break encapsulation. The visitor pattern typically requires the ObjectStructure to open its internal data to visitors and ObjectStructure, which breaks the encapsulation of the object.

Secondary dispatch technique

A method that executes different code depending on the type of two classes is called “double dispatch.” The Java language does not support dynamic multiple dispatch, which means that Java does not support dynamic double dispatch. But it is also possible to implement dynamic double dispatch in the Java language by using design patterns.

Case presentation

Create the observation Element Element Element

class Problem {
	void accept(Support support){
		support.solve(this); }}class HardProblem extends Problem{

	@Override
	void accept(Support support) {
		support.solve(this); }}Copy the code

Create the Element Element and declare the Accept method to accept observers.

Create observer

class Support {
	void solve(Problem problem){
		System.out.println("Ordinary Supporters: Fixing Common Problems.");
	}
	
	void solve(HardProblem hardProblem){
		System.out.println("Ordinary Supporters: Fixing hard Problems."); }}class SpecialSupport extends Support{

	@Override
	void solve(Problem problem) {
		System.out.println("Expert supporter: Fixing common Problems.");
	}

	@Override
	void solve(HardProblem hardProblem) {
		System.out.println("Expert supporter: Fixing hard Problems."); }}Copy the code

Client demo

public class Client {

	/ * * *@param args
	 */
	public static void main(String[] args) {
		Problem problem = new Problem();
		Problem hardProblem = new HardProblem();
		/** * Single-dispatch, because the solve function is a polymorphic function, it finds the solve method in SpecialSupport via hyperstates * but because the parameter type of the function is statically bound, that is, the static type of the HardProblem is Problem. * Therefore, when compiling to problem and hardProblem, it is fixed to solve(problem). Although the actual types of * problem and hardProblem are different during runtime, Java does not call the corresponding overloaded functions according to this difference. * That is, there is no second dispatch, that is, Java only supports single dispatch. * /
		SpecialSupport specialSupport = new SpecialSupport();
		specialSupport.solve(problem);
		specialSupport.solve(hardProblem);
		
		System.out.println("");
		/** * simulates double dispatch * By letting the compiler know at compile time of the incoming type, overloaded functions can be called correctly. * How can you do this? The trick is to make the argument the calling function and provide a polymorphic accept method in the argument class. The main purpose of this function is to use polymorphism to get the actual type of the argument object */problem.accept(specialSupport); hardProblem.accept(specialSupport); }}Copy the code

On the first execution, you do this by binding the Problem to Support, which is single-dispatch because the solve function is a polymorphic function that finds the solve method in SpecialSupport through hyperstates, but because the parameter type of the function is statically bound, The static type of Problem, HardProblem, is Problem. Therefore, when compiling to problem and hardProblem, the function solve(Problem) is bound to the function solve(problem). Although the actual type of problem and hardProblem is different during running, Java does not call the corresponding overloaded function according to this difference. That is, there is no second dispatch, that is, Java only supports single dispatch. On the second execution, assigning the problem to specific Support simulates double dispatch, and by letting the compiler know the type passed in at compile time, the overloaded function can be called correctly. But how do you do that? The trick is to make the argument the calling function and provide a polymorphic accept method in the argument class. The main purpose of this function is to use polymorphism to get the actual type of the argument object.

The results

Expert supporter: Repair common problems Expert supporter: Repair common problems Expert supporter: repair difficult problemsCopy the code

The double dispatch technique enables the client’s request to be no longer statically bound to the element object. What function is actually performed depends on both the visitor type and the element type. Even the same element type, as long as the visitor type is different, the final function will be different ****. This allows you to change the functionality that is actually performed by changing the visitor type while the element object remains the same.

References:

  • JAVA and Patterns, visitor patterns
  • Behavioral pattern – Visitor pattern