一、观察者模式简介:
首先看百度百科上对观察者模式的简介:观察者模式(Observer)完美的将观察者和被观察的对象分离开。举个例子,用户界面可以作为一个观察者,业务数据是被观察者,用户界面观察业务数据的变化,发现数据变化后,就显示在界面上。面向对象设计的一个原则是:系统中的每个类将重点放在某一个功能上,而不是其他方面。一个对象只做一件事情,并且将他做好。观察者模式在模块之间划定了清晰的界限,提高了应用程序的可维护性和重用性。
观察者模式体现了系统内模块之间存在的1:n的依赖关系,其中 1在整个系统中被称作主题(Subject),n在系统中被称作观察者(Observer)。在整个系统中,这些观察者均需要利用主题的状态来决定自身的状态,当主题的状态发生改变时,这些观察者需要能被通知到并改变自身的状态。同时,当向系统中增加新的观察者时不能对系统中已存在的观察者和主题的代码造成影响,即需满足软件设计模式的开-闭原则,因此需要运用到抽象接口等技术。在观察者模式中存在以下几个角色:
- Subject:抽象主题(抽象被观察者),抽象主题角色把所有观察者对象保存在一个集合里,每个主题都可以有任意数量的观察者,抽象主题提供一个接口,可以增加和删除观察者对象。
- ConcreteSubject:具体主题(具体被观察者),该角色将有关状态存入具体观察者对象,在具体主题的内部状态发生改变时,给所有注册过的观察者发送通知。
- Observer:抽象观察者,是观察者者的抽象类,它定义了一个更新接口,使得在得到主题更改通知时更新自己。
- ConcrereObserver:具体观察者,实现抽象观察者定义的更新接口,以便在得到主题更改通知时更新自身的状态。
那么问题来了,为什么需要这么设计呢?其实在系统中,主题可能不止一个,观察者可以同时观察多个主题,两边形成了多对多的关系。众多主题均有一个不变的特性—通知:当自己的状态发生变化时通知观察自己的观察者状态发生变化;众多观察者均有一个不变的特性—更新:当收到主题的状态改变信号时对应的更新自己的状态。因此需要把这两个不变的部分从中抽取出来即分离变化,因而需要设计Subject抽象主题以及Observer抽象观察者。举个简单的例子,当学校周一进行一周汇报时老师不可能将一周工作和每个同学一一通知到位,不同的老师会有不同的信息,但他们均采用广播通报的方式进行工作汇报,因此广播通报的方式是老师这个主题不变的特性;同时学生接受信息的时候均具有听这一特性,但每个学生的听的方式却各有不同。这就很好的说明了观察者模式在生活中有很多的应用。在数据发生变化的时候只需通过访问接口类的更新方法即可,在添加新的观察者或者主题时只需实现相应的接口即可。
以下为观察者模式的类图:
二、观察者模式在股票分析中的应用
股票的价格经常处于一种瞬息万变状态之中,股民想要从中获利必须要对股票近段时间的数据走向进行分析,很多炒股软件均提供了数据分析的功能,其中根据股票价格的变化提供分时图,K线图均是很常见的功能。从以上的描述中可以得出股票交易是具体主题,分时图、K线图则是具体观察者。当股票的价格发生变化时,分时图和K线图需要能够根据股票价格的变换而相应的调整自身的状态图,因此对于股票价格的数据分析可以通过观察者模式来解决。为了简化问题来模拟股市的变化,我们先做出以下假设:
- 假设每天的开盘时间段为上午9:00至11:00
- 假设每天的开盘价为每天前10分钟的交易总额的平均值,每天的收盘价为每天最后10分钟的交易总额的平均值
- 假设每支股票的模拟的第一天的前一天的收盘价为100元/股,涨跌幅为10%
- 股票价格的模拟采用随机数来模拟,且每一分钟的价格建立在前一分钟价格的基础上(我采用的模拟公式为P(t) = P(t-1) + random(0,1)*1 - 0.5, 这公式不准确但有效果,最好的方式是采用蒙特卡罗模拟算法)
在以上的假设的基础上,我们针对本问题进行设计:
1、设计4个基本角色:
①抽象主题Subject(包含三种抽象方法):
②抽象观察者Observer(包含抽象方法update):
③具体主题StockDealSubject(实现抽象接口Subject模拟股市交易):在观察者模式中观察者具有两种获取数据的方式:推数据和拉数据。其中推数据方式是指具体主题将变化后的数据全部传给具体观察者,即将变化后的数据作为参数进行传递;而拉数据是指具体主题不将变化后的数据交给具体观察者,而是提供获取这些数据的额方法,具体观察者在得到通知后可以调用具体主题的方法得到数据,就相当于观察者自己将数据拉了过来,故称作拉数据。因为我设计的图形化界面是通过用户点击对应的按钮产生对应的分析图,故在这里我采用的是拉数据的方式。
④具体观察者TimeFigure(分时图)和KlineFigure(K线图):这两个具体观察者均实现了抽象观察者中的drawFig()的更新方法。在具体的drawFig方法中,由于我设计的图形界面通过用户点击对应的按钮产生对应的分析图,因此需要当前的更新操作是否是对自己这个观察者的更新操作。在数据结构的设计方面,我采用的是二维数组prices[m][n],其含义为第m支股票在第n/120+1天的第n%120分钟的收益为prices[m][n]。对于K线图的绘制,我自定义了数据类Klineobject,其中分别包含了K线图的四个基本属性:开盘价、收盘价、当天交易额最大值、当天交易额最小值,并用ArrayList来存储每天的属性。在这些数据及结构的基础上,需要对涨停和跌停这两种情况进行限定,故需要对随机产生的股票价格数据进行处理,使得其在满足条件的范围内,得出新的数据后则需要根据这些数据来进行绘图。
2、根据得出的股票价格数据绘图:Java绘图我是采用了JFreeChart,它是JAVA平台上的一个开放的图表绘制类库。需要导入三个包:jfreechart.jar、jcommon.jar、gnujaxp.jar,这些包可以去http://mvnrepository.com/上下载,版本可以挑选下载量最多的。然后根据得到的股票数据创建两个绘图类DrawKlineChart和DrawLineChart来分别绘制K线图和分时图,以下为绘图代码:
3、以上已经将整个观察者模式的框架搭建完成,包括数据分析图的绘制,因此可以在这个基础上编写图形用户界面来使用该观察者模式。因此编写Application类来作为程序的入口,用户可以通过点击对应的分时图或K线图按钮来查看相应的分析图:
三、实验效果截图:
①运行程序,查看4支股票在一天内的股价变化图,则通过getPrice(120,4)来获取最初的股价数据,以下为截图(由于我定义的随机数生成是在Application中的,故需要通过重新运行程序来获得多组结果):
②运行程序,查看2只股票10天内的K线图,则通过getPrice(1200,2)来获取最初的股价数据,以下为截图(由于我定义的随机数生成是在Application中的,故需要通过重新运行程序来获得多组结果):
以上即为本次对观察者模式的学习与应用,感觉蛮有趣的哈哈
转载请注明出处,谢谢~