Detailed explanation and application of observer mode and visitor mode of design mode


1. Detailed Explanation of Visitor Mode

1.1 Definition of visitor pattern

definition:

The visitor pattern [visitor Pattern] is a design pattern that separates data structure from data operation. Refers to

Encapsulates operations that operate on elements of a data structure.

feature:

New operations on these elements can be defined without changing the data structure.

Belongs to the behavioral model.

illustrate:

The visitor pattern is known as the most complex design pattern. Not used much.

1.1.1 The embodiment of visitor mode in daily life

1. Personnel participating in the KPI assessment

The KPI assessment standard is generally fixed, but the employees participating in the KPI assessment will change frequently.

The people who score the KPI assessment will also change frequently.

2. Restaurant staff

When eating in a restaurant, the menu of the restaurant is basically stable, and the diners basically change every day. A diner is a visitor.

Summarize:

The visitor seems to be a changing element, and the compositional relationship with the unchanging structure [standard, rule] handles the role.

1.1.2 Applicable scenarios of visitor mode

The visitor mode is rarely used, and once it needs to be used, the systems involved are often more complicated.

1. The data structure is stable, and the operation of the data structure is often changed.

2. Scenarios that require the separation of data structures and data operations.

3. It is necessary to operate on different data types (elements) without using branches to judge specific types of scenarios.

1.2 General implementation of visitor pattern

1.3 KPI Assessment of Use Cases of Visitor Mode

1.3.1 Class diagram design

img

1.3.2 Code implementation

1. Element top-level interface definition

package com.oldlu.visitor.demo.kpi;

import java.util.Random;

/**
 * @ClassName Employee
 * @Description 员工,元素抽象
 * @Author oldlu
 * @Date 2020/6/24 10:38
 * @Version 1.0
 */
public abstract class Employee {
    
    
    private String name;
    private int kpi;

    public Employee(String name) {
    
    
        this.name = name;
        this.kpi = new Random().nextInt(10);
    }

    public abstract void accept(IVisitor visitor);

    public String getName() {
    
    
        return name;
    }

    public int getKpi() {
    
    
        return kpi;
    }
}

2. Element specific realization

package com.oldlu.visitor.demo.kpi;

import java.util.Random;

/**
 * @ClassName Engineer
 * @Description 普通开发人员
 * @Author oldlu
 * @Date 2020/6/24 10:43
 * @Version 1.0
 */
public class Engineer extends Employee{
    
    
    public Engineer(String name) {
    
    
        super(name);
    }

    @Override
    public void accept(IVisitor visitor) {
    
    
        visitor.visit(this);
    }
    //考核:代码量
    public int getCodingLine(){
    
    
        return new Random().nextInt(100000);
    }

}
package com.oldlu.visitor.demo.kpi;

import java.util.Random;

/**
 * @ClassName Manager
 * @Description 项目经理
 * @Author oldlu
 * @Date 2020/6/24 10:44
 * @Version 1.0
 */
public class Manager extends Employee {
    
    
    public Manager(String name) {
    
    
        super(name);
    }

    @Override
    public void accept(IVisitor visitor) {
    
    
        visitor.visit(this);
    }
    //考核:每年的新产品研发数量
    public int getProducts(){
    
    
        return new Random().nextInt(10);
    }
}

3. Visitor top-level interface and implementation

package com.oldlu.visitor.demo.kpi;

/**
 * @ClassName IVisitor
 * @Description 访问者接口
 * @Author oldlu
 * @Date 2020/6/24 10:41
 * @Version 1.0
 */
public interface IVisitor {
    
    
    //传参具体的元素
    void visit(Engineer engineer);

    void visit(Manager manager);
}
package com.oldlu.visitor.demo.kpi;

/**
 * @ClassName CTOVisitor
 * @Description ceo考核者,只有看kpi打分就行
 * @Author oldlu
 * @Date 2020/6/24 10:49
 * @Version 1.0
 */
public class CEOVisitor implements IVisitor{
    
    

    @Override
    public void visit(Engineer engineer) {
    
    
        System.out.println("工程师:"+engineer.getName()+" ,KPI:"+engineer.getKpi());
    }

    @Override
    public void visit(Manager manager) {
    
    
        System.out.println("项目经理:"+manager.getName()+" ,KPI:"+manager.getKpi());
    }
}
package com.oldlu.visitor.demo.kpi;

/**
 * @ClassName CTOVisitor
 * @Description cto考核者
 * @Author oldlu
 * @Date 2020/6/24 10:49
 * @Version 1.0
 */
public class CTOVisitor implements IVisitor{
    
    

    @Override
    public void visit(Engineer engineer) {
    
    
        System.out.println("工程师:"+engineer.getName()+" ,编写代码行数:"+engineer.getCodingLine());
    }

    @Override
    public void visit(Manager manager) {
    
    
        System.out.println("项目经理:"+manager.getName()+" ,产品数量:"+manager.getProducts());
    }
}

4. Data structure definition

package com.oldlu.visitor.demo.kpi;

import java.util.LinkedList;
import java.util.List;

/**
 * @ClassName BusinessReport
 * @Description 业务报表,数据结构
 * @Author oldlu
 * @Date 2020/6/24 10:55
 * @Version 1.0
 */
public class BusinessReport {
    
    
    private List<Employee> employeeList = new LinkedList<>();

    public BusinessReport() {
    
    
        employeeList.add(new Manager("项目经理A"));
        employeeList.add(new Manager("项目经理B"));
        employeeList.add(new Engineer("程序员A"));
        employeeList.add(new Engineer("程序员B"));
        employeeList.add(new Engineer("程序员C"));
    }

    public void showReport(IVisitor visitor){
    
    
        for (Employee employee : employeeList) {
    
    
            employee.accept(visitor);
        }
    }
}

5. Test code

package com.oldlu.visitor.demo.kpi;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author oldlu
 * @Date 2020/6/24 10:54
 * @Version 1.0
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        BusinessReport report = new BusinessReport();
        System.out.println("===========CEO看报表===============");
        report.showReport(new CEOVisitor());
        System.out.println("===========CTO看报表===============");
        report.showReport(new CTOVisitor());
    }
}

Test Results:

img

illustrate:

When the top-level interface of the visitor is defined, the visit overload method is defined internally, and the subclasses are overloaded for different visit element implementations.

Why not design it as a method?

Because here is a method to associate specific elements.

When the system needs to add an element implementation subclass, only one implementation subclass needs to be added, and an overloaded method is added to the interface.

The system is easy to expand.

1.4 Visitor pattern extension - dispatch

Static dispatch and dynamic dispatch in java. There is also double dispatch.

Dispatching in Java is a special form of method overloading. That is, the overloaded method has the same method name, the same number of parameters, and different types of forms.

1.4.1 Static dispatch sample code in java

package com.oldlu.visitor.dispatch;

/**
 * @ClassName Main
 * @Description 测试静态分派
 * @Author oldlu
 * @Date 2020/6/24 11:20
 * @Version 1.0
 */
public class Main {
    
    
    public static void main(String[] args) {
    
    
        String str = "1";
        Integer integer = 1;
        Main main = new Main();
        main.test(integer);
        main.test(str);
    }
    public void test(String str){
    
    
        System.out.println("String "+str);
    }
    public void test(Integer integer){
    
    
        System.out.println("Integer "+integer);
    }
}

illustrate:

In the above test code, there are two overloaded methods in the test method, with the same number of parameters and different types.

In the compilation stage, the parameter type can be clearly known, which is called static assignment.

The same method name, different methods of different types, this form is also called multiple dispatch.

1.4.2 Dynamic dispatch in java

In the program compilation stage, it is not clear which type it is, only at runtime can we know which type it is.

Called dynamic dispatch.

1. Define interface and implementation

package com.oldlu.visitor.dispatch.dynamic;

/**
 * @ClassName Person
 * @Description 接口定义
 * @Author oldlu
 * @Date 2020/6/24 11:33
 * @Version 1.0
 */
public interface Person {
    
    
    void test();
}
package com.oldlu.visitor.dispatch.dynamic;

/**
 * @ClassName Women
 * @Description 女人
 * @Author oldlu
 * @Date 2020/6/24 11:35
 * @Version 1.0
 */
public class Women implements Person{
    
    
    @Override
    public void test() {
    
    
        System.out.println("女人");
    }
}
package com.oldlu.visitor.dispatch.dynamic;

/**
 * @ClassName Man
 * @Description 男人
 * @Author oldlu
 * @Date 2020/6/24 11:34
 * @Version 1.0
 */
public class Man implements Person {
    
    
    @Override
    public void test() {
    
    
        System.out.println("男人");
    }
}

2. Test class

package com.oldlu.visitor.dispatch.dynamic;

/**
 * @ClassName Main
 * @Description 测试类
 * @Author oldlu
 * @Date 2020/6/24 11:35
 * @Version 1.0
 */
public class Main {
    
    
    public static void main(String[] args) {
    
    
        Person man = new Man();
        Person women = new Women();
        man.test();
        women.test();
    }
}

illustrate:

When at compile time, man or women don't know what type they are, only at run time,

The specific type can only be known when an instance is created through new, so it is a dynamic allocation.

1.4.3 Pseudo-dynamic double dispatch in the visitor pattern

In the data structure, the collection elements are generally traversed. As follows:

img

It can be seen that this is a dynamic assignment, and the accept method is called. The specific type cannot be determined until runtime.

Moreover, Employee is also an abstract interface, and the type cannot be determined.

img

When entering a subclass, such as Engineer, the accept method calls the visit method and passes the parameter this, which is also dynamically dispatched. need to

The type can only be determined at runtime. [Because this instance needs to be created at runtime]

img

1.5 The visitor mode is applied in the source code

1.5.1 jdk中FileVisitor

FileVisitResult visitFile(T file, BasicFileAttributes attrs)
    throws IOException;

The visitFile method is defined in the FileVisitor interface. Passing parameters to BasicFileAttributes is also an interface.

1.5.2 spring中BeanDefinitionVisitor

public void visitBeanDefinition(BeanDefinition beanDefinition) {
    
    
        visitParentName(beanDefinition);
        visitBeanClassName(beanDefinition);
        visitFactoryBeanName(beanDefinition);
        visitFactoryMethodName(beanDefinition);
        visitScope(beanDefinition);
        if (beanDefinition.hasPropertyValues()) {
    
    
            visitPropertyValues(beanDefinition.getPropertyValues());
        }
        if (beanDefinition.hasConstructorArgumentValues()) {
    
    
            ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
            visitIndexedArgumentValues(cas.getIndexedArgumentValues());
            visitGenericArgumentValues(cas.getGenericArgumentValues());
        }
    }

When accessing, the content in it is not changed, but the corresponding result is returned. Separate data manipulation from structure.

1.6 Summary of the use of visitor mode

1.6.1 Summary of advantages and disadvantages

advantage:

1. Decouple the data structure and data operation, and use the operation set to change independently

2. Good scalability: different operations on data sets can be realized by extending the role of visitors

3. The specific type of element is not single, and all visitors can operate

4. Separation of responsibilities for each role, in line with the principle of single responsibility.

shortcoming:

1. Unable to add element types: If the system data structure objects are easy to change, new data objects are often added,

Then the visitor class must increase the operation of the corresponding element, which violates the principle of opening and closing.

2. It is difficult to change specific elements: adding attributes to specific elements, deleting attributes and other operations will lead to the need for corresponding visitor classes

The corresponding modification, especially when there are a large number of visitor classes, the scope of modification is too large.

3. Violation of the dependency inversion principle: In order to achieve "discriminatory treatment", visitors rely on specific element types, not abstractions.

2 Detailed explanation of the observer mode

2.1 Definition of Observer Pattern

definition:

Observer mode [Observer Pattern], also known as publish-subscribe [Publish/Subscribe] mode, model-view [Model/View] mode,

Source listener [Source/Listener] mode, slave [Dependents] mode.

Define a one-to-many relationship, a subject object can be monitored by multiple observer objects at the same time, so that whenever the state of the subject object changes, all

Objects that depend on it will be notified and updated automatically.

Belongs to the behavioral model.

2.1.1 Application of Observer Pattern in Life Scenes

1. App corner notification

2. Wake up alarm clock setting

2.1.2 Applicable scenarios of observer mode

1. When an abstract model contains two aspects, one of which depends on the other.

2. A change in one or more other objects depends on a change in another object.

3. Realize the function similar to the broadcast mechanism, no need to know the specific listener, just distribute the broadcast, interested in the system

objects automatically receive the broadcast.

4. Multi-layer nesting is used to form a chain trigger mechanism, which enables events to be notified across domains (across two observer types).

2.2 Questions and Answers Prompt Corner Mark for Observer Mode Application Cases

In the learning community, when we have questions, we can post questions for help. When posting a question, you can invite someone [or a teacher] to

answer.

However, the teacher is usually very busy and does not always refresh the page. Therefore, a notification function will be made.

Once there is a question, put it to the teacher, and the number on the notification icon will increase by 1.

When the teacher logs in to the page, he only needs to check the notification corner to know whether someone asks him a question, which is convenient for answering.

2.2.1 Class diagram design

img

2.2.2 Code implementation

Description: Here is a publish-subscribe api implementation based on jdk.

1. Definition of the Observed

package com.oldlu.observer.demo.gper;

import java.util.Observable;

/**
 * @ClassName GPer
 * @Description 社区生态圈,被观察者
 * @Author oldlu
 * @Date 2020/6/24 17:31
 * @Version 1.0
 */
public class GPer extends Observable {
    
    
    private String name = "GPer 生态圈";

    public String getName() {
    
    
        return name;
    }

    private static final GPer gper = new GPer();

    private GPer() {
    
    
    }

    public static GPer getInstance(){
    
    
        return gper;
    }

    public void publishQuestion(Question question){
    
    
        System.out.println(question.getUserName()+" 在" +this.name +"提交了一个问题");
        //调用jdk api
        setChanged();
        notifyObservers(question);
    }
}

2. Data structure, problem class

package com.oldlu.observer.demo.gper;

/**
 * @ClassName Question
 * @Description 问题
 * @Author oldlu
 * @Date 2020/6/24 17:34
 * @Version 1.0
 */
public class Question {
    
    
    //问题发布者
    private String userName;
    //内容
    private String content;

    public void setUserName(String userName) {
    
    
        this.userName = userName;
    }

    public void setContent(String content) {
    
    
        this.content = content;
    }

    public String getUserName() {
    
    
        return userName;
    }

    public String getContent() {
    
    
        return content;
    }
}

3. Observer definition

package com.oldlu.observer.demo.gper;

import java.util.Observable;
import java.util.Observer;

/**
 * @ClassName Teacher
 * @Description 观察者
 * @Author oldlu
 * @Date 2020/6/24 17:38
 * @Version 1.0
 */
public class Teacher implements Observer {
    
    
    private String name;

    public Teacher(String name) {
    
    
        this.name = name;
    }

    @Override
    public void update(Observable ob, Object arg) {
    
    
        GPer gper = (GPer) ob;
        Question question = (Question) arg;
        System.out.println("===================");
        System.out.println(name+"老师,你好\n" +
                ",您收到一个来自"+gper.getName()+"的提问,希望你解答,问题内容如下:\n"+question.getContent()+
                "\n提问者:"+question.getUserName());
    }
}

4. Test class

package com.oldlu.observer.demo.gper;

import javax.management.Query;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author oldlu
 * @Date 2020/6/24 17:44
 * @Version 1.0
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        GPer gper = GPer.getInstance();
        Teacher tom = new Teacher("tom");
        Teacher jerry = new Teacher("Jerry");

        gper.addObserver(tom);
        gper.addObserver(jerry);

        //用户行为
        Question question = new Question();
        question.setUserName("张三");
        question.setContent("观察者模式适用于哪些场景?");
        gper.publishQuestion(question);
    }
}

2.3 Google open source components implement observer mode

Dependency package:

<dependency>
      <groupId>com.google.guava</groupId>
      <artifactId>guava</artifactId>
      <version>18.0</version>
    </dependency>

2.3.1 Sample code

1. Observer class

package com.oldlu.observer.guava;

import com.google.common.eventbus.Subscribe;

/**
 * @ClassName GuavaEvent
 * @Description 观察者
 * @Author oldlu
 * @Date 2020/6/24 18:07
 * @Version 1.0
 */
public class GuavaEvent {
    
    
    //表示观察者回调
    @Subscribe
    public void observer(String str){
    
    
        System.out.println("执行observer方法,传参为:"+str);
    }
}

2. Test class

package com.oldlu.observer.guava;

import com.google.common.eventbus.EventBus;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author oldlu
 * @Date 2020/6/24 18:09
 * @Version 1.0
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        EventBus eventBus = new EventBus();
        GuavaEvent event = new GuavaEvent();
        eventBus.register(event);

        eventBus.post("tom");
    }
}

Test Results:img

2.4 Mouse Event Interaction in Observer Mode Application Cases

When the mouse initiates an action [such as: click, move, scroll. . . ] to get the corresponding response.

In order to issue an action, it needs to be monitored [in reality, the operating system monitors]

Mouse: implemented as an observer.

Event listener: listen for actions [trigger an event]

Business class: event class [pass parameters, distinguish between different events or actions]

Event callback: After listening, you need to respond and make a callback. act as an observer.

2.4.1 Class diagram design

img

2.4.2 Code implementation

1. Event listener

package com.oldlu.observer.mouseclick.core;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * @ClassName EventListener
 * @Description 事件监听器,被观察者的抽象
 * @Author oldlu
 * @Date 2020/6/24 18:21
 * @Version 1.0
 */
public class EventListener {
    
    
    protected Map<String, Event> events = new HashMap<String,Event>();

    public void addListener(String eventType, Object target, Method callback){
    
    
        events.put(eventType,new Event(target,callback));
    }
    public void addListener(String eventType, Object target){
    
    
        try{
    
    
            this.addListener(eventType,target,target.getClass().getMethod("on"+ toUpperFirstCase(eventType),Event.class));

        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }

    private String toUpperFirstCase(String eventType) {
    
    
        char [] chars = eventType.toCharArray();
        if(chars[0]> 'a' && chars[0] < 'z'){
    
    
            chars[0] -= 32;
        }
        return String.valueOf(chars);
    }

    private void trigger(Event event){
    
    
        event.setSource(this);
        event.setTime(System.currentTimeMillis());
        try{
    
    
            if(null != event.getCallback()){
    
    
                //反射调用 回调函数
                event.getCallback().invoke(event.getTarget(),event);
            }
        }catch (Exception e){
    
    
            e.printStackTrace();
        }
    }

    protected void trigger(String trigger){
    
    
        if(!this.events.containsKey(trigger)){
    
    return;}
        //如果已进行注册,回调
        trigger(this.events.get(trigger).setTrigger(trigger));

    }
}

2. Monitored object, mouse class

package com.oldlu.observer.mouseclick.event;

import com.oldlu.observer.mouseclick.core.EventListener;

/**
 * @ClassName Mouse
 * @Description 具体的被观察者
 * @Author oldlu
 * @Date 2020/6/24 18:21
 * @Version 1.0
 */
public class Mouse extends EventListener {
    
    
    public void click() {
    
    
        System.out.println("调用单机方法");
        this.trigger(MouseEventType.ON_CLICK);
    }
}

3. Event callback

package com.oldlu.observer.mouseclick.event;

import com.oldlu.observer.mouseclick.core.Event;

/**
 * @ClassName MouseCallback
 * @Description 事件响应,回调,观察者
 * @Author oldlu
 * @Date 2020/6/24 18:22
 * @Version 1.0
 */
public class MouseEventCallback {
    
    
    public void onClick(Event event){
    
    
        System.out.println("=============触发鼠标单击事件========\n"+event);
    }

    public void onMove(Event event){
    
    
        System.out.println("触发鼠标双击事件");
    }

}

4. Pass parameter bean, event class

package com.oldlu.observer.mouseclick.core;

import java.lang.reflect.Method;

/**
 * @ClassName Event
 * @Description 事件抽象,传参对象
 * @Author oldlu
 * @Date 2020/6/24 18:22
 * @Version 1.0
 */
public class Event {
    
    
    //事件源,如:鼠标,键盘
    private Object source;
    //事件触发,要通知谁(观察者)
    private Object target;

    private Method callback;
    //事件名称
    private String trigger;
    //事件触发时间
    private Long time;

    public Event(Object target, Method callback) {
    
    
        this.target = target;
        this.callback = callback;
    }

    public Object getSource() {
    
    
        return source;
    }

    public Event setSource(Object source) {
    
    
        this.source = source;
        return this;
    }

    public Object getTarget() {
    
    
        return target;
    }

    public Event setTarget(Object target) {
    
    
        this.target = target;
        return this;
    }

    public Method getCallback() {
    
    
        return callback;
    }

    public Event setCallback(Method callback) {
    
    
        this.callback = callback;
        return this;
    }

    public String getTrigger() {
    
    
        return trigger;
    }

    public Event setTrigger(String trigger) {
    
    
        this.trigger = trigger;
        return this;
    }

    public Long getTime() {
    
    
        return time;
    }

    public Event setTime(Long time) {
    
    
        this.time = time;
        return this;
    }


    @Override
    public String toString() {
    
    
        return "Event{" +
                "source=" + source +
                ", target=" + target +
                ", callback=" + callback +
                ", trigger='" + trigger + '\'' +
                ", time=" + time +
                '}';
    }
}

5. Event type constant definition

package com.oldlu.observer.mouseclick.event;

/**
 * @ClassName MouseEventType
 * @Description 鼠标事件
 * @Author oldlu
 * @Date 2023/2/19 10:47
 * @Version 1.0
 */
public interface MouseEventType {
    
    
    String ON_CLICK = "click";
}

6. Test class

package com.oldlu.observer.mouseclick;

import com.oldlu.observer.mouseclick.event.Mouse;
import com.oldlu.observer.mouseclick.event.MouseEventCallback;
import com.oldlu.observer.mouseclick.event.MouseEventType;

/**
 * @ClassName Test
 * @Description 测试类
 * @Author oldlu
 * @Date 2023/2/19 11:15
 * @Version 1.0
 */
public class Test {
    
    
    public static void main(String[] args) {
    
    
        MouseEventCallback callback = new MouseEventCallback();
        Mouse mouse = new Mouse();
        mouse.addListener(MouseEventType.ON_CLICK,callback);

        mouse.click();
    }
}

The test results are as follows:

img

2.5 Application of observer mode in source code

2.5.1 jdk中ServletContextListener

In fact, the typical prompt of the observer mode, *Listener [listener]

public interface ServletContextListener extends EventListener {
    
    

    public void contextInitialized ( ServletContextEvent sce );
    public void contextDestroyed ( ServletContextEvent sce );
}

2.5.2 spring中ContextLoaderListener

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    
    
    public ContextLoaderListener() {
    
    
    }

    public ContextLoaderListener(WebApplicationContext context) {
    
    
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
    
    
        this.initWebApplicationContext(event.getServletContext());
    }

    public void contextDestroyed(ServletContextEvent event) {
    
    
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}

2.6 Summary of the use of observers

2.6.1 Summary of Advantages

1. The observer and the observed are loosely coupled, in line with the principle of dependency inversion.

2. Separate the presentation layer [observer] and the data logic layer [observed], and establish a trigger mechanism so that data changes can be

Responses to multiple presentation layers.

3. Realize a one-to-many communication mechanism, support event registration mechanism, support interest distribution mechanism, when the observer triggers an event, only interested

Observers of can receive notifications.

2.6.2 Summary of disadvantages

1. If there are too many observers, the event notification will take a long time.

2. The event notification has a linear relationship. If one of the observers gets stuck processing the event, it will affect subsequent observers to receive the event.

3. If there is a circular dependency between the observer and the observed, it may cause a circular call between the two, resulting in a system crash.

Guess you like

Origin blog.csdn.net/ZGL_cyy/article/details/129109669