设计模式-观察者模式
博客专区 > 商者 的博客 > 博客详情
设计模式-观察者模式
商者 发表于1年前
设计模式-观察者模式
  • 发表于 1年前
  • 阅读 4
  • 收藏 0
  • 点赞 0
  • 评论 0

腾讯云 十分钟定制你的第一个小程序>>>   

摘要: 设计模式-观察者模式

观察者模式分析

观察者模式又叫做发布-订阅(Publish/Subscribe)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

特点:
观察者模式的通知者可以有任意数目的依赖它的Observer,一旦通知者的状态改变,所有的Observer都可以得到通知。通知者发出通知时并不需要知道谁是它的观察者,也就是说,具体观察者是谁,它根本不需要知道。
总得来讲,观察者模式所做的工作其实就是在接触耦合。让耦合的双方都依赖于抽象,而不是依赖于具体。从而使得各自的变化都不会影响另一边的变化。

那么在观察者模式时,如果观察者之间非常相似,那么可以用抽象类来共用一些代码,但是现实编程中,具体的观察者完全有可能是风马牛不相及的类,但它们都需要根据通知者的通知来做出Update()操作,所以让它们都实现下面一个接口就可以实现这个想法了。

interface Observer {
    void update();
}

下面这个观察者模式,我会一步步以例子进行分析。
下面的观察者以工作中开小差玩游戏的同事为原型,通知者以秘书为原型,一旦老板出现,秘书立即通知玩游戏、睡觉的同事,做好准备。

 

双向耦合代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package observer1;

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

/**
 * Created by benjamin on 1/4/16.
 * 秘书类
 */
public class Secretary {
    private List<PlayGameObserver> observers = new ArrayList<PlayGameObserver>();   //观察者的集合
    private String noticeMessage;   //通知的消息

    public Secretary(){}

    /**
     * 增加观察者
     * @param observer
     */
    public void addObserver(PlayGameObserver observer) {
        observers.add(observer);
    }

    /**
     * 删除观察者
     * @param observer
     */
    public void removeObserver(PlayGameObserver observer) {
        observers.remove(observer);
    }

    /**
     * 通知所有观察者
     */
    public void notifyObserver() {
        for (PlayGameObserver o : observers) {
            o.update();
        }
    }

    public String getNoticeMessage() {
        return noticeMessage;
    }

    public void setNoticeMessage(String noticeMessage) {
        this.noticeMessage = noticeMessage;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package observer1;

/**
 * Created by benjamin on 1/4/16.
 * 玩游戏的观察者
 */
public class PlayGameObserver {

    private String name;    //玩游戏的观察者姓名
    private Secretary sub;  //通知者(秘书)

    public PlayGameObserver(String name, Secretary sub) {
        this.name = name;
        this.sub = sub;
    }

    public void update() {
        System.out.println(sub.getNoticeMessage() + this.name + "关闭游戏,继续工作!");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package observer1;

/**
 * Created by benjamin on 1/4/16.
 */
public class ObserverTest {

    public static void main(String[] args) {
        Secretary secretary = new Secretary();
        PlayGameObserver o1 = new PlayGameObserver("王菲", secretary);
        secretary.addObserver(o1);

        secretary.setNoticeMessage("老板回来了!");
        secretary.notifyObserver();
    }
}

结果输出为:

老板回来了!王菲关闭游戏,继续工作!

上面的类虽然能实现功能,但是仔细一看,他们之间的耦合太大了。秘书类需要增加观察者,而观察者们需要秘书类。如果观察者不止一个人,并且干得事情不一样的时候(比如睡觉、吃东西什么的),就无法让秘书增加对应的观察者,并一起通知。我们来改改代码。

解耦

抽象观察者类(Observer)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package observer1;

/**
 * Created by benjamin on 1/4/16.
 * 观察者
 */
public abstract class Observer {

    protected String name;
    protected Notification notice;

    public Observer(String name, Notification notice) {
        this.name = name;
        this.notice = notice;
    }

    public abstract void update();  //更新抽象类
}

抽象通知者类(Notification)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package observer1;

/**
 * Created by benjamin on 1/4/16.
 */
public abstract class Notification {

    protected String noticeMessage; //通知消息

    public abstract void addObserver(Observer observer);
    public abstract void removeObserver(Observer observer);
    public abstract void notifyObserver();

    public String getNoticeMessage() {
        return noticeMessage;
    }

    public void setNoticeMessage(String noticeMessage) {
        this.noticeMessage = noticeMessage;
    }
}

观察者们

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package observer1;

/**
 * Created by benjamin on 1/4/16.
 * 玩游戏的观察者
 */
public class PlayGameObserver extends Observer {

    public PlayGameObserver(String name, Notification notice) {
        super(name, notice);
    }

    @Override
    public void update() {
        System.out.println(notice.noticeMessage + name + "关闭游戏,继续工作!");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package observer1;

/**
 * Created by benjamin on 1/4/16.
 * 睡觉的观察者
 */
public class SleepObserver extends Observer {

    public SleepObserver(String name, Notification notice) {
        super(name, notice);
    }

    @Override
    public void update() {
        System.out.println(notice.noticeMessage + name + "停止睡觉,继续工作!");
    }
}

通知者们

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package observer1;

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

/**
 * Created by benjamin on 1/4/16.
 * 秘书类
 */
public class Secretary extends Notification {
    private List<Observer> observers = new ArrayList<Observer>(); //观察者集合

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

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

    @Override
    public void notifyObserver() {
        for (Observer o : observers) {
            o.update();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package observer1;

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

/**
 * Created by benjamin on 1/4/16.
 * 老板
 */
public class Boss extends Notification {
    private List<Observer> observers = new ArrayList<Observer>(); //观察者集合

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

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

    @Override
    public void notifyObserver() {
        for (Observer o : observers) {
            o.update();
        }
    }
}

进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package observer1;

/**
 * Created by benjamin on 1/4/16.
 */
public class ObserverTest {

    public static void main(String[] args) {
        // 第一次老板回来了,秘书通知
        Secretary secretary = new Secretary();
        PlayGameObserver o1 = new PlayGameObserver("王菲", secretary);
        SleepObserver o2 = new SleepObserver("谢霆锋", secretary);
        secretary.addObserver(o1);
        secretary.addObserver(o2);

        secretary.setNoticeMessage("老板回来了!");
        secretary.notifyObserver();

        System.out.println("------------------------------------");

        // 第二次老板回来了,找秘书有事,秘书来不及通知,老板看见了,老板来通知
        Boss huhansan = new Boss();
        PlayGameObserver p1 = new PlayGameObserver("张柏芝", huhansan);
        PlayGameObserver p2 = new PlayGameObserver("谢霆锋", huhansan);
        SleepObserver p3 = new SleepObserver("王菲", huhansan);
        huhansan.addObserver(p1);
        huhansan.addObserver(p2);
        huhansan.addObserver(p3);
        huhansan.removeObserver(p3);    //抓到一个

        huhansan.setNoticeMessage("你们不好好工作!");
        huhansan.notifyObserver();

        /**
         * 输出
         * 老板回来了!王菲关闭游戏,继续工作!
           老板回来了!谢霆锋停止睡觉,继续工作!
           ------------------------------------
           你们不好好工作!张柏芝关闭游戏,继续工作!
           你们不好好工作!谢霆锋关闭游戏,继续工作!
         */
    }
}

观察者模式的不足

比如我们在使用Eclipse以及Xcode等一些编辑工具的时候,我们可能一个运行可以导致很多窗口隐藏或者出现,而且各个变化都涉及到不同的控件。那么我们要是想通知它们,不可能让它们都去实现Observer接口,因为这些控件早都已经被他们的制造商给封装了。

尽管我们用了依赖倒转原则,但是‘抽象通知者’还是依赖‘抽象观察者’,也就是说,万一没有了抽象观察者这样的接口,我这通知的功能就完不成了。另外就是每个具体观察者,它不一定要使用‘更新’这个方法,有可能是‘工具箱隐藏’方法,有可能是‘导航栏变色’等。这根本就是不同名的方法,这应该就是不足的地方吧。

运用事件委托delegate机制

委托就是一种引用方法的类型。一旦为委托分配了方法,委托将与该方法具有完全相同的行为。委托方法的使用可以像其他任何方法一样,具有参数和返回值。委托可以看做是对函数的抽象,是函数的“类”,委托的实例将代表一个具体的函数。
一个委托可以搭载多个方法,所有方法被依次唤起。更重要的是,它可以使得委托对象所搭载的方法并不需要属于同一个类。

刚刚的例子我们加上Event和EventHandler作为委托,修改代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package observer1;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * Created by benjamin on 1/4/16.
 */
public class Event {

    private Object object;
    private String methodName;
    private String[] params;
    private Class[] paramTypes;

    public Event(Object object, String methodName, String...params){
        this.object = object;
        this.methodName = methodName;
        this.params = params;
        resolveParams(this.params);
    }

    private void resolveParams(String[] params) {
        int length = params.length;
        paramTypes = new Class[length];
        for (int i = 0; i < length; i ++) {
            paramTypes[i] = params[i].getClass();
        }
    }

    public void invoke() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Method method = object.getClass().getMethod(methodName, paramTypes);
        if (method == null) return;
        method.invoke(object, params);
    }

}
1
2
3
4
5
6
7
8
9
10
package observer1;

/**
 * Created by benjamin on 1/4/16.
 */
public interface Delegate {

    void addEvent(Event e);
    void notifyX() throws Exception;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package observer1;

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

/**
 * Created by benjamin on 1/4/16.
 */
public class EventHandler implements Delegate{
    private List<Event> events = new ArrayList<Event>();

    public EventHandler(){}

    public void addEvent(Event e) {
        events.add(e);
    }

    public void notifyX() throws Exception {
        for (Event e : events) {
            e.invoke();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package observer1;

/**
 * Created by benjamin on 1/4/16.
 */
public abstract class Notification {

    private Delegate delegate ;

    public Notification(Delegate delegate) {
        this.delegate = delegate;
    }

    public abstract void addListener(Object object, String methodName, String...params);
    public abstract void notifyX();

    public Delegate getDelegate() {
        return this.delegate;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package observer1;

/**
 * Created by benjamin on 1/4/16.
 * 秘书类
 */
public class Secretary extends Notification {

    public Secretary(Delegate delegate) {
        super(delegate);
    }

    @Override
    public void addListener(Object object, String methodName, String... params) {
        this.getDelegate().addEvent(new Event(object, methodName, params));
    }

    @Override
    public void notifyX() {
        try {
            this.getDelegate().notifyX();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package observer1;

/**
 * Created by benjamin on 1/4/16.
 * 老板
 */
public class Boss extends Notification {

    public Boss(Delegate delegate) {
        super(delegate);
    }

    @Override
    public void addListener(Object object, String methodName, String... params) {
        this.getDelegate().addEvent(new Event(object, methodName, params));
    }

    @Override
    public void notifyX() {
        try {
            this.getDelegate().notifyX();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package observer1;

/**
 * Created by benjamin on 1/4/16.
 * 玩游戏的观察者
 */
public class PlayGameObserver {

    private String gamer;

    public PlayGameObserver(String name) {
        gamer = name;
    }

    public void stopGame(String noticeMessage) {
        System.out.println(noticeMessage + gamer + "关闭游戏,继续工作!");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package observer1;

/**
 * Created by benjamin on 1/4/16.
 * 睡觉的观察者
 */
public class SleepObserver {

    private String sleeper;

    public SleepObserver(String name) {
        sleeper = name;
    }

    public void stopSleep(String noticeMessage) {
        System.out.println(noticeMessage + sleeper + "停止睡觉,继续工作!");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package observer1;

import sun.jvm.hotspot.runtime.Threads;

/**
 * Created by benjamin on 1/4/16.
 */
public class ObserverTest {

    public static void main(String[] args) {
        // 第一次老板回来了,秘书通知
        Delegate delegate = new EventHandler();
        Secretary secretary = new Secretary(delegate);
        PlayGameObserver o1 = new PlayGameObserver("王菲");
        SleepObserver o2 = new SleepObserver("谢霆锋");
        secretary.addListener(o1, "stopGame", "老板来了!");
        secretary.addListener(o2, "stopSleep", "老板来了!");

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        secretary.notifyX();

        System.out.println("------------------------------------");

        // 第二次老板回来了,找秘书有事,秘书来不及通知,老板看见了,老板来通知
        Delegate delegate1 = new EventHandler();
        Boss huhansan = new Boss(delegate1);
        PlayGameObserver p1 = new PlayGameObserver("张柏芝");
        PlayGameObserver p2 = new PlayGameObserver("谢霆锋");
        SleepObserver p3 = new SleepObserver("王菲");
        huhansan.addListener(p1, "stopGame", "你们不好好工作!");
        huhansan.addListener(p2, "stopGame", "你们不好好工作!");
        huhansan.addListener(p3, "stopSleep", "你们不好好工作!");

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        huhansan.notifyX();

        /**
         * 输出
         * 老板回来了!王菲关闭游戏,继续工作!
           老板回来了!谢霆锋停止睡觉,继续工作!
           ------------------------------------
           你们不好好工作!张柏芝关闭游戏,继续工作!
           你们不好好工作!谢霆锋关闭游戏,继续工作!
           你们不好好工作!王菲停止睡觉,继续工作!
         */
    }
}

最后总结:
1、通知者不知道玩游戏的和睡觉的存在,完全解耦。(功劳归功于Event和EventHandler)
2、老板来后,一次通知,并且通知的消息可以变化,执行不同的方法
3、扩展性很高,再来一个玩纸牌的加上就可以,告诉一下通知者。

 

共有 人打赏支持
商者
粉丝 33
博文 131
码字总数 34228
×
商者
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: