Web编程技术交流网

博客文章

欢迎访问本站博客,本站免费提供大量网站编程的技术资料和娱乐信息,欢迎编程交流1群讨论!

设计模式在Magento中的应用–观察者模式

设计模式在Magento中的应用–观察者模式

观察者模式的定义:定义对象之间一种一对多的依赖关系,使得一个对象改变状态,则所有依赖对象都会得到通知并被自动更新。从定义看可以看出几个关键点:

1、一个是对象间的一种1:n依赖关系,是怎样建立的呢?
2、一个对象的状态发生了变化,那么他如何把这个通知发出去呢?
3、所依赖的对象是怎么被自动更新的呢?

带就这些问题,我们来看一个生活动的实列。<<潜伏>>是前一段时间热播的电视剧,中共放了一个余则成到国民党的天津站进行潜伏工作,负责收集国民党的情报工作,一旦国民党有啥动情,余则成就知道了,然后立刻通知中共,此时中共会根据情报作出一些反应。这是一个潜伏工作的正常工作流程。现在我们再假设现在日本方面出派了一个人到国民党的天津站进行潜伏工作,我们给这个日本特务叫山本56。现在国民党的天津站出现了一个重大事件要购进大量的美式装备,此时山本56,余则成都知道了,此时他们分别通知了日本方面和中共方面,日本方面和中共方面得到通知(此时的通知包含主题:购进美式装备,其中还有一些细节,如国民党这边负责人,美方负责人,购进那些装备等),根据通知日本方面和中共方面做出不同的反应。

从上面的例子我们得知:
1、国民党是对象的一方,而日本和中共是另一方,从而形成一个1:2的依赖关系,他们的关系是通过山本56和余则成建立起来的
2、国民党的状态发生变化(装备更精良),他们会被潜伏者山本56和余则成窃听到,从而把这个消息发送出去。
3、日本方面和中共方面得到通知,会执行自身方法的调用,从而被自动更新他们的状态。

从以上的分析我们要得到观察者模式中的一些重要的对象或者说是要素
1、被侦听者对象(被观察者)—–国民党
2、事件—-购进大量的美式装备.这个事件是由国民党发出的.
3、侦听者—-山本56/余则成
4、事件的响应者(观察者)—日本和中共

从以上我们也可以看出这个模式的优点:
1、很容易增加观察者,被观察者的变动和观察者内部发生变化都不会影响对方,从程序的角色看,则解藕了。
2、各自的职任都很明显。

下面的分析我们从现实例子中回到程序的角色来考虑,分析现实例子与程序之间的区别,从而更好理解观察者模式。我们知道不管那种程序,他的执行都是线性的,也就是要一行一行执行。但是从上面的例子,可以看到,购进大量的美式装备事件发出后,山本56/余则成是并行把事件给日本和中共,日本和中共是分别进行响应的,也就是说他们是并行响应的,不会出现先等你中共响应后,日方才响应。如果要做到并行响应,从程序的角色来说要由多个线程去执行,每个线程可以并行处理自己的代码。而且从国民党来看,他事件发出去后,就不再管了,我还要接着做我购装备的其它事。从程序的角色来看,主线程国民党发出事件后,将生成两个线程(山本56/余则成)分别去做他们的事,国民党主线程还是向下执行。

所以从程序上来说实现观察者模式有两种方式

1、同步机制(Magento就是使用此种)
也就是只有一个线程来处理所有的事,如国民党发出事件后,要等余则成去告诉中方,执行完成中方响应后,再由山本56告诉日方,执行完成日文响应后,再把执行权交给国民党,国民党再去做其它事(也放,中/日反应后,国民党的做法可能不同了).
同步机制实现起来比较简单,但是他有如下不好的点方:
1.1、如果观察者很多,会形成一个很长的观察者链,对程序的调试带来一些麻烦。而且链很长,性能会发生问题。
1.2、如果一个观察者出现问题,如异常(异常未被catch),error,那么后继观察者将不会执行,同时还会影响主程序的执行。
1.3、如果一个观察者出现问题,自身将不会再次执行,执行不是很可靠。
好处:
1.1 、实现起来比较简单
1.2、由于是同步,主程序可以根据观察者的反应(如一些参数的调整)来更改自己的执行路线。

2、异常机制
也就是上面分析的那种与现实情况一样的。
在这里我说一种我曾经在项目中使用过的实现方式.
主程序国民党发出购进大量的美式装备事件,其实它是向数据库中insert一条记录,把事件名和相关的数据存储到数据库中。我们程序写两个线种(山本56/余则成)每5秒或者多长时间执行一下这个线程.(java中可以使用quartz scheduler,php中可以利用操作系统的定时执行某个程序),这两个线程检查这个表中没有执行过的数据,如果存在,先把此线程执行此条数据状态标到其它表中(running),此线程成功执行后,把此线程执行此条数据状态更新(completed),如果执行失败,把状态更新为fail,同时把失败原因记录下来。下一次此线程再运行时,就可以重新读出再次执行相应的处理程序.

这种方式的好处是:
2.1、各观察者各自执行,性能方面有很大的提高。
2.2、一个线程(观察者)的执行失败不会影响到其它观察者,而且一个观察者一次执行失败,没有关系,等一下他还会重新执行,也就是说更加可靠.

不好的地方
2.1、实现起来比较复杂。
2.2、由于是异步,主程序不可以根据观察者的反应(如一些参数的调整)来更改自己的执行路线。

在分析完成观察者模式后,我们从源程序研究Magento的观察者模式的应用。
我们先一个magento中谁来担任观察者模式中的一些重要的对象或者说是要素
1、被侦听者对象(被观察者)—–magento中任一对象 ,这里并不一定是对象状态发生变化。他只是表明这个事件是由我发出的
2、事件—-magento核心代码中已定义好的或根据业务需求由用户(程序员)定义.
3、侦听者—-根据业务需求由用户(程序员)定义.
4、事件的响应者(观察者)—根据业务需求由用户(程序员)定义,一般为model类,观察者有时从概念上讲包含侦听者

在Magento中,他只是在某些地方,发出一个事件,把事件名和事件相关的数据给出。这些事件名和数据有由magento核心程序定义好的,也可以在我们自己扩写的模块中定义,他给我们提供了一个在不修改magento核心代码的情况,根据业务需求扩展magneto的能力。

1、Magento中的事件从概念上被分成以下几区域(组)

1.1、global 全局
1.2、adminhtml 后台
1.3、frontend 前台
1.4、crontab 后台调度程序域
magento中事件被分配到以上几组中,从表示上看,事件将会被配置到这些节点下.这些组将会被保存到Mage_Core_Model_App类的对象的类变量_events中,在magento初始化中初始过此类,这个对象在Magneto中只有一个对象的实例.当区域(组)未加入到_events之前,定义到这个区域(组)下的事件侦听都不会被执行。那么我们来看一下,这些区域(组)的值是什么时候加入到类变量_events的呢?
global 全局:
在Mage_Core_Model_App中的中app方法中(在magento初始化之后)有一句如下代码:
self::$_app->loadAreaPart(Mage_Core_Model_App_Area::AREA_GLOBAL, Mage_Core_Model_App_Area::PART_EVENTS);
此行代码向_event变量加入了global,此时$_events=array(‘global’=>”).在此之前加入到global节点下的事件侦听都不会被执行,在此行代码后加入到global加入到的都会执行。如在前端控制器介绍部份,有一个事件controller_front_init_routers,用来收集cms路由控制器,就是配置在global区域(组)下,因为此时的_events中还只有global,如果配置到其它区域(组)下,侦听此事件的侦听者都不会被执行.
frontend 前台
在执行每个action方法之前,都必须执行前端(frontend)用户控制器Mage_Core_Controller_Varien_Action中的preDispatch方法(在用户控制器中分析过),在preDispatch方法中有一行代码Mage::app()->loadArea($this->getLayout()->getArea());,会向$event中加入frontend,此时$events值为:$_events=array(‘global’=>”,’frontend’=>”),在此之前加入到frontend节点下的事件侦听都不会被执行,在此行代码后加入到frontend
加入到的都会执行.
adminhtml 后台
在执行每个action方法之前,都必须执行后端(admin)用户控制器Mage_Core_Controller_Varien_Action中的preDispatch方法(在用户控制器中分析过),在preDispatch方法中有一行代码Mage::app()->loadArea($this->getLayout()->getArea());,会向$event中加入adminhtml ,此时$events值为:$_events=array(‘global’=>”,’adminhtml’=>”),在此之前加入到adminhtml节点下的事件侦听都不会被执行,在此行代码后加入到adminhtml加入到的都会执行.
前台后台有两个子类Mage_Core_Controller_Front_Action和Mage_Adminhtml_Controller_Action来向$this->getLayout()->getArea()中设置不同的值.
crontab 后台调度程序
后台调度程序是入口不是index.php,而是cron.php,在cron.php下有一句Mage::app()->addEventArea(‘crontab’);下接加到_events中,此时人值为:$_events=array(‘crontab’=>’)

事件的发布与接收

Magento中任一对象都可以成为被侦听者对象,在这个对象中根据业务需求或者将来可能是扩展点,进行事件的发布,我们以订购成功后为例进行说明。在我们订单成功之后,可能要做一些业务方面的处理,有可能是订购成功了,根据定购的总金额给用户送一些coupon或者积分,或者是调用其它系统,把订单数据写到其它系统等等。
在Mage_Checkout_OnepageController中successAction方法中发布了如下事件:
代码块1

Mage::dispatchEvent(‘checkout_onepage_controller_success_action’);
您可以跟踪进去看一下这里的调用,如果把源程序研究清楚了,下面的分析就一目了然了

这个事件的发布没有带数据,只布布了事件名,也就是相当国民党只把购进大量的美式装备发布出来,没有给详细的数据。
在config的frontend节点下定义了如下两个侦听者
代码块2






singleton
googleanalytics/observer
order_success_page_view


singleton
rewardpoints/observer
recordPointsForOrderEvent




第1行说明区域(组)为frontend,第2行为events表示事件,这个名称是死的,第3说明侦听那个事件,这个名称必须与代码块1中的一致.
第4行说明下面定义的是观察者,observers也是一个固定的值,5-9行是定单完成之后要把数据写到ga中去,10-14是定单完成之后给用户积分。
行5是我们自定义的一个名字googleanalytics_order_success,相当于山本56,这个名字由我们定义,一般要给一个有意义的名字。行10相当于余则成,说明这里有两个侦听者,行6,7,8相当定义日方,行11,12,13相当定义中共,说明侦听者通知谁,在这里,我们会执行googleanalytics/observer这个model下的order_success_page_view这个方法,相当Mage::getSingleton(‘googleanalytics/observer’)->order_success_page_view(),注意type节点有三个值:singleton/object/model,如果是singleton,将调用Mage::getSingleton(‘googleanalytics/observer’),后两个相当于调用Mage::getModel(‘googleanalytics/observer’),在执行完成googleanalytics_order_success侦听后,会接着执行recordOrderPoints侦听者.

事件数据的传输。

现在假设在上面的的事件发布中传过去了一些数据:

Mage::dispatchEvent(‘checkout_onepage_controller_success_action’,
array(‘controller_action’=>$this,’bbbb’=>’hello bb’));

事件发布时的参数是一个数组。观察者接收数据

public function order_success_page_view($observer){
$event = $observer->getEvent();
$controllerAction = $event->getControllerAction();
$bbbb = $event->getBbbb();
}

$observer这个参数据我们不要动,注意第一个字母转为了大写,并_后的a被转为大写,并把_去掉了.



顶一下
(0)
100%

订阅分享回复

踩一下
(0)
100%

如果您觉得此内容对您有价值,可以点击上面的按钮支持作者,谢谢^_^
14,751 次

发表于分类: magento, PHP相关

发表一下您的观点: (1) →

1 Comment

  1. 一月 27, 2015

    博主辛苦了, :!: :smile: 谢谢博主的无私分享!

    回复

发表评论

+=

无觅相关文章插件,快速提升流量