前言

在上一个博客中我们介绍了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. Linux操作系统目录一览表

    / // 根目录 /bin //存放必要的命令 比如ls.cp.mkdir等命令 /boot //存放内核以及启动所需的文件 /dev //存放硬件设备文件 比如声卡.磁盘.光驱 /etc //存放系 ...

  2. centos7里创建用户和组

    1.创建组distro,其GID为2019groupadd -g 2019 distro 2.创建用户mandriva, 其ID号为1005:基本组为distro useradd mandriva - ...

  3. 【Linux 架构】Linux内核架构

    (1)System Call Interface(SCI)------系统调用接口(2)Process Management(PM)-------进程管理模块(3)Memory Management( ...

  4. uboot常用命令

    一. 常用简单命令 1.1. help命令 a. 帮助查看其他命令的使用方法,类型linux下man b. 示例: help help x210 # help help help [command . ...

  5. ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;

    参考来源:https://blog.csdn.net/yuxinha11/article/details/80090197 ENGINE=InnoDB不是默认就是这个引擎吗?——是的,如果不写也是ok ...

  6. 搜索专题: HDU1258Sum It Up

    Sum It Up Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total ...

  7. 【学习总结】快速上手Linux玩转典型应用-第7章-WebServer安装和配置讲解

    课程目录链接 快速上手Linux玩转典型应用-目录 目录 1. Apache的安装 2. Apache的虚拟主机配置及伪静态操作 3. Nginx的基本操作 4. Nginx伪静态的实现 5. 实例演 ...

  8. wpf中文本框只能输入整数

    private void txtBarCodeNum_KeyUp(object sender, KeyEventArgs e) { TxtInt(sender as TextBox); } priva ...

  9. IPv6地址格式示例及IPv6与IPv4的区别分析

    认识IPv6地址 IPv4地址是类似 A.B.C.D 的格式,它是32位,用\".\"分成四段,用10进制表示: 而IPv6地址类似X:X:X:X:X:X:X:X的格式,它是128 ...

  10. vb6中word编程总结

    1,在project\references 中加入microsoft word 9.0 object library 2, 启动word    Dim wApp As Word.Application ...