Design Patterns (24) - Visitor Pattern

visitor pattern

1. Definition

       Represents an operation that acts on elements in an object structure, allowing you to define new operations that act on elements without changing their classes.

2. Sample code

   

/**
 * The visitor interface for accessing the composite object structure
 */
public interface Visitor {
    /**
     * Accessing the composite object is equivalent to adding a visitor function to the composite object
     * @param composite composite object
     */
    public void visitComposite(Composite composite);
    /**
     * Accessing the leaf object is equivalent to adding a visitor function to the leaf object
     * @param leaf leaf object
     */
    public void visitLeaf(Leaf leaf);
}

 

/**
 * Abstract component object, equivalent to the element object in the visitor pattern
 */
public abstract class Component {
    /**
     * Accept visitor's visit
     * @param visitor visitor object
     */
    public abstract void accept(Visitor visitor);
    /**
     * Add the component object to the composite object
     * @param child is the component object that is added to the composite object
     */
    public void addChild(Component child) {
       // The default implementation, throwing an exception, the leaf object does not have this function, or the child component does not implement this function
       throw new UnsupportedOperationException("The object does not support this function");
    }
    /**
     * Remove a component object from the composite object
     * @param child the component object from which the child was moved
     */
    public void removeChild(Component child) {
       // The default implementation, throwing an exception, the leaf object does not have this function, or the child component does not implement this function
       throw new UnsupportedOperationException("The object does not support this function");
    }
    /**
     * Return the component object corresponding to an index
     * @param index The index of the component object to be obtained, the index starts from 0
     * @return the component object corresponding to the index
     */
    public Component getChildren(int index) {
       throw new UnsupportedOperationException("The object does not support this function");
    }
}

   

/**
 * A composite object, which can contain other composite objects or leaf objects,
 * Equivalent to the specific Element implementation object of the visitor pattern
 *
public class Composite extends Component{
    public void accept(Visitor visitor) {
       // Call back the corresponding method of the visitor object
       visitor.visitComposite(this);
       / / Loop child elements, let the child elements also accept access
       for(Component c : childComponents){
           //Call the sub-object to accept access, and implement recursion in disguise
           c.accept(visitor);
       }
    }
    /**
     * Used to store the subcomponent objects contained in the composite object
     */
    private List<Component> childComponents = new ArrayList<Component>();
    /**
     * the name of the composite object
     */
    private String name = "";
    /**
     * Constructor, pass in the name of the combined object
     * @param name the name of the composite object
     */
    public Composite(String name){
       this.name = name;
    }
    public void addChild(Component child) {
       childComponents.add(child);
    }
    public String getName() {
       return name;
    }
}

   

/**
 * Leaf object, equivalent to the specific Element implementation object of the visitor pattern
 */
public class Leaf extends Component{
    public void accept(Visitor visitor) {
       // Call back the corresponding method of the visitor object
       visitor.visitLeaf(this);
    }
    /**
     * the name of the leaf object
     */
    private String name = "";
    /**
     * Constructor, pass in the name of the leaf object
     * @param name the name of the leaf object
     */
    public Leaf(String name){
       this.name = name;
    }
    public String getName() {
       return name;
    }
}

 

/**
 * Specific visitor, implementation: output the name of the object, add "node:" in front of the name of the composite object,
 * Add "leaf:" before the name of the leaf object
*/
public class PrintNameVisitor implements Visitor {
    public void visitComposite(Composite composite) {
       //Access the data of the composite object
       System.out.println("节点:"+composite.getName());
    }
    public void visitLeaf(Leaf leaf) {
       //Access the data of the leaf object     
       System.out.println("叶子:"+leaf.getName());
    }
}

 

/**
 * Object structure, where element objects are usually traversed, allowing visitors to access all elements
 */
public class ObjectStructure {
    /**
     * Represents an object structure, which can be a composite structure
     */
    private Component root = null;
    /**
     * Provides a high-level interface for client operations
     * @param visitor The visitor the client needs to use
     */
    public void handleRequest(Visitor visitor){
       //Let the root element in the combined object structure accept access
       //The traversal of elements has been implemented in the combined object structure
       if(root!=null){
           root.accept(visitor);
       }
    }
    /**
     * Pass in the combined object structure
     * @param ele composition object structure
     */
    public void setRoot(Component ele){
       this.root = ele;
    }
}

 

public class Client {
    public static void main(String[] args) {
       //define all composite objects
       Component root = new Composite("服装");
       Component c1 = new Composite("男装");
       Component c2 = new Composite("女装");
       //define all leaf objects
       Component leaf1 = new Leaf("Shirt");
       Component leaf2 = new Leaf("jacket");
       Component leaf3 = new Leaf("skirt");
       Component leaf4 = new Leaf("Set");
       / / Combine the composition object and the leaf object according to the structure of the tree
       root.addChild(c1);
       root.addChild(c2);   
       c1.addChild(leaf1);
       c1.addChild(leaf2);      
       c2.addChild(leaf3);
       c2.addChild(leaf4);    
       //Create ObjectStructure
       ObjectStructure os = new ObjectStructure();
       os.setRoot(root);      
       //Call ObjectStructure to handle the request function
       Visitor psVisitor = new PrintNameVisitor();
       os.handleRequest (psVisitor);
    }
}

 

/*
*To achieve this function, it is more difficult to traverse sub-objects in the combined object structure, because to output this tree structure, it is necessary to control the number of backspaces of each object when outputting, which needs to be in the The object structure is controlled in a loop, this function can choose to traverse the object structure among the visitors.
* To adapt the above example, see how to iterate through the elements through the visitor to achieve such a function.
*First, remove the code that recursively calls sub-objects in the composite's accept implementation, and add a method that allows visitors to access the sub-objects it contains. The sample code is as follows:
*/
public class Composite extends Component{
     //Other identical parts are omitted, just look at the method of change
     public void accept(Visitor visitor) {
       // Call back the corresponding method of the visitor object
       visitor.visitComposite(this);    
     }
     public List<Component> getChildComponents() {
       return childComponents;
     }
}

/**
* Specific visitor, implementation: output the structure of the combined object itself
*/
public class PrintStructVisitor implements Visitor {
    /**
     * Used to accumulate the grid that the record object needs to go back
     */
    private String preStr = "";
    public void visitComposite(Composite composite) {
       // first output yourself
       System.out.println(preStr+"+"+composite.getName());
       //If it also contains subcomponents, then output these subcomponent objects
       if(composite.getChildComponents()!=null){
           //Then add a space to indent one space back
           preStr+=" ";     
           // output the child object of the current object
           for(Component c : composite.getChildComponents()){
              // recursively output each child object
              c.accept(this);
           }
           //Remove the extra backspace added by the loop sub-object
           preStr = preStr.substring (0, preStr.length () - 1);
       }
    }
    public void visitLeaf(Leaf leaf) {
       //Access the data of the leaf object     
       System.out.println(preStr+"-"+leaf.getName());
    }
}
/*Client test*/
public class Client {
    public static void main(String[] args) {
       //The process of defining all the combined objects is the same as the previous client, which is omitted here   
       //Call the method of the root element to accept the request function
       Visitor psVisitor = new PrintStructVisitor();
       root.accept(psVisitor);
    }
}

  

3. Practical application

       The visitor pattern can transparently add new functionality to a set of objects. This avoids modifying this series of objects during maintenance, and also realizes the functions of multiplexing visitors in disguise. Because it is an operation for a series of objects, it also causes some trouble if you only want to add functions to some objects in a series of objects; and it is always guaranteed that this series of objects are called, no matter if it is a loop No matter whether it is recursive or recursive, in short, every object must be accessed.
  • If you want to perform some operations on an object structure that depend on concrete classes in the object structure, you can use the visitor pattern
  • If you want to perform many different and unrelated operations on each element in an object structure, in order to avoid these operations from cluttering the class, you can use the visitor pattern to distribute these operations to different visitor objects. Each visitor object implements the same class of functionality
  • If the object structure rarely changes, but you need to define new operations for the element objects in the object structure frequently, you can use the visitor pattern
The essence of the visitor pattern: reserved access, callback implementation

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326277851&siteId=291194637