设为首页 收藏本站
查看: 511|回复: 0

[经验分享] PHP沉思录之二:PME模型

[复制链接]

尚未签到

发表于 2017-3-31 09:55:35 | 显示全部楼层 |阅读模式
PME模型
        在大规模的程序设计中,组件(component)已经成为一种非常流行的技术。常见的组件技术都基于PME模型,即属性(Property)、方法(Method)和事件(Event)。基于PME的组件技术可以方便地实现IoC(Inversion of Control,控制反转),是从IDE的plugin到应用服务器的“热发布”等许多技术的基础。
        PHP从版本5开始,大大完善了对OO的支持,以前不能被应用的许多pattern现在都可以在PHP5中实现。因此,是否能够实现基于PHP的组件技术,也就成了一个值得讨论的问题。
        下面对PHP对于PME模型的支持,逐一进行讨论:
1.属性(Property)
PHP 并不支持类似Delphi或者C#的property语法,但这并不是问题。Java也不支持property语法,但是通过getXXX()和 setXXX()的命名约定,同样可以支持属性。PHP也可以通过这一方式来支持属性。但是,PHP提供了另一种也许更好的方法,那就是__set()和 __get()方法。
在PHP中,每一个class都会自动继承__set()和__get()方法。它们的定义如下:

void __set ( string name, mixed value )
mixed __get ( string name )

这两个方法将在下列情况下被触发:当程序访问一个当前类没有显式定义的属性时。在这个时候,被访问的属性名称作为参数被传入相应的方法。任何类都可以重载__set()和__get()方法,以实现自己的功能。
如下例:

class PropertyTester {
public function __get($PropName) {
echo "Getting Property $PropName\n";
}
public function __set($PropName, $Value) {
echo "Setting Property $PropName to '$Value'\n";
}
}
$Prop = new PropertyTester();
$Prop->Name;
$Prop->Name = "some string";

类 PropertyTester重载了__set()和__get()方法,为了测试,仅仅将参数打印输出,没有做更多的工作。测试代码创建了 PropertyTester类的实例,并试图读写它并不存在的一个属性Name。此时,__set()和__get()相继被调用,并打印出相关参数。它的输出结果如下:
Getting Property Name
Setting Property Name to 'some string'
基于这种机制,我们可以将属性的值放在一个private的List中,在读写属性时,通过重载__set()和__get()方法,读写List中的属性值。
但是,__set()和__get()方法的有趣之处远不止及。通过这两个方法,可以实现动态属性,也就是不在程序中显式定义,而是在运行时动态生成的属性。只要想想这种技术在OR Mapping中的作用就能够明白它的重要性了。配合__call()方法(用于实现动态方法,在下一节中详述),它能够取代丑陋的代码生成器(code generator)的大部分功能。
2.方法(Method)
       PHP对方法的支持比较简单,没有太多可以讨论的。值得一提的是,PHP从版本5开始支持类的静态方法(static method),这使得程序员再也不用无谓地增加许多全局函数了。
3.事件(Event)
        事件也许是PHP遇到的最复杂的问题。PHP并没有在语法层面提供对事件的支持,我们只能考虑通过别的途径来实现。因此,我们需要先对事件的概念和其他语言对事件的实现方式进行讨论。
        事件模型可以简述如下:充当事件触发者的代码本身并不处理事件,而仅仅是在事件发生时,把程序控制权转交给事件的处理者,在事件处理完成后,再收回控制权。事件触发者本身并不知道事件将会被如何处理,在大多数情况下,事件触发者的代码要先于事件处理者的代码被完成。
        在传统的面向过程的语言(例如C或者PASCAL)中,事件可以通过函数指针来实现。具体来说,事件触发者定义一个函数指针,这个函数指针可以在以后被指向某个处理事件的函数。在事件发生时,调用该函数指针指向的处理函数,并将事件的上下文作为参数传入。处理完成后,控制权再回到事件触发者。
        在面向对象的语言中,方法指针(指向某个类的方法的指针)取代了函数指针。以Delphi为例,事件处理的例子如下:
type
TNotifyEvent = procedure(Sender: TObject) of object;
TMainForm = class(TForm)
procedure ButtonClick(Sender: TObject);

End;
Var
MainForm: TMainForm;
OnClick: TNotifyEvent;

       可以看出,TNotifyEvent被定义为所谓的过程类型(Procedural Type),事实上就是一个方法指针。TMainForm的ButtonClick方法是一个事件处理者,符合TNotifyEvent的签名。 OnClick是一个事件触发者。在实际使用时,通过如下代码:
OnClick := MainForm.ButtonClick;
        将MainForm.ButtonClick方法绑定到了OnClick事件。当OnClick事件触发时,MainForm.ButtonClick方法将被调用,并且将Sender(触发事件的组件对象)作为参数传入。
        回到PHP,由于PHP不支持指针,因此无法使用函数指针这一技术。但是,PHP支持所谓的“函数变量”,可以把函数赋予某个变量,其作用类似于函数指针。如下例:

function EventHandler($Sender) {
echo "Calling EventHandler(), arv = $Sender\n";
}
$Func = 'EventHandler';
$Func('Sender Name');


        由于PHP是一种动态语言,变量可以为任何类型,所以无须先定义函数指针的类型作为事件的签名。直接定义了一个函数EventHandler作为事件处理者,然后将它赋予变量$Func(注意直接使用了字符串形式的函数名),最后触发该事件,并将一个字符串“Sender Name”传给它作为参数。输出的结果是:
Calling EventHandler(), arv = Sender Name
同样地,PHP也提供了类似方法指针的机制。如下例:

<?php
Class EventHandler {
public function DoEvent($Sender) {
echo "Calling EventHandler.DoEvent(), arg = $Sender";
}
}
$EventHanler = new EventHandler();
$HandlerObject = $EventHanler;
$Method = 'DoEvent';
$HandlerObject->$Method('Sender Name');


由于PHP中没有能够直接引用对象方法的变量,因此需要使用两个变量来间接实现:$HandlerObject指向对象,$Method指向对象方法。通过$HandlerObject->$Method方式的调用,可以动态地指向任何对象方法。
为了使代码更加优雅和更适合复用,可以定义一个专门的类NotifyEvent,并使用一段新的调用代码:

<?php
final class NotifyEvent {
private $HandlerObject;
private $Method;
public function __construct($HandlerObject, $Method) {
$this->HandlerObject = $HandlerObject;
$this->Method = $Method;
}
public function Call($Sender) {
$Method = $this->Method;
$this->HandlerObject->$Method($Sender);
}
}
$EventHanler = new EventHandler();
$NotifyEvent = new NotifyEvent($EventHanler, 'DoEvent');
$NotifyEvent->Call('Sender Name');


NotifyEvent类定义了两个私有变量$HandlerObject和$Method,分别指向事件处理者对象和处理方法。在构造函数中对这两个变量赋值,再通过Call方法来调用。
熟悉C#的读者可以发现,NotifyEvent类与C#中的Delegate十分类似。Delegate超过NotifyEvent的地方在于支持多播(Multicast),也就是一个事件可以绑定多个事件处理者。只要事件触发者自己维护一个NotifyEvent对象数组,支持多播也不是一件难事。
至此,PHP对事件的支持已经得到了比较圆满的解决。但是,人的求知欲是无穷无尽的。还有没有可能通过其他的方式来实现事件呢?
除了方法指针,接口(interface)也可以用于实现事件。在Java中,这种技术被广泛应用。其核心思想是,将事件处理者的处理函数定义抽象为一个接口(相当于函数指针的签名),事件触发者针对这个接口编程,事件处理者则实现这个接口。
这种方式的好处在于,不需要语言支持函数指针或方法指针,让代码显得更加清晰和优雅,缺陷在于,实现同一种功能,要使用更多的代码。如下例:

<?php
interface IEventHandler {
public function DoEvent($Sender, $Arg);
}
class EventHanlerAdapter implements IEventHandler {
public function DoEvent($Sender, $Arg) {
echo "Calling EventHanlerAdapter.DoEvent(), Sender = $Sender, arg = $Argn";
}
}
class EventRaiser {
private $EventHanlerVar;
public function __construct($EventHanlerAdapter) {
$this->EventHanlerVar = $EventHanlerAdapter;
}
public function RaiseEvent() {
if ($this->EventHanlerVar != null) {
$this->EventHanlerVar->DoEvent($this, 'some string');
}
}
public function __tostring() {
return 'Object EventRaier';
}
}
$EventHanlerAdapter = new EventHanlerAdapter();
$EventRaiser = new EventRaiser($EventHanlerAdapter);
$EventRaiser->RaiseEvent();


首先定义了一个接口IEventHandler,它包含了方法的签名。EventHanlerAdapter类作为事件处理者,实现了这个接口,并提供了相应的处理方法。EventRaiser类作为事件触发者,针对$EventHanlerVar变量(它应该是IEventHandler接口类型,但是在 PHP中不用显式定义)编码。
在实际应用中,将EventHanlerAdapter的实例作为参数赋予传给EventRaiser类的构造函数,当事件发生时,相应的处理方法将被调用。输出结果如下:
Calling EventHanlerAdapter.DoEvent(), Sender = Object EventRaier, arg = some string
最后,让我们回到现实世界中来。虽然我们用PHP完整地实现了PME模型,但是这到底有什么用呢?毕竟,我们不会用PHP去编写IDE,也不会用它编写应用服务器。回答是,基于PME模型的组件技术可以实现更加方便和更大规模的代码复用。
在基于PHP的应用系统中,虽然插件已经被广泛使用,但是通过组件技术可以实现功能更强大、更加规范和更容易维护的插件。此外,组件技术在实现一些大的Framework(例如,针对Web UI的Framework)时,也是不可或缺的。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-357979-1-1.html 上篇帖子: mac下php编程出现php [function.move-uploaded-file]failed to open stream 下篇帖子: 三个小时学懂php基础。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表