欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  后端开发

Openbiz 实现 PHP的元数据编程

程序员文章站 2022-04-30 23:29:25
...
元数据(Metadata)编程思想源于Java这种高级语言,简单的说就是将业务逻辑与实现代码进行分离,仅用XML这类的描述性语言描述业务之间的映射关系,不需要写实现代码即完成编程。

源于Java的元数据编程特性

原数据是软件架构方面的先进技术之一,让你可以编写更少的代码实现更多的事情,将业务逻辑的重用性发挥到了极致。也许听起来有些抽象,让我们来看一个实际的例子来帮你理解我主张的元数据编程理念。

Openbiz架构让 PHP的元数据编程变为可能

对于脚本级的PHP语言,现在已经成为web开发的主流语言之一。但由于他的出身是一个面向过程的编程语言(我说的是php3,有铁锹那年的事儿了),并不像Python或者Ruby这样的语言,上来就是为了对象而生的极致对象化语言。

基于这个简单的背景,也许还有PHP本身开源免费的原因,我们看到针对php语言的高级扩展相对于.Net 、Java、Objective C这样的商业化语言总是滞后一步。当PHP5发布时高喊"哥们儿 如今已经面向对象了!" Java,.Net和Cocoa问他"你有元数据编程概念么? 你有UI级别的可重用控件么?Zend怎么还没给你穿上衣服?回去玩儿铁锹吧"

Zend 框架的发展路线始终热衷于底层逻辑代码重用,比如zend_cache, zend_mail, zend_gdata , zend_table 确实实现了大量的底层逻辑的重用,解决了很多微妙复杂的问题。

这里我们积极认可Zend作出的贡献,不过"大哥您到多往前迈一步啊!",不!咱们大哥就喜欢干粗活,不喜欢搞文艺。

最后特别幽默,经过Zend不懈的努力,大哥终于把一件复杂的事情(PHP)抽象为另一件复杂的事情了(Zend框架)。你数数zend的API接口并不比php本身的extension简单多少。

在此不得不提到 伊利诺伊大学毕业的硕士高才生,曾任摩托罗拉的技术主管,美籍华人Rocky Swen,(本人一直是他技术思想的追随者),早在2003年,php4那个年代,他提出了让PHP基于元数据,不用写代码即实现编程这一概念,并有了Openbiz框架的雏形,经过了9年的完善到了今天。

这个理念当时就让我眼前一亮,你看看Zend, CodeIgniter,CakePHP 无一例外属于继承式框架 相当于一组可重用的代码库,Openbiz框架特别之处在于这是一个解释型框架,相当于"编译器"的角色。 当其它开发环境和框架致力于让开发人员少写代码的时候,Rocky兄 提出,别让他们写代码了直接用简单XML语言来描述映射关系即完成编程。

让我们来比较一下这几段程序

以订单管理的数据对象为例

PHP传统的数据对象写法

Class OrderDO

{

protected $m_id;

protected $m_client;

protected $m_product;

protected $m_price;

protected $m_timestamp;

public function create(){ … }

public function delete(){ … }

public function update(){ … }

public function search(){ … }

public function list(){ … }

}

PHP基于Openbiz的元数据写法

太棒了!这让我看到了两种未来的可能性,如果编程的主体工作是基于一种描述性的XML语言完成的,那么以后极有可能出现完全所见即所式的编程方式。

另一种极致可能是下文提到的对象工厂概念,自动化编程。

对象工厂概念,一个会写程序的程序!

每次提到这个概念都让我激动不已,仿佛我们距离智能化编程只有咫尺之遥。这个理念据我所知最先提出的是.Net的自省(这个汉语翻译很诡异)这一概念,即由主程序动态创建出另一个独立的子程序,动态编译,然后按需装载及销毁(跟变形金刚似的),当时看的我也十分激动,此后这个概念基本上就再也没人提了。

直到后来我阅读分析过了Openbiz的底层源代码惊人地发现了基于PHP实现的对象工厂这一理念。剖析一下思路,以数据对象为例:

基于XML的元数据文件被视为发给"工厂"的装配单,上面描述了应具体如何"组装"这个对象,以及这个对象与地层数据库的映射关系,与同层级的其它对象的映射关系(例如一对多的ORM)

对象工厂接到创建这样对象的生产指令后,按描述创建并组装所需对象,并以串行化的方式将对象体和状态缓存在系统内,为再次触发调用,而优化性能。直到元数据配置文件改变之前,对象只需要动态生产一次,即无限次使用。

基于这种编程逻辑,我们解决一个常见的修改和扩展问题。

例如:客户经常会再项目验收时提出底地层数据字段的修改,"您看联系人管理这个模块,能不能再增加个 生日 和 喜好 字段,要不然这尾款恐怕..."。

怎么办?

改吧。 增删读写(CRUD),列取(List),搜索(Search)一个不能少全都要改。

谁改?

肯定你改啊,因为是你写程序。

Openbiz元数据就不一样了,现在我只修改一个数据描述文件,然后是对象工厂会检测到元数据配置文件发生改变,然后他来自动重新编写对象和所有与其相关的映射调用(ORM)。

当你面对的是一个业务偶合性特别复杂的系统时,你会发现这些上层对象"你中有我,我中有你"堆叠式调用复杂至极(恶心至极)。比如在文档修改记录的视图中也调用了联系人的这几个字段等,你确定能一个不差的修改遍与这个数据结构的每一个角落么?

此非人力所能为也!但对象工厂可以,因为是按需生产创建。

PHP语言自身对对象的处理还存在这样一个缺陷,对象的生命期是不能跨视图的,当一个页面请求被执行完毕,与其相关所有资源就都被自动销毁回收了。特别是数据对象这种通常会带有数据库链接和游标状态的对象。

Openbiz的会话管理机制配合对像工厂的实现可持久的对象状态。

对象工厂会在交付对象前调用抽象类的MetaObject::setSessionData()接口来自动还原对象状态。

这样在编写程序的时候,我们在整个用户会话期内可以跨视图去调用用户曾经输入过的数据。例如将用户曾经输入过的信息作为默认值显示在当前这个视图上。

在php中的调用语法范例:

$defaultValue =

BizSystem::getObject("package.do.DataObjName")

->getActiveRecord()

->fieldName;

在元数据中通过Openbiz的SimpleExpression的语法范例

DefaultValue="{@package.do.DataObjName[fieldName].Value}"

/>

不仅描述数据映射,基于Openbiz元数据描述业务逻辑

如果你已经对这种元数据编程方式有兴趣了,请继续往下看。

出了数据对象与数据表之间的关联映射可以被元数据化,还有什么可以元数据化?

MVC的三层结构,UI也可以元数据化,VIew和Form这些UI级元素同样可以通过XML语言来描述装载与触发关系,例如

这个View应该装载哪几个Form?

这个编辑Form上的文本控件应该绑定到哪个数据对象的哪个字段上?

当按下这个按钮的时候,应当触发哪个类的哪个方法?

我们想象增删读改(CRUD)这些常用逻辑框架底层能实现,如果我的需求是当客户下完订单后,自动发邮件通知我,并且发短信通知配货部门。这样的相对复杂业务逻辑,如何元数据化实现?

数据对象触发器 和 可配置的插入式服务

这个邮件和短信的触发肯定不应该在UI层实现,因为我们要考虑不管订单从何处被生成,都应触发发送邮件这个逻辑。所以这个业务逻辑应该被耦合在数据对象上,即只要有订单被生成就应当触发该逻辑。

而发邮件和发短信些种常见的可重用性逻辑,可以被定义为pluginService,例如在发邮件的Service中,收件人,标题,内容应当是API的参数,而发邮件的帐户,SMTP服务器信息相对于业务整个系统来说通常变化不大,应作为元数据接口,而如何与服务器链接来发送邮件则是具体被重用的对象逻辑了。这种设计的精妙之处我们将在下一篇文章中具体给大家分析。

这样实现刚才说的逻辑,我只需要创建一个

OrderDO_Trigger.xml的元数据文件

当"INSERT" 事件发生的时候,调用发邮件服务,收件人为用户的邮箱,邮件内容模板为OrderConfirmEmail

到现在为止,一行代码都没写就把这个典型的业务逻辑用元数据的方式描述了出来。而用传统开发方式,处理这样的问题,150行以上代码的工作开销是怎么也避免不了的。而且可读性还不一定这样清晰。

说到这里,本人太喜欢Openbiz这只飞鸟(我是说他的Logo)了,感觉像是给PHP插上的翅膀。

现在我们PHPer可以对Java说,PHP现在能描述业务逻辑,你Java无非只能是数据映射和配置信息吧。

Openbiz 元数据编程 与 Zend命名规则匹配编程 的比较

本文介绍的Openbiz这种元数据思想并非唯一,如果单纯就数据对象的抽象化而言,还有一种不得不提的先进模式,叫命名规则匹配模式。这种也是Java的一种扩展。翻译成php实现方法,例如:

$obj = new stdDataObj($tableName);

$obj->name='ABC';

$obj->attribute_1 = 123;

$obj->attribute_2 = 456;

$obj->save();

我并不需要在使用前定义这个对象的结构,而是随需要而创建,反正保存数据的时候让它自己去自动与数据库匹配去。 但貌似这种逻辑的扩展性只限对数据逻辑,扩展逻辑还要用传统的定义声明方式。

重要的这还是不能将开发人员的主要工作从代码中解脱出来。

本文以Openbiz框架为例,让我们看到了编程的另一种可能性。

从如何简化代码,到极致的尽量不写代码,而只描述业务逻辑。

这才是面向对象思想中最大化重用的精髓。