业务场景模拟

小孩在睡觉是被观察者

爸爸,妈妈,和宠物是观察者

小孩状态:睡觉 和 刚睡醒之后可能哭闹(cry)

代码版本V1

Github地址

package com.observer.v1;

class Child {
private boolean cry = false;
private Dad dad = new Dad();
private Mum mum = new Mum();
private Dog dog = new Dog();

public boolean isCry() {
return cry;
}

public void wekeup(){
cry = true;
dad.feed();
dog.Wang();
mum.hug();
}
}

class Dad{
public void feed(){
System.out.println("dad feeding ...");
}
}

class Mum{
public void hug(){
System.out.println("mum hugging ...");
}
}

class Dog{
public void Wang(){
System.out.println("dog wang ...");
}
}

public class Main{
public static void main(String[] args) {
Child child = new Child();
child.wekeup();
}
}

代码说明

这个版本代码比较简单,被观察者持有观察者引用,当业代码调用小孩的weakup时触发通知,此时爸爸,妈妈,宠物会作出不同的反应。

缺陷

可见代码耦合度非常高,当新增一个观察者时必须通过修改源码才能实现,比如增加一个叔叔观察小孩,而且观察者的反应也不应该耦合到被观察者身上,比如狗见鸡跳墙也可能会叫

代码改进

  1. 定义Observer接口 里面有个方法叫 action 。抽象出观察者,方便扩展
  2. 被观察者定义一个集合用来存观察者
  3. 以下代码是在代码块里面初始化的观察者,实际上可以放在配置文件里面初始化观察者。这也新增观察者不会再需要修改源码了

代码版本V2

Github地址

package com.observer.v2;

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

interface Observer{
void action();
}
class Child {
private boolean cry = false;
private List<Observer> observers = new ArrayList<>();

{
observers.add(new Dad());
observers.add(new Mum());
observers.add(new Dog());
}

public boolean isCry() {
return cry;
}

public void wekeup(){
cry = true;
observers.forEach(o->{
o.action();
});
}
}

class Dad implements Observer{
public void feed(){
System.out.println("dad feeding ...");
}

@Override
public void action() {
feed();
}
}

class Mum implements Observer{
public void hug(){
System.out.println("mum hugging ...");
}

@Override
public void action() {
hug();
}
}

class Dog implements Observer{
public void wang(){
System.out.println("dog wang ...");
}

@Override
public void action() {
wang();
}
}

public class Main{
public static void main(String[] args) {
Child child = new Child();
child.wekeup();
}
}

新需求

对应观察者(爸爸,妈妈,宠物)来说只能观察小孩,我们现在需求,比如观察厨房里面的汤做好没。观察家里闹钟等等,如此我们需要将被观察者也进行抽象(定义一个Subject接口,被观察者去实现,现在指的是小孩,汤,水)。被观察者会发出不同的事件,比如闹钟响事件,汤做好了事件,小孩醒了事件,水烧开了事件,所以定义事件对象(Event)

代码版本V3

Github地址


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

interface Observer {
/**
* 不同的观察者根据事件类型不同,做不同的处理
* @param event
*/
void action(Event event);
}

interface Subject {
void addObserver(Observer observer);

void deleteObserver(Observer observer);

void notifyObserver();
}

class Child implements Subject {
private boolean cry = false;
private List<Observer> observers = new ArrayList<>();

public boolean isCry() {
return cry;
}

private void wekeup() {
cry = true;
observers.forEach(o -> {
Event e = new Event(System.currentTimeMillis(),"sleep",this);
o.action(e);
});
}

@Override
public void addObserver(Observer observer) {
observers.add(observer);
}

@Override
public void deleteObserver(Observer observer) {
observers.remove(observer);
}

@Override
public void notifyObserver() {
wekeup();
}
}

/**
* 事件对象
*/
class Event {
/**
* 事件发生事件
*/
private long timestamp;
/**
* 事件名称,或者类型,可以定义枚举。咋这里为了掩饰方便
*/
private String name;
/**
* 事件源对象
*/
private Subject source;

public Event(long timestamp, String name, Subject source) {
this.timestamp = timestamp;
this.name = name;
this.source = source;
}

public long getTimestamp() {
return timestamp;
}

public String getName() {
return name;
}

public Subject getSource() {
return source;
}
}

class Dad implements Observer {
private void feed() {
System.out.println("dad feeding ...");
}

@Override
public void action(Event event) {
feed();
}
}

class Mum implements Observer {
private void hug() {
System.out.println("mum hugging ...");
}

@Override
public void action(Event event) {
hug();
}
}

class Dog implements Observer {
private void wang() {
System.out.println("dog wang ...");
}

@Override
public void action(Event event) {
wang();
}
}

public class Main {
public static void main(String[] args) {
Subject subject = new Child();
subject.addObserver(new Dad());
subject.addObserver(new Mum());
subject.addObserver(new Dog());
subject.addObserver((e)->{
System.out.println(e.getName());
System.out.println(e.getTimestamp());
System.out.println(e.getSource());
});
subject.notifyObserver();
}
}

总结

缺点:

  1. 一个观察目标对象有很多直接和间接的观察者,将所有的观察者都通知到会很花时间。

  2. 如果在观察者和观察目标之间存在循环依赖,观察目标会触发他们之间循环调用,可能会死循环。

使用场景:

  1. 一个对象的改变将导致一个或多个其他对象也发生改变,而不知道具体改变了多少。也不知道改变了谁。

  2. 需要在系统中创建一个触发链,A对象的行为将影响B对象,B对象将影响C以此类推。创建触发链条机制。

博主主页

可以加博主微信一起交流:twobixiaoxin