设计模式之观察者模式

/ 设计模式 / 1 条评论 / 543浏览

今天我继续和大家分享一下设计模式中的知识,今天我们来看一下观察者模式。观察者模式也可以叫发布订阅模式,在实际的场景中有很多时候会遇到这种设计模式。在现实的生活中可以将这种模式理解为报纸订阅服务。也就是说,如果用户订阅了某个报社的报纸,那么报社在收到这个订阅请求后,就会每天把最新的报纸送到用户的手中,如果某一天用户不想继续看这家报社的报纸了,那么就可以取消这个订阅,那么这时报社又收到这个用户取消订阅的请求,然后把这个用户从以后的送报纸用户的名单中删除掉。所以第二天在给其他用户送报纸的时候,就不会继续给这个用户送了。通过上面这个小的例子使我们知道所谓观察者也就是上述例子中的用户,那么这个用户在观察什么呢?答案可显而知,也就是观察订阅这家报社的报纸有没有最新的,如果有最新的报纸,那么报社就会自动将新的报纸,送到自己的手中。那么报纸如果没有最新的呢,也就是没有更新呢?例如在法定假日期间,(并不是所有的报纸都是按天发版的,还有一些报纸法定假日停刊),那么这时用户就不会收到新的报纸了。

上面我举了报纸订阅的服务来简单说明了观察者模式。那么接下来我们将按照软件开发的角度,来分析一下观察者模式的具体使用。我们前面提到过了,观察者模式的例子有很多,我们下面将以用户在网上买东西,也就是用户下单为例来详细了解一下观察者模式的使用。

大家知道,在电商网站中,用户在网上成功买完东西付款成功后,系统都会为该用户创建一个订单来记录用户所购买的所有商品,实际上当用户购买失败时,系统也会创建订单,只不过该订单用户支付状态为失败而已。这里我们暂时只考虑支付成功的情况,也就是默认用户如果下单了,就支付成功。虽然用户支付成功了,但对于系统来说这个流程还没有完毕,这刚好只是个开始,因为当用户下单成功后我们系统在后续的处理中会涉及到很多个系统的调用。例如:物流系统、商品系统、积分系统等等。也就是说当用户下单成功后,会调用物流系统、商品系统、积分系统等这些系统来分别处理相应的逻辑。只要系统检测到了订单发生了变化(暂时我们只考虑成功的情况),其它的系统也跟着发生变化。在这一点上和我们上述报纸订阅服务是一个意思。只不过不同的之处就是把用户换成了不同的系统而已,而不同的系统要观察的就是订单是否变化。下面我们按照这样的思路把上述的业务用代码表现出来,具体的代码如下。

/**
* 订单支付成功
*/
public class OrderSuccess {

private LogisticsSystem logisticsSystem;
private GoodsSystem goodsSystem;
private IntegralSystem integralSystem;

public OrderSuccess() {
logisticsSystem = new LogisticsSystem();
goodsSystem = new GoodsSystem();
integralSystem = new IntegralSystem();
}

/**
* 当订单变动时执行此方法
*/
public void orderUpdate() {
String order = "新的支付成功订单编号为:20171125202644";
logisticsSystem.orderUpdate(order);
goodsSystem.orderUpdate(order);
integralSystem.orderUpdate(order);
}

}
/**
* 物流系统
*/
public class LogisticsSystem {

public void orderUpdate(String order) {
System.out.println(String.format("物流系统收到订单更新\t%s", order));
}
}
/**
* 商品系统
*/
public class GoodsSystem {

public void orderUpdate(String order) {
System.out.println(String.format("商品系统收到订单更新\t%s", order));
}
}
/**
* 积分系统
*/
public class IntegralSystem {

public void orderUpdate(String order) {
System.out.println(String.format("积分系统收到订单更新\t%s", order));
}
}
public class Test {

public static void main(String[] args) {
OrderSuccess orderSuccess = new OrderSuccess();
orderSuccess.orderUpdate();
}

}
物流系统收到订单更新 新的支付成功订单编号为:20171125202644
商品系统收到订单更新 新的支付成功订单编号为:20171125202644
积分系统收到订单更新 新的支付成功订单编号为:20171125202644

我们看输出实现了我们想要的结果,也就是当订单发生更新时,其他3个系统都会收到信息。但我们在之前的文章中提到过,我们在设计系统时,不要针对实现编程,要针对接口编程,这样程序比较方便扩展。按照我们上述的代码,如果我们要添加新的系统,例如卡卷系统 ,那么这时我们就要修改曾经已经编写好的代码,也就是OrderSuccess接口,那这就违背了设计模式的基本原则了。显然上述的代码,虽然可以实现需求,但是却不是最好的,因为不方便扩展。那么怎么办?我们分析需求知道这显然是一个一对多的关系,当订单更新时,其他和它相关的系统都需要接到通知然后更新,类似报纸订阅是一样的,只要报纸发生变化,那么订阅该报纸的人都能知道。其实,这就是典型的观察者模式。下面我们先看一下观察者模式的定义。

观察者模式:定义了对象之间的一对多依赖,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。

我们在说的简单点比如对一个有状态的对象,我们称之为主题对象,然后我们有一堆和主题对象依赖的对象,我们叫它观察者对象。这样当主题对象更新时,观察者对象会自动收到通知并更新。

按照我们上面的代码怎么把他们修改为观察者模式呢?我们按之前学到的知识应该对接口编程,而不是对实现编程。所以我们应该抽取出两个接口一个是主题接口,一个是观察者接口。这样主题只知道了观察者都实现了观察者接口,而主题不需要知道观察者具体的类是谁,这样在任何时候我们都可以随时增加新的观察者,只要实现观察者接口就可以了。而主题并不做任何的修改,因为主题对象唯一依赖的东西是一个实现了观察者接口的对象列表,所以我们可以随时添加任意的观察者,而主题对象并不需要做任何的更新,这就遵循了设置模式的原则,将对象中可能变化的部分提取出来,这样当其他对象变化时,该对象不需要做任何的更改。这里我们还有一个重要的设置模式原则,也就是为交互对象之间的松耦合设计而努力。在说的直白点就是我们在设计系统时应该将对象与对象之间的耦合度设计的尽量低,耦合度越低,对象与对象的依赖关系也就越低,这样也就方便我们更好的扩展。

那我们应该怎么将上述的代码修改为观察者模式呢?通过以前的知识我们知道应该对接口编程,而不是对实现编程,因为这样我们比较方便扩展。所以我们应该将上述代码中涉及到的物流系统、商品系统、积分系统抽象出一个公共的接口,同理我们也将订单抽象出一个接口,这样的好处是,当创建新的子类时,对接口的编程代码是不需要变化的,这就遵循了我们上述提到过的松耦合了。下面我们将上述代码修改为真正的观察者模式,具体代码如下:

/**
* 观察者接口
*/
public interface SystemObserver {

/**
* 接受主题通知
* @param subject
*/
public void receiveSubjectNotify(String subject);
}
/**
* 主题接口
*/
public interface OrderSubject {

/**
* 添加一个观察者
* @param observer
*/
public void addObserver(SystemObserver observer);

/**
* 删除一个观察者
* @param observer
*/
public void removeObserver(SystemObserver observer);

/**
* 当主题变化时调用此方法通知所有观察者
*/
public void notifyObserver();
}
/**
* 订单支付成功
*/
public class OrderSuccess implements OrderSubject {

private List<SystemObserver> systemObservers; // 保存所有观察者对象列表


public OrderSuccess() {
systemObservers = new ArrayList<SystemObserver>();
}

/**
* 将新的观察者对象保存到观察者对象列表中
*/
public void addObserver(SystemObserver observer) {
systemObservers.add(observer);
}

/**
* 将当前的观察者对象从观察者对象列表中删除
*/
public void removeObserver(SystemObserver observer) {
systemObservers.remove(observer);
}

/**
* 通知所有观察者
*/
public void notifyObserver() {
String order = "新的支付成功订单编号为:20171125202644";
for (SystemObserver observer : systemObservers) {
observer.receiveSubjectNotify(order);
}
}
}
/**
* 物流系统
*/
public class LogisticsSystem implements SystemObserver {

public void receiveSubjectNotify(String subject) {
System.out.println(String.format("物流系统收到订单更新\t%s", subject));

}
}
/**
* 商品系统
*/
public class GoodsSystem implements SystemObserver {

public void receiveSubjectNotify(String subject) {
System.out.println(String.format("商品系统收到订单更新\t%s", subject));

}
}
/**
* 积分系统
*/
public class IntegralSystem implements SystemObserver {

public void receiveSubjectNotify(String subject) {
System.out.println(String.format("积分系统收到订单更新\t%s", subject));

}
}
public class Test {

public static void main(String[] args) {
OrderSuccess orderSuccess = new OrderSuccess();

LogisticsSystem logisticsSystem = new LogisticsSystem();
GoodsSystem goodsSystem = new GoodsSystem();
IntegralSystem integralSystem = new IntegralSystem();

orderSuccess.addObserver(logisticsSystem);
orderSuccess.addObserver(goodsSystem);
orderSuccess.addObserver(integralSystem);

orderSuccess.notifyObserver();
}

}
物流系统收到订单更新	新的支付成功订单编号为:20171125202644
商品系统收到订单更新	新的支付成功订单编号为:20171125202644
积分系统收到订单更新	新的支付成功订单编号为:20171125202644

这样我们就将上述的代码修改为真正的观察者模式的代码,这样的好处就是非常方便我们的扩展,我们在新添加新的系统时,而并不需要修改曾经已经开发好的代码,也就是订单中的已有的代码,这样就真正做到了可扩展了。下面我们将新增一个卡卷系统,来证明我们上述所说的可扩展性。

/**
* 卡卷系统
*/
public class TicketSystem implements SystemObserver {

public void receiveSubjectNotify(String subject) {
System.out.println(String.format("卡卷系统收到订单更新\t%s", subject));

}
}
public class Test {

public static void main(String[] args) {
OrderSuccess orderSuccess = new OrderSuccess();

LogisticsSystem logisticsSystem = new LogisticsSystem();
GoodsSystem goodsSystem = new GoodsSystem();
IntegralSystem integralSystem = new IntegralSystem();
TicketSystem ticketSystem = new TicketSystem();

orderSuccess.addObserver(logisticsSystem);
orderSuccess.addObserver(goodsSystem);
orderSuccess.addObserver(integralSystem);
orderSuccess.addObserver(ticketSystem);

orderSuccess.notifyObserver();
}

}
物流系统收到订单更新 支付成功订单编号为:20171125202644
商品系统收到订单更新 新的支付成功订单编号为:20171125202644
积分系统收到订单更新 的支付成功订单编号为:20171125202644
卡卷系统收到订单更新 支付成功订单编号为:20171125202644

快看,我们成功的将新的卡卷系统添加到了这个观察者了,并且它成功收到了订单变更的通知,并且我们并没有修改任何有关订单的代码,这就是我们上面所说的低耦合,这也就是观察者模式的好处。

到这里,我们已经将观察者模式都介绍完了,本应该到这里就结束了,但这个观察者模式有点特别,Java为了我们更方便的使用观察者模式,所以在Java中直接内置的支持观察者模式,也就是我们自己并不需要创建主题和观察者了,因为Java中直接就提供了这两个接口(确切的说是一个接口和一个类)。下面我们将上述的代码,用Java中内置的观察者模式来实现。

下面为具体的代码:

/**
* 订单支付成功
*/
public class OrderSuccess extends Observable {

/**
* 通知所有观察者
*/
public void notifyObserver() {
String order = "新的支付成功订单编号为:20171125202644";
setChanged(); // 调用此方法则证明当前主题有更新
super.notifyObservers(order); // 直接调用父类的通知方法通知所有观察者
}
}
/**
* 物流系统
*/
public class LogisticsSystem implements Observer {

public void update(Observable o, Object arg) {
if (o instanceof OrderSuccess) {
System.out.println(String.format("物流系统收到订单更新\t%s", arg));
}
}
}
/**
* 商品系统
*/
public class GoodsSystem implements Observer {

public void update(Observable o, Object arg) {
if (o instanceof OrderSuccess) {
System.out.println(String.format("商品系统收到订单更新\t%s", arg));
}
}
}
/**
* 积分系统
*/
public class IntegralSystem implements Observer {

public void update(Observable o, Object arg) {
if (o instanceof OrderSuccess) {
System.out.println(String.format("积分系统收到订单更新\t%s", arg));
}
}
}
/**
* 卡卷系统
*/
public class TicketSystem implements Observer {

public void update(Observable o, Object arg) {
if (o instanceof OrderSuccess) {
System.out.println(String.format("卡卷系统收到订单更新\t%s", arg));
}
}
}
public class Test {

public static void main(String[] args) {
OrderSuccess orderSuccess = new OrderSuccess();

LogisticsSystem logisticsSystem = new LogisticsSystem();
GoodsSystem goodsSystem = new GoodsSystem();
IntegralSystem integralSystem = new IntegralSystem();
TicketSystem ticketSystem = new TicketSystem();

orderSuccess.addObserver(logisticsSystem);
orderSuccess.addObserver(goodsSystem);
orderSuccess.addObserver(integralSystem);
orderSuccess.addObserver(ticketSystem);

orderSuccess.notifyObserver();
}

}
物流系统收到订单更新 支付成功订单编号为:20171125202644
商品系统收到订单更新 新的支付成功订单编号为:20171125202644
积分系统收到订单更新 的支付成功订单编号为:20171125202644
卡卷系统收到订单更新 支付成功订单编号为:20171125202644

我们看使用Java内置的观察者和我们自定义的观察者模式的效果是一样的,但代码却大大的减少了,因为大部分的代码都已经被内置的实现了。虽然这样有很大的好处,但有一点不太方便,可能你们也发现了,也就是Observable是一个类,而不是一个接口,如果我们要想使用Java内置的观察者模式,如果主题已经继承了其他的父类,那我们就不能使用Java内置的观察者模式了,因为在Java中并不支持多重继承,这也就是Java内置的观察者模式的弊端。下面是观察者模式的类图:

WX20171126-020654.png

  1. 这个必须要收藏

    回复