观察者模式及应用

观察者模式及应用
观察者模式是对象的行为模式,顾名思义,即存在观察者和被观察者。 观察者模式可以让多个观察者同时监听同一个被观察对象,当被观察对象发生变化时,并通知所有观察者,使各个观察者能作出相应的响应。适当地运用观察者模式,能提高自身代码的设计水平。
  观察者模式理解和编码都比较简单,通常包括以下步骤:
1. 设计观察者接口类;
  2. 观察者类实现该接口;
  3. 设计被观察者抽象类,该类中提供一些方法,如:添加观察者对象,删除观察者对象,把事件通知给各个观察者对象;
  4. 设计被观察者类,继承被观察者抽象类,在该类中,可以根据需要在该类中,可以定义方法:被观察者是否发生变化

一个例子
电信业务的系统中虽然全国各地的用户,能享受的服务都差不多,但是从系统层面上来说,每个地区的实现是不仅相同的。这里的原因有很多,比如各个省的系统的厂商是不一样的,即使厂商是一样的,由于每个地区所推的业务不一样,开发团队的不一样等等。
既然这么多省市,每个省所用的系统是不一样的,而不是全国使用的是同一个系统。所以每个省的系统之间必然要存在某种通讯,以便实现异地业务(比如浙江的用户在河南充值等)。如果省与省之间的系统直接进行通讯,那么这将会是个有向图(每两个点之间有且只有一条边)。这样就太复杂,而且耦合度非常高。
为了降低耦合度并且使实现更加简化,这里加入一个系统:中心系统C。各个省的系统只与中心系统C进行交互,并有中心系统C路由到目标省系统。这样就由一个有向图变成了一个单根多叶的树形结构:

[img]/upload/attachment/109627/20b2b46a-ff08-37bc-b66d-e76b783d4b6f.gif[/img]


这样一来,必然存在中心系统和其他系统之间的通讯。各省的系统如果需要做异地业务,那么需要首先和中心系统通讯,由中心系统和目标的省系统进行交互,并等待其返回结果,并将结果返回给源省系统。
通讯有成功有失败,所以中心需要考核各省系统的成功率和及时率。对哪些达不到考核要求的进行扣分。而个省系统,为了满足中心系统要求的成功率和及时率,会做一些系统上的设计。
其中有一个功能,系统需要将交易中错误的笔数和超时的笔数,在最近一个小时内达到规定次数的,需要通知相关的人,以便相关人员在第一时间可以作出反映。这里的通知方式有很多种:邮件通知,短信通知,IM通知等。这里我们就讨论这个通知机制在系统中的实现。
第一种方式
先来看设计的类图。

[img]/upload/attachment/109629/94305462-6ce9-3ff9-8868-853b95503d24.gif[/img]

ErrorOutput是个类,当系统发生超时,或者处理失败之后,会来调用这个类的process方法。另外,此类引用了INotify接口,在process方法里,首先会调用INofify接口的nofify方法,以达到通知相关人员的效果,然后会做一些其他的业务逻辑。而INofify的实例是通过工厂类NofifyFactory获得的。
INotify分别有三个实现类:邮件通知类(EMailNofify)、即时通讯工具通知(IMNofify)、短信方式通知(SMSNofify)。
ErrorOutput只是应用了Inofify接口,而不是具体的实现类,所以它并不知道以什么样的方式通知相关人员。这个通知方式由NofifyFactory决定。
这种方式实现的缺点:
1. 只能以一种方式通知相关人员
2. 需要等待通知程序通知完成了,才能向下执行其他逻辑。影响主程序及时性的要求。
如果需要将这个设计,硬要向观察者模式上套,那么ErrorOutput是被观察者,而INofify等是观察者。当ErrorOutput的process被调用时,就需要通知观察者对象INofify。
第二种方式
第二种方式和第一种方式没有太大的区别,只是加入了一个线程。以达到主处理逻辑和通知异步的效果,使通知程序不影响主处理程序的运行。其类图如下:


[img]/upload/attachment/109631/3d957235-4b1f-37a6-b827-a555bd130996.gif[/img]


这里ErrorOutput类不再是被观察者,它只是向线程NofifyTreed类注册了“又发生了一次错误”(通过addError方法)。而NofifyTread类承担起了被观察者的角色。在线程处理的方法(run)里,扫描errorList中产生错误的错误嘛,从而决定是否需要通知观察者(INofify)。

更优雅的实现方式
观察者模式,允许向被观察者对象注册更多的观察者对象,以达到通知更多的观察者的目的。前两种实现方式中,并没有这样的功能。而是在被观察者对象里,指定了谁作为我的观察者,外部不能随意的添加和删除观察者。
在这个实现方式里,我们引入JAVA自带的观察者模式。在 java.util包下有两个类,Observable和Observer。Observable是个抽象类,承担被观察者抽象角色;Observer是个接口,承担观察者抽象角色。
所以我们不再需要接口INofify。而用Observer接口代替。
由于Observable是个抽象接口,所以这里的线程需要实现接口Runable。具体类图如下:

[img]/upload/attachment/109633/10e11983-f6d1-34fc-b365-9dc726d9e4a6.gif[/img]

NofifyRunable变成了被观察者的实现,同时它还实现了Runable接口。是为了通讯机制不影响主处理逻辑的运行。
具体的代码如下:
ErrorOutput.java类:
public class ErrorOutput {

private Thread notifyThread;
private NotifyRunable notifyRunable;

public ErrorOutput() {
notifyRunable = NotifyRunable.getInstance();
notifyRunable.addObserver(new SmsNotify());
notifyRunable.addObserver(new EMailNotify());
notifyRunable.addObserver(new IMNotify());
notifyThread = new Thread(notifyRunable);
notifyThread.start();
}

public void process(String errorCode) {
notifyRunable.addErrorCode(errorCode);
//do other thing
}
}

NotifyRunable.java类文件:
public class NotifyRunable extends Observable implements Runnable {

public static void main(String[] args) {
NotifyRunable runable = new NotifyRunable();
Thread thread = new Thread(runable);
thread.start();
}

private static NotifyRunable instance = null;

private ICounter needNotifyCounter;
private HashMap counterMap = null;
private ArrayList errorCodeList = null;
private final static Object lock = new Object();

private boolean stop = false;

private NotifyRunable() {
needNotifyCounter = createCounter();
counterMap = new HashMap();
errorCodeList = new ArrayList();
}

private static ICounter createCounter() {
return new IntervalCounter(DisasterToleranceUtil.getNotifyInterval() * 1000);
}

public static NotifyRunable getInstance() {
if (instance == null) {
instance = new NotifyRunable();
}
return instance;
}


public void addErrorCode(int errorCode) {
if (!DisasterToleranceUtil.isDisasterToleranceErrorCode(errorCode)) {
return;
}
synchronized (lock) {
errorCodeList.add(String.valueOf(errorCode));
}
}


private void slep(long l) {
try {
Thread.sleep(l);
} catch (Exception e) {
//do nothing
}
}

public void run() {
while (true) {
while (!stop) {
ArrayList list = new ArrayList();
synchronized (lock) {
if (errorCodeList.size() > 0) {
list = new ArrayList(errorCodeList.size());
moveList(errorCodeList, list);
}
}
processNotify(list);
slep(3 * 1000);
}
slep(120 * 1000);
}
}

private void moveList(ArrayList src, ArrayList dst) {
for (int i = src.size() - 1; i >= 0; i--) {
dst.add(src.remove(i));
}
}

private void processNotify(ArrayList list) {
for (int i = list.size() - 1; i >= 0; i--) {
long times = needNotifyCounter.plus();
String message = "在过去" + DisasterToleranceUtil.getNotifyInterval() + "秒时间内,已经发生" + times + "次容灾!最大次数为:" + DisasterToleranceUtil.getMaxToleranceTimes();
if (CBossLogUtil.getLogger(this.getClass()).isDebugEnabled()) {
CBossLogUtil.getLogger(this.getClass()).debug(message);
}
String errorCode = (String) list.get(i);
ICounter counter = (ICounter) counterMap.get(errorCode);
if (counter == null) {
counter = createCounter();
counter.setName(errorCode);
counterMap.put(errorCode, counter);
}
counter.plus();
if (times > DisasterToleranceUtil.getMaxToleranceTimes()) {
sendMesssage();
}
}
}

private void sendMesssage() {
if (CBossLogUtil.getLogger(this.getClass()).isInfoEnabled()) {
CBossLogUtil.getLogger(this.getClass()).info("发通知给相关人员");
}
String message = "在过去" + DisasterToleranceUtil.getNotifyInterval() + "秒时间内,已经发生" + needNotifyCounter.getCurrentCount() + "次错误!";
message += "其中";
ICounter[] counter = (ICounter[]) counterMap.values().toArray(new ICounter[counterMap.size()]);
message += sortString(counter);
try {
setChanged();
notifyObservers("系统通知你!" + message);
} catch (Exception e) {
System.out.println("发通知失败!"+e);
}
}

private String sortString(ICounter[] counters) {
for (int i = 0; i < counters.length; i++) {
for (int j = i + 1; j < counters.length; j++) {
if (counters[i].getCurrentCount() < counters[j].getCurrentCount()) {
ICounter tmp = counters[i];
counters[i] = counters[j];
counters[j] = tmp;
}
}
}
int num = counters.length > 5 ? 5 : counters.length;
String[] message = new String[num];
for (int i = 0; i < num; i++) {
ICounter counter = counters[i];
message[i] = counters[i].getName() + "=" + counter.getCurrentCount();
}
return StringUtils.join(message, ",");
}

Observer的实现类就不再描述了。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值