[Original] Do-it-yourself step-by-step realization of the observer mode

introduction

Since the last article " Do it yourself to implement the awesome singleton mode " came out, it has been well received. So the blogger gave up the opportunity to go out and play on May 1st, and rushed to produce this "Do-it-yourself Step-by-Step Implementation of the Observer Mode", and still deduced the final version of the observer mode step by step.

Definition of Observer Pattern

In many designs, often involving multiple objects that are interested in data changes in a particular object, and multiple objects wish to track data changes in that particular object, the Observer pattern can be used.
Here, we take the mother observing the baby as an example, the baby is sleeping, and after waking up, the mother makes the corresponding feeding behavior.

Observer V1

First define a Baby class. The baby sleeps for five seconds and wakes up after five seconds. The code is as follows

package rjzheng.observer1;
public class Baby implements Runnable {
    // 默认是睡着
    private boolean wakeup = false;
    // 醒来的行为
    public void wakeUp() {
        this.wakeup = true;
    }
    public boolean isWakeup() {
        return wakeup;
    }
    public void setWakeup(boolean wakeup) {
        this.wakeup = wakeup;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        wakeUp();
    }
}

The mother class is the Mother class, which always monitors the status of the baby, and feeds the baby as soon as it wakes up. The code is as follows

package rjzheng.observer1;
public class Mother implements Runnable {
    private Baby baby;
    public Mother(Baby baby) {
        this.baby = baby;
    }
    public void feed(Baby baby){
        System.out.println("已经给宝贝喂食");
    }
    @Override
    public void run(){
        while(!baby.isWakeup()){
            for(int i=0;i<5;i++){
                try {
                    Thread.sleep(1000);
                    System.out.println("宝宝还有"+(5-i)+"秒醒来");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        this.feed(baby);
    }
}

Then write an ObserverTest1 to test it, the code is as follows

package rjzheng.observer1;

public class ObserverTest1 {
    public static void main(String[] args) {
        Baby baby =new Baby();
        new Thread(baby).start();
        new Thread(new Mother(baby)).start();
        
    }
}

The output is as follows

宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
已经给宝贝喂食

Observer V2

But in the previous version, the mother had to watch the baby all the time. The mother felt too tired. If the mother wanted to chat on WeChat or something, it was very inconvenient, so the V2 version appeared. Can we take the initiative to tell the mother when the child wakes up? So modify Baby's wakeUp method, that is, after the wake-up state changes, add a method to notify the mother.
In summary, the Baby class makes the following modifications

  1. Hold a reference to Mother, modify the constructor
  2. Add logic in wakeUp method to notify mother
  3. In the run method, you wake up after five seconds and modify your state by yourself
package rjzheng.observer2;
public class Baby implements Runnable {
    // 默认是睡着
    private boolean wakeup = false;
    
    private Mother mother;
    //1. 持有Mother的引用,修改构造函数
    public Baby(Mother mother){
        this.mother = mother;
    }
    
    //2. 在wakeUp方法中增加,通知母亲的逻辑
    public void wakeUp() {
        wakeup = true;
        this.mother.feed(this);
    }
    public boolean isWakeup() {
        return wakeup;
    }
    public void setWakeup(boolean wakeup) {
        this.wakeup = wakeup;
    }
    //3. 在run方法中,自己五秒后醒来,自己修改自己的状态
    @Override
    public void run() {
        while(!this.isWakeup()){
            for(int i=0;i<5;i++){
                try {
                    Thread.sleep(1000);
                    System.out.println("宝宝还有"+(5-i)+"秒醒来");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            this.wakeUp();
        }
    }
}

The Mother class does not need to do thread operations. After the Baby class notifies the Mother class, it can handle it accordingly. Therefore, the Mother class is modified as follows

  1. Delete thread-related operations such as the run method
  2. Add a parameterless constructor (because the Mother class does not need to hold a reference to the Baby class)
    To sum up, the code of the Mother class is as follows
package rjzheng.observer2;
public class Mother{
    private Baby baby;
    public Mother(){
        
    }
    public Mother(Baby baby) {
        this.baby = baby;
    }
    public void feed(Baby baby){
        System.out.println("已经给宝贝喂食");
    }
}

The code of the test class ObserverTest2 is as follows

package rjzheng.observer2;

public class ObserverTest2 {
    public static void main(String[] args) {
        Baby baby =new Baby(new Mother());
        new Thread(baby).start();
        
    }
}

The output looks like this

宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
已经给宝贝喂食

Observer V3

At this time, the baby's father came back. The father feels that after the baby wakes up, he should take the baby out to play. So I made an agreement with the child's mother to decide what to do with the child according to the time when the child wakes up. If you wake up at meal time, feed your child; if it is not at meal time, take your child out to play.
So here comes the point. Whether the time to wake up is a meal time attribute is not suitable for fathers; it is not suitable for babies; it is also not suitable for mothers. According to the principle of object-oriented design, we define a wake-up event class WakeUpEvent, and put whether the wake-up time is meal time in WakeUpEvent. In addition, define an event source source property, then the WakeUpEvent class is as follows

package rjzheng.observer3;
/**
 * 醒来的事件对象
 * @author zhengrongjun
 *
 */
public class WakeUpEvent {
    //醒来时间是否为饭点
    private boolean isFoodTime;
    //事件源
    private Baby source;
    public WakeUpEvent(boolean isFoodTime,Baby source){
        this.isFoodTime = isFoodTime;
        this.source = source;
    }
    public boolean isFoodTime() {
        return isFoodTime;
    }
    public void setFoodTime(boolean isFoodTime) {
        this.isFoodTime = isFoodTime;
    }
    public Baby getSource() {
        return source;
    }
    public void setSource(Baby source) {
        this.source = source;
    }
}

So, next, the Father class has a method to take the baby out to play as shown below

package rjzheng.observer3;

public class Father {
    
    public void play(WakeUpEvent wakeUpEvent){
        if(!wakeUpEvent.isFoodTime()){
            System.out.println("抱宝贝出去玩");
        }
    }
}

Naturally, the Mother class has a method to feed the baby as shown below

package rjzheng.observer3;
public class Mother{
    public void feed(WakeUpEvent wakeUpEvent){
        if(wakeUpEvent.isFoodTime()){
            System.out.println("给宝贝喂食");
        }
    }
}

Then in this case the Baby class is modified as follows

package rjzheng.observer3;

public class Baby implements Runnable {

    private Mother mother;
    private Father father;

    public Baby(Mother mother,Father father) {
        this.mother = mother;
        this.father = father;
    }

    public void wakeUp() {
        this.mother.feed(new WakeUpEvent(true, this));
        this.father.play(new WakeUpEvent(true, this));
    }

    @Override
    public void run() {
        boolean flag = true;
        while (flag) {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println("宝宝还有" + (5 - i) + "秒醒来");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            this.wakeUp();
            flag=false;
        }
    }
}

The test class ObserverTest3 looks like this

package rjzheng.observer3;

public class ObserverTest3 {
    public static void main(String[] args) {
        Baby baby =new Baby(new Mother(),new Father());
        new Thread(baby).start();
        
    }
}

The running result is as follows

宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
给宝贝喂食

Observer V4

After a few days, the child was not happy. . . . The child thinks that it is too troublesome for me to remember what my father can do with me and what my mother can do with me every time. Later, when grandparents come, do I have to remember what you can do, and then notify the corresponding people. Why don't you do it like this, I'll call you all here, you can discuss it yourself, why should you take me?

OK. . This is also the disadvantage of the Observer V3 version, and the code reusability is too poor. If there is one more grandfather class, then there is one more reference to the Grandfather class in the Baby class, and adding notification logic in wakeup is too troublesome. Simply, define a WakeUpListener interface, and those who inherit this interface have the ability to handle WakeUpEvent events.

Then the source code of the WakeUpListener interface is as follows

package rjzheng.observer4;

public interface WakeUpListener {
    public void actiontoWakenUp(WakeUpEvent wakeUpEvent);
}

At this time, the code of the Father class and the Mother class is as follows, both of which implement the WakeUpListener interface

package rjzheng.observer4;

public class Father implements WakeUpListener {

    @Override
    public void actiontoWakenUp(WakeUpEvent wakeUpEvent) {
        // TODO Auto-generated method stub
        if(!wakeUpEvent.isFoodTime()){
            System.out.println("抱宝贝出去玩");
        } 
    }
}
package rjzheng.observer4;
public class Mother implements WakeUpListener{

    @Override
    public void actiontoWakenUp(WakeUpEvent wakeUpEvent) {
        if(wakeUpEvent.isFoodTime()){
            System.out.println("给宝贝喂食");
        }
    }
}

The Baby class at this time is as follows

package rjzheng.observer4;

import java.util.ArrayList;
import java.util.List;

public class Baby implements Runnable {

    private List<WakeUpListener> wakeUpListeners = new ArrayList(); 

    public void addListeners(WakeUpListener wakeUpListener){
        this.wakeUpListeners.add(wakeUpListener);
    }

    public void wakeUp() {
        for(WakeUpListener listener : wakeUpListeners)
            listener.actiontoWakenUp(new WakeUpEvent(true, this));
    }

    @Override
    public void run() {
        boolean flag = true;
        while (flag) {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                    System.out.println("宝宝还有" + (5 - i) + "秒醒来");
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            this.wakeUp();
            flag=false;
        }
    }
}

The test class ObserverTest4 is

package rjzheng.observer4;

public class ObserverTest4 {
    public static void main(String[] args) {
        Baby baby =new Baby();
        baby.addListeners(new Father());
        baby.addListeners(new Mother());
        new Thread(baby).start();
    }
}

The final result is as follows

宝宝还有5秒醒来
宝宝还有4秒醒来
宝宝还有3秒醒来
宝宝还有2秒醒来
宝宝还有1秒醒来
给宝贝喂食

Summarize

This article gives four observer modes, generally using the V4 version. . The previous V1, V2, and V3 are just foreshadowing. In order to lead to the last version of the observer mode, I hope everyone can gain something.

Guess you like

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