前言

在上一个博客中我们介绍了Strategy模式,它是行为型模式麾下的一员大将。那么本博客我们来学习一下行为型模式麾下的另一员大将Observer模式。

思考题

老套路,先来思考下面的问题:

问题:思考报纸订阅的问题,有一个体育报社每天都会有新的体育报道,关注体育新闻的用户会获取每天的报道

首先,我们还是来看一看不使用设计模式的代码:

SportNews.java:

public class SportNews {
private String context;
//用户获取新闻
public String getContext() {
return context;
}
//产生新的新闻
public void newContext(String context) {
this.context = context;
}
}

People.java:

public class People {
private final String name;
private SportNews sportNews;
public People(String name, SportNews sportNews) {
this.sportNews = sportNews;
this.name = name;
}
//读新闻
public void readNews() {
System.out.println(String.format("I am %s,I am looking %s", name, sportNews.getContext()));
}
}

Robot.java:

public class Robot {
private static int num = 0;
private final int id = num++;
private SportNews sportNews; public Robot(SportNews sportNews) {
this.sportNews = sportNews;
} public void readNews() {
System.out.println(String.format("Robot:%s,Context:%s", id, sportNews.getContext()));
}
}

测试程序:

TestMain.java:

public class TestMain {
public static void main(String... ars) {
SportNews sportNews = new SportNews();
People xiaoLi = new People("xiao li", sportNews);
People xiaoMin = new People("xiao min", sportNews);
Robot robot = new Robot(sportNews); sportNews.newContext("H:F 1:1");
xiaoLi.readNews();
xiaoMin.readNews();
robot.readNews();
sportNews.newContext("湖人:火箭 102:103");
xiaoLi.readNews();
xiaoMin.readNews();
robot.readNews();
}
}

print:

I am xiao li,I am looking H:F 1:1
I am xiao min,I am looking H:F 1:1
Robot:0,Context:H:F 1:1
I am xiao li,I am looking 湖人:火箭 102:103
I am xiao min,I am looking 湖人:火箭 102:103
Robot:0,Context:湖人:火箭 102:103

上面的代码展示了xiaoli、xiaomin、Robot是怎样获取新的新闻内容的,每个人都需要调用 readNews 才可以获取新的新闻内容。如果再增加一个人,那么这个人也需要调用相应的方法才可以获取。这就相当于每个人都知道报社的地址,每天都要去报社自己拿新的报纸。那么怎样才能改变这样的关系,让人不需要去报社拿新的报纸,那就是使用观察这模式,下面就让我们来具体的介绍这个模式。

介绍Observser

  • 定义: 定义对象之间一对多的关系,当一个对象的状态发生改变时,该对象所有依赖对象都会得到通知。
  • 类图:

从上面的类图中可以总结一下几点:

  1. 抽象主题类(Observable):持有一个 Observer 的集合List,通过 registerObserver 方法可以将 Observer 注册到集合中。通过 notifyServers 方法可以让所有在集合里面的 Observer 都获得通知
  2. 抽象观察者类(Observer):持有一个 Observable ,其目的是为了在 Observable 的集合中取消订阅。而注册行为是发生在构造函数中
  3. 具体主题类(ConcreteObservable):继承了 Observable ,并且实现了父类的抽象方法 changeState ,在该方法中调用了父类的方法 notifyObservers

  4. 具体观察者类(ConcreteObserver):继承了 Observer ,并且实现了父类的方法 update 方法,该方法是具体观察者要做的动作

重构思考题

通过上面的学习,我们已经基本上了解了观察者模式了,下面我们针对上面提到的问题,使用观察者模式来实现它:

首先,我们定义一个抽象主题类:

Subject.java:

import java.util.List;
import java.util.ArrayList; public abstract class Subject {
protected List<Observer> observers; public Subject() {
observers = new ArrayList<Observer>();
} public boolean registerObserver(Observer observer) {
return observers.add(observer);
} public boolean removeObserver(Observer observer) {
return observers.remove(observer);
} public void notifyObservers(String context) {
for(Observer observer: observers) {
observer.update(context);
}
} public abstract void newContext(String context);
}

然后,我们来实现抽象观察者:

Observer.java:

public abstract class Observer {
private Subject subject;
public Observer(Subject subject) {
this.subject = subject;
subject.registerObserver(this);
} public abstract void update(String context);
}

之后,实现一个具体主题类:

SportNews.java:

public class SportNews extends Subject {
@Override
public void newContext(String context) {
notifyObservers(String.format("sport news:%s", context));
}
}

最后,实现具体观察者类:

People.java:

public class People extends Observer {
private final String name; public People(String name, Subject subject) {
super(subject);
this.name = name;
} @Override
public void update(String context) {
System.out.println(String.format("I am %s,I am looking %s", name, context));
}
}

Robot.java:

public class Robot extends Observer {
private static int num = 0;
private final int id = num++; public Robot(Subject subject) {
super(subject);
} @Override
public void update(String context) {
System.out.println(String.format("Robot:%s,Context:%s", id, context));
}
}

测试用例:

TestMain.java:

public class TestMain {
public static void main(String... ars) {
Subject subject = new SportNews();
People xiaoLi = new People("xiao li", subject);
People xiaoMin = new People("xiao min", subject);
Robot robot = new Robot(subject); subject.newContext("H:F 1:1");
subject.newContext("湖人:火箭 102:103");
}
}

以上就是观察者模式的全部代码,通过使用观察者模式,可以看出我们只需要将观察者注册到主题中。不需要调用观察者的任何方法,就可以自动的获得通知。极大的减少了代码量和维护成本。这就相当于你只要订阅了报纸,每天都会有人给你送过去,而不需要自己去报社拿。

扩展

在我们的java中也定义了观察者模式的接口,并且Java中的事件监听机制就是使用观察者模式实现的,它们分别是 java.util.Observer 和 java.util.Observable 。通过实现这两个接口我们可以实现自己的观察者模式,下面就让我们来看看这两个类。

类图:

简单介绍下上面的类图:

  1. 抽象主题类(Observable): addObserver 是用来注册观察者的。 notifyObservers 是用来通知所有的观察者的。 setChanged 是用来告知 notifyObservers 状态已经改变,可以通知了。其他的就不做介绍了。

下面让我们使用java提供的观察者模式接口来实现上面的问题:

People.java:

import java.util.Observer;
import java.util.Observable;
public class People implements Observer {
private final String name;
private Observable observable;
public People(String name, Observable o) {
this.name = name;
observable = o;
observable.addObserver(this);
} @Override
public void update(Observable o, Object arg) {
System.out.println(String.format("I am %s,I am looking %s", name, arg.toString()));
}
}

Robot.java:

import java.util.Observer;
import java.util.Observable; public class Robot implements Observer {
private static int num = 0;
private final int id = num++;
private Observable observable; public Robot(Observable observable) {
this.observable = observable;
observable.addObserver(this);
} @Override
public void update(Observable o, Object arg) {
System.out.println(String.format("Robot:%s,Context:%s", id, arg.toString()));
}
}

SportNews.java:

import java.util.Observable;

public class SportNews extends Observable {
public void newContext(String context) {
setChanged();
notifyObservers(context);
}
}

测试用例:

TestMain.java:

public class TestMain {
public static void main(String... ars) {
SportNews sportNews = new SportNews();
People xiaoLi = new People("xiao li", sportNews);
People xiaoMin = new People("xiao min", sportNews);
Robot robot = new Robot(sportNews); sportNews.newContext("H:F 1:1");
sportNews.newContext("湖人:火箭 102:103");
}
}

以上是本博客的全部内容,希望看完会对你有一定的启发。

[design pattern](2) Observer的更多相关文章

  1. Design Pattern: Observer Pattern

    1. Brief 一直对Observer Pattern和Pub/Sub Pattern有所混淆,下面打算通过这两篇Blog来梳理这两种模式.若有纰漏请大家指正. 2. Use Case 首先我们来面 ...

  2. Learning JavaScript Design Patterns The Observer Pattern

    The Observer Pattern The Observer is a design pattern where an object (known as a subject) maintains ...

  3. 说说设计模式~大话目录(Design Pattern)

    回到占占推荐博客索引 设计模式(Design pattern)与其它知识不同,它没有华丽的外表,没有吸引人的工具去实现,它是一种心法,一种内功,如果你希望在软件开发领域有一种新的突破,一个质的飞越,那 ...

  4. [转]Design Pattern Interview Questions - Part 4

    Bridge Pattern, Composite Pattern, Decorator Pattern, Facade Pattern, COR Pattern, Proxy Pattern, te ...

  5. [转]Design Pattern Interview Questions - Part 2

    Interpeter , Iterator , Mediator , Memento and Observer design patterns. (I) what is Interpreter pat ...

  6. [转]Design Pattern Interview Questions - Part 1

    Factory, Abstract factory, prototype pattern (B) What are design patterns? (A) Can you explain facto ...

  7. C++ Design Pattern: What is a Design Pattern?

    Q: What is a Design Pattern? A: Design Patterns represent solutions to problems what arise when deve ...

  8. Design Pattern in Simple Examples

    Instead of defining what is design pattern lets define what we mean by design and what we mean by pa ...

  9. java设计模式大全 Design pattern samples in Java(最经典最全的资料)

    java设计模式大全 Design pattern samples in Java(最经典最全的资料) 2015年06月19日 13:10:58 阅读数:11100 Design pattern sa ...

随机推荐

  1. 题解 AT1357 【n^p mod m】

    此题就是快速幂取模 先简单讲一讲快速幂 首先,快速幂的目的就是做到快速求幂,假设我们要求a^b,按照朴素算法就是把a连乘b次,这样一来时间复杂度是O(b)也即是O(n)级别,快速幂能做到O(logn) ...

  2. 初学Python写二进制文件

    初学Python写二进制文件 把一个图片的16进制数据保存到一个txt文本,从这个txt文本读出并保存为二进制文件jpg图片文件.说明:图片读出的0xff粘贴ff到文本中,读出时是字符串的”ff”. ...

  3. vuex辅助函数和vuex5个属性

    在上篇中,我们可以知道如果想要访问vuex.store中state中的数据,需要this.$store.state.属性名.显然这样访问数据写的代码很很不简洁的,辅助函数就是用来解决这个问题的. 1. ...

  4. vue项目引入外部字体

    1.UI设计图有"华文黑体",担心客户端没有该字体,将"huawen.ttf"字体文件,放入项目中: 2.创建一个font.scss(或font.css)文件: ...

  5. js中的生成器函数

    入门 简单来说,用法如下: function* fn() { console.log(1); //暂停! yield; //调用next方法继续执行 console.log(2); } var ite ...

  6. MongoDB的使用学习之(四)权限设置--用户名、密码、端口==

    本文参照:http://hi.baidu.com/tianhuimin/item/590d96cfd7ac1509c610b26a 本人也是按照此文章操作的,但是有些不妥,红色文字就是我实践后,需要改 ...

  7. Solr知识点摘录

    1.Solr和Lucene区别: 2.Solr的下载与安装 3. 4. 5. 6.Solr基础 7. 8.Solr索引操作 9. 10.

  8. VLAN原理详解[转载] 网桥--交换机---路由器

    来自:http://blog.csdn.net/phunxm/article/details/9498829 一.什么是桥接          桥接工作在OSI网络参考模型的第二层数据链路层,是一种以 ...

  9. Myabtis中批量更新update多字段

    在mybatis中批量更新多个字段 推荐使用如下操作: 方式1:在Dao层接口中: void updateBatch(@Param("list")List<Student&g ...

  10. 03python面向对象编程5

    5.1 继承机制及其使用 继承是面向对象的三大特征之一,也是实现软件复用的重要手段.Python 的继承是多继承机制,即一个子类可以同时有多个直接父类. Python 子类继承父类的语法是在定义子类时 ...