ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Visitor
    Modeling/DesignPattern 2020. 2. 29. 19:02

    1. Overview

    • Visitor pattern allows us to define new operations that can be performed on an object without changing the class definition of the object
    • Think of this pattern as an object visitor that visits all nodes in an object structure. Each time our visitor visits a particular object from the object structure, that object calls a specific method on a visitor, passing itself as an argument.
    • Each time we need a new operation we create a subclass of a visitor, implement the operation in that class and visit the object structure.
    • Objects themselves only implement an accept visit where the visitor is pass as an argument. Objects know about the method in visitor created specifically for it and invoke that method inside the accept method.

    2. Description

    2.1 Implementation

    • We create visitor interface by defining visit method for each class we want to support
    • The classes who want functionalities provided by visitor define accept method which accepts a visitor. These methods are defined using the visitor interface as parameter type so that we can pass any class implementing the visitor to these methods
    • In the accept method implementation, we'll call a method on visitor which is defined specifically for that class
    • Next, we implement the visitor interface in on or more classes. Each implementation provides specific functionality for interested classes. If want another feature we create a new implementation of the visitor.

    2.2 Consideration

    2.2.1 Implementation

    • Visitor can work with objects of classes which do not have a common parent. So having a common interface for those classes is optional. However, the code which passes our visitor to these objects must be aware of these individual classes.
    • Often visitors need access to an internal state of objects to carry out their work. So we may have to expose the state using getters/setters

    2.2.2 Design

    • One effect of this pattern is that related functionality is grouped in a single visitor class instead of spread across multiple classes. So adding new functionality is as simple as adding new visitor class
    • Visitors can also accumulate state. So along with behavior we can also have state per object in our visitor. We don't have to add new state to objects for behavior defined in visitor
    • Visitor can be used to add new functionality to object structure implemented using composite or can be used for doing interpretation in interpreter design pattern.

    2.3 Pitfalls

    • Often visitors need access to object's state. So we end up exposing a lot of state through getter methods, weakening the encapsulation
    • Supporting a new class in our visitors requires changes to all visitor implementations
    • If the classes themselves change then all visitors have to change as well since they have to work with the changed class
    • A little bit confusing to understand and implement

    3. Usage

    • org.dom4j.Visitor and implementation org.dom4j.VisitorSupport in dom4j library
    • java.nio.file.fileVisitor and its implementation SimpleFileVisitor

    4. Comparison with Strategy

    Visitor Strategy
    All visitor subclasses provide possibly different functionalities from each other In strategy design pattern each subclass represents a separate algorithm to solve the same problem

    5. Example

    interface Employee {
        int getPerformanceRating();
        void setPerformanceRating(int rating);
        Collection<Employee> getDirectReports();
        int getEmployeeId();
    
        void accept(Visitor visitor);
    }
    interface Visitor {
        void visit(Programmer programmer);
        void visit(ProjectLead lead);
        void visit(Manager manager);
        void visit(VicePresident vp);
    }
    abstract class AbstractEmployee implements Employee {
        private int performanceRating;
        private String name;
        private static int employeeIdCounter = 101;
        private int employeeId;
        public AbstractEmployee(String name) {
            this.name = name;
            employeeId = employeeIdCounter++;
        }
        public String getName() {
            return name;
        }
        @Override
        public int getPerformanceRating() {
            return performanceRating;
        }
        @Override
        public void setPerformanceRating(int rating) {
            performanceRating = rating;
        }
        @Override    public Collection<Employee> getDirectReports() {
            return Collections.emptyList();
        }
        @Override
        public int getEmployeeId() {
            return employeeId;
        }
    }
    class PerformanceRating {
        private int id;
        private int personalRating;
        private int teamAverageRating;
        private int finalRating;
        public PerformanceRating(int id, int personalRating) {
            this.id = id;
            this.personalRating = personalRating;
        }
        public int getId() {
            return id;
        }
        public int getPersonalRating() {
            return personalRating;
        }
        public int getTeamAverageRating() {
            return teamAverageRating;
        }
        public int getFinalRating() {
            return finalRating;
        }
        public void setTeamAverageRating(int teamAverageRating) {
            this.teamAverageRating = teamAverageRating;
        }
        public void setFinalRating(int finalRating) {
            this.finalRating = finalRating;
        }
    }
    class PrintVisitor implements Visitor {
        @Override
        public void visit(Programmer programmer) {
            String msg = programmer.getName() + " is a " + programmer.getSkill() + " programmer.";
            System.out.println(msg);
        }
        @Override
        public void visit(ProjectLead lead) {
            String msg = lead.getName() + " is a Project Lead with "
                    + lead.getDirectReports().size() + " programmers reporting.";
            System.out.println(msg);
        }
        @Override
        public void visit(Manager manager) {
            String msg = manager.getName() + " is a Manager with "
                    + manager.getDirectReports().size() + " leads reporting.";
            System.out.println(msg);
        }
        @Override
        public void visit(VicePresident vp) {
            String msg = vp.getName() + " is a Vice President with "
                    + vp.getDirectReports().size() + " managers reporting.";
            System.out.println(msg);
        }
    }
    class Manager extends AbstractEmployee {
        private List<Employee> directReports = new ArrayList<>();
        public Manager(String name,Employee...employees) {
            super(name);
            Arrays.stream(employees).forEach(directReports::add);
        }
        @Override
        public Collection<Employee> getDirectReports() {
            return directReports;
        }
    
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }
    class Programmer extends AbstractEmployee {
        private String skill;
        public Programmer(String name, String skill) {
            super(name);
            this.skill = skill;
        }
        public String getSkill() {
            return skill;
        }
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }
    
    class ProjectLead extends AbstractEmployee {
        private List<Employee> directReports = new ArrayList<>();
        public ProjectLead(String name, Employee...employees) {
            super(name);
            Arrays.stream(employees).forEach(directReports::add);
        }
        @Override
        public Collection<Employee> getDirectReports() {
            return directReports;
        }
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    
    
    }
    class VicePresident extends AbstractEmployee{
        private List<Employee> directReports = new ArrayList<>();
        public VicePresident(String name, Employee...employees) {
            super(name);
            Arrays.stream(employees).forEach(directReports::add);
        }
        @Override
        public Collection<Employee> getDirectReports() {
            return directReports;
        }
        @Override
        public void accept(Visitor visitor) {
            visitor.visit(this);
        }
    }
    class AppraisalVisitor implements Visitor{
    
    	private Ratings ratings = new Ratings();
    
    	@SuppressWarnings("serial")
    	public class Ratings extends HashMap<Integer, PerformanceRating>{
    		public int getFinalRating(int empId) {
    			return get(empId).getFinalRating();
    		}
    	}
    
    	@Override
    	public void visit(Programmer programmer) {
    		PerformanceRating finalRating = new PerformanceRating(programmer.getEmployeeId(), programmer.getPerformanceRating());
    
    		finalRating.setFinalRating(programmer.getPerformanceRating());
    
    		ratings.put(programmer.getEmployeeId(),
    				finalRating);
    
    	}
    
    	@Override
    	public void visit(ProjectLead lead) {
    		//25% team & 75% personal
    		PerformanceRating finalRating = new PerformanceRating(lead.getEmployeeId(), lead.getPerformanceRating());
    
    		int teamAverage = getTeamAverage(lead);
    		int rating = Math.round(0.75f * lead.getPerformanceRating() + 0.25f*teamAverage);
    		finalRating.setFinalRating(rating);
    		finalRating.setTeamAverageRating(teamAverage);
    
    		ratings.put(lead.getEmployeeId(),
    				finalRating);
    
    	}
    
    	@Override
    	public void visit(Manager manager) {
    		//50% team & 50% personal
    		PerformanceRating finalRating = new PerformanceRating(manager.getEmployeeId(), manager.getPerformanceRating());
    
    		int teamAverage = getTeamAverage(manager);
    		int rating = Math.round(0.5f * manager.getPerformanceRating() + 0.5f*teamAverage);
    		finalRating.setFinalRating(rating);
    		finalRating.setTeamAverageRating(teamAverage);
    
    		ratings.put(manager.getEmployeeId(),
    				finalRating);
    	}
    
    	@Override
    	public void visit(VicePresident vp) {
    		//75% team & 25% personal
    		PerformanceRating finalRating = new PerformanceRating(vp.getEmployeeId(), vp.getPerformanceRating());
    
    		int teamAverage = getTeamAverage(vp);
    		int rating = Math.round(0.25f * vp.getPerformanceRating() + 0.75f*teamAverage);
    		finalRating.setFinalRating(rating);
    		finalRating.setTeamAverageRating(teamAverage);
    
    		ratings.put(vp.getEmployeeId(),
    				finalRating);
    
    	}
    
    	private int getTeamAverage(Employee emp) {
    		return (int)Math.round(emp.getDirectReports().stream().mapToDouble(e->e.getPerformanceRating()).average().getAsDouble());
    	}
    
    	public Ratings getFinalRatings() {
    		return ratings;
    	}
    }
    public class Client {
        public static void main(String[] args) {
            Employee emps = buildOrganization();
            Visitor visitor = new PrintVisitor();
            visitOrgStructure(emps, visitor);
    
    //        Visitor visitor1 = new AppraisalVisitor();
    //        visitOrgStructure(emps, visitor1);
        }
        private static Employee buildOrganization() {
    
            Programmer p1 = new Programmer("Rachel","C++");
            Programmer p2 = new Programmer("Andy","Java");
    
            Programmer p3 = new Programmer("Josh","C#");
            Programmer p4 = new Programmer("Bill","C++");
    
            ProjectLead pl1 = new ProjectLead("Tina", p1, p2);
            ProjectLead pl2 = new ProjectLead("Joey", p3, p4);
    
            Manager m1 = new Manager("Chad", pl1);
            Manager m2 = new Manager("Chad II", pl2);
    
            VicePresident vp = new VicePresident("Richard", m1,m2);
    
            return vp;
        }
        private static void visitOrgStructure(Employee emp, Visitor visitor) {
            emp.accept(visitor);
            emp.getDirectReports().forEach(e -> visitOrgStructure(e, visitor));
        }
    }

    6. Reference

    https://en.wikipedia.org/wiki/Visitor_pattern

    https://courseprobe.com/instructors/coffee-powered-crew/

    'Modeling > DesignPattern' 카테고리의 다른 글

    Composite  (0) 2020.02.29
    Chain of responsibility  (0) 2020.02.29
    Iterator  (0) 2020.02.29
    Command  (0) 2020.02.29
    Flyweight  (0) 2020.02.28

    댓글

Designed by Tistory.