cnw168 回复于:2004-06-11 09:26:07 | ||||||||||||||||||||||||||||
没有听懂,可以说的再细点吗。:) | ||||||||||||||||||||||||||||
dualface 回复于:2004-06-11 22:35:02 | ||||||||||||||||||||||||||||
用C++或者Java来试验吧,php4的oo能力还达不到那个程度。 | ||||||||||||||||||||||||||||
longnetpro 回复于:2004-06-12 00:20:16 | ||||||||||||||||||||||||||||
基本上是可以的。
[code:1:77fd787f2b] class ClassFactory { function &forClass($className){ if (class_exists($className)){ $obj = new $className; }else{ $obj = null; // or error handler here } return $obj; } } // Create an object which class type is 'MyClass' $className = 'MyClass'; $obj =& ClassFactory::forClass($className); if ($obj === null) echo 'Failed to create class '.$className.' object.'; else echo 'Create class'.$className.' object suclearcase/" target="_blank" >ccessfully'; [/code:1:77fd787f2b] 基本上这就是类工厂模式最简单的实现。另外,关于类的设计模式,并不一定代码都完全一样或是在形式上一样,只要能实现功能就可以了。PHP4中的变量无类型及变量的变量的特性,如果灵活运用并运用得当的话,基本可以实现JAVA可以实现的设计模式,不过没有必要一定要追求代码形式一样。其实有时用PHP4实现的设计模式比用JAVA实现起来更容易,因为PHP没有JAVA那么严格。 其实对什么设计模式,我认为没有必要太过拘泥,针对不同的使用环境,用不同的方法,实现的过程也不尽相同。设计模式只不过是一种思想,没有什么。但如果把这些思想太过条条框框了的话,反而感觉很别扭。JAVA是很严谨的,但我总觉得在某些方面它做得太过烦琐,虽然很规范但有时又过于严格,对做某些应用来说反而不是很好。PHP4就与JAVA的特性有些相反,PHP5会好多了。但是,只要一种语言支持OO的大部分功能,OO的各种设计模式从理论上都能够用这种语言实现(模式与编程语言无关)。因此PHP4可以基本实现各种设计模式,只是说实现的程度可能不够深或是不完全。但从表面上,无论是模拟还是真实的,总之还是能实现的——比如在CU上,我很早还发过一帖,模拟OO的单子模式(singleton),比用JAVA的还要简单,但模式特性也实现得足够完全。而Factory模式,虽然用PHP代码不能象JAVA那样(PHP4的reflection功能还不强),但基本可以用我上面的代码进行模拟,功能可能是差一点,但总之是模拟成功了,在应用中,很可能也只需要用到我模拟的这个程度,如果想要更复杂的实现,可以另外运用PHP的灵活性来实现——当然这个实现需要根据需求,也需要程序编制者对设计模式有深刻的理解以及对PHP熟练地掌握以及有足够的编程水平与技巧。但不管怎么说,设计模式用PHP是可以实现的,在PHP4下也可以。 | ||||||||||||||||||||||||||||
shukebeita 回复于:2004-06-12 01:14:09 | ||||||||||||||||||||||||||||
方向是正确。我来试一试看看能不能说清楚。Factory Method 是面向对象程序设计中一个重要的模式(什么是模式?是前人总结的一系列解决问题方法,就像武术套路拳法心诀。我们在这里经常讨论的多数问题例如如何设置apache,如何发送邮件了,用不用模板了等等都属于一些简单的擒拿散手,收拾个地痞流氓还可以,真要遇到大家伙来踢馆没有成路数的真功夫要想拿下的确很难,这时就需要经半年修炼而成的“模式”了。楼主你也别着急,为了让更多的朋友能够理解我们在说的什么,我先介绍一下Factory method,你问题的答案在最后,如果等不及你可以直接看答案。)
还是通过例子来看一看,很多讲OOP的书都是用java或者C++来举例,而且经常用形状啦,水果啦比例子,那些和我们在实际中的应用距离太远。这次我用php中经常要用到的表单处理来举个例子,说明一下。这个例子不是我发明的,我也是从别人那里学来的具体在哪里记不清了,在此对于写出这个例子的原作者表示感谢。 html表单中有一个很麻烦的问题就是验证问题。所以我想试试看用OOP的方法能不能简单一点。 首先我们来构造一个处理表单的类叫Form,它包装了一些通用的表单操作,其中的两个基本操作是验证和数据更新. [code:1:69bf0df2d8] class Form { function isValid() { //表单的验证工作 } function doUpdate() { //通过验证的表单的操作,比如发送邮件,更新数据库 } } [/code:1:69bf0df2d8] 这个Form类是一个父类,具体的实现要通过扩展它来实现,比如有一个会员注册的表单可以这样扩展: [code:1:69bf0df2d8] class RegistrationForm extends Form { //... } [/code:1:69bf0df2d8] 类是用来封装功能的,所以不要太追究它里面有什么,也不要问我到底怎么实现的,我现在还不知道,(厉害吧?oop的程序可以这样写,什么都不知道就可以写了.) 好了,现在我们来看看怎么用. [code:1:69bf0df2d8] $regForm = new RegistrationForm(); if ($regForm->isValid()) //验证数据无误 { $regForm->doUpdate();//进行这个表单的相关操作 } else { echo $regForm->errorMessage();//显示错误信息 } [/code:1:69bf0df2d8] 现在可以再进一步,每个表单都有很多的域或者说字段英文叫做(field)我们要对每个字段进行验证通过,才可以认为这个表单里的数据是合法的。所以我们需要充实一下这个Form类实现这个功能。增加3个属性,和一些功能。 [code:1:69bf0df2d8] class Form { var $fields; //数组包括了表单中所有的字段 var $valid; //boolean 是否合法 var $error; //错误信息 function isValid() { return $this->valid; } /** * 从数据源中获得表单数据,在使用中这个$source可以是$_GET 也可以是 $_POST * 验证的功能实际上在这里实现了。 */ function post(&$source) { $this->valid = true; foreach($this->fields as $name=>$val) { $refField = & $this->fields[$name]; // by reference if(!isset($source[$refField->getName()])) { if ($refField->isNullAllowed()) { $this->error .= 'Required field missing: ' . $refField->getName(); $this->valid = false; } } else { $refField->setValue($source[$refField->getName()]); if (!$refField->isValid()) { $this->error .= 'Invalid data for field: ' . $refField->getName(); $this->valid = false; } } } return $this->valid; } function doUpdate() { //合法表单的操作,比如发送邮件,更新数据库 die('Should be implemented in my children'); } } [/code:1:69bf0df2d8] 注意Form中的 fields 成员 它是一个数组, 数组的每个元素都是一个字段对象 Field。这里看看新的对象Field的内部结构,其实它也是非常简单的。 [code:1:69bf0df2d8] class Field { var $name; var $nullAllowed; var $value; var $isset; var $maxLength; function Field($name,$nullAllowed,$maxLength) { $this->name = $name; $this->nullAllowed = $nullAllowed; $this->value = ''; $this->isset = false; $this->maxLength = $maxLength; } function setValue(&$value) { $this->value = $value; $this->isset = true; } function isValid() { if (!$this->hasValue() || $this->isEmpty()) { return $this->nullAllowed; } return true; } function isEmpty() { return empty($this->value); //return isset($this->value)?false:true; } function hasValue() { return $this->isset; } function &getValue() { return $this->value; } function getName() { return $this->name; } function isNullAllowed() { return $this->nullAllowed; } } [/code:1:69bf0df2d8] 这个不用多解释了吧,它包装了对于一般字段的一些通用操作,获得字段名,获得字段值等等。我们不能直接来使用它,我们需要根据字段的不同要求来扩展它,比如我们需要Email字段,数字字段,日期字段等等,就要对它进行扩展然后重写一下验证规则就好了。 下面是一些简单的例子: [code:1:69bf0df2d8] class EmailField extends Field { function isValid() { if (parent::isValid()) { if (!$this->isEmpty()) { return $this->checkEmail(); } return true; } return false; } function checkEmail() { return eregi("^[^@ ]+@[^@ ]+\.[^@ \.]+$",$this->getValue()); } } class PhoneField extends Field { function isValid() { if (parent::isValid()) { if (!$this->isEmpty()) { return $this->checkPhone(); } return true; } return false; } function checkPhone() { return eregi("^[0-9\\(\\)\+-]{1,20}$",$this->getValue()); } } class TextField extends Field { function isValid() { if (parent::isValid()) { if (!$this->isEmpty()) { return $this->checkText(); } return true; } return false; } function checkText() { //echo('debuger in textfield'); return true; } } [/code:1:69bf0df2d8] 字段类我们有了,需要在表单对象中使用字段对象我们还要为Form类增加一个新的方法。先来看看我们最终希望怎样来使用这个RegistrationForm类. [code:1:69bf0df2d8] $regForm = new RegistrationForm(); $regForm->addField('userName','text',false,64); $regForm->addField('userPhone','phone'false,10); $regForm->addField('userEmail','email',false,256); if ($regForm->post($_POST)) //把$_POST过来的值放到表单对象中并且进行验证 { $regForm->doUpdate();//进行这个表单的相关操作 } else { echo $regForm->errorMessage();//显示错误信息 } [/code:1:69bf0df2d8] 这里看到我们需要一个新的功能addField来为表单对象添加字段.所以我们仔细看一看addField怎么做的: [code:1:69bf0df2d8] function addField($name, $type, $nullAllowed,$maxLength=99) { $this->fields[$name] =& FieldFactory::createField($name, $type, $nullAllowed,$maxLength); } [/code:1:69bf0df2d8] 参数中第一个是字段名,第二个是类型,第三个字段是否可以空缺,第四个最大长度. 哈哈,写到这里今天的主角Factory Method才正式登场。 我们看看这个FieldFactory类能做些什么 [code:1:69bf0df2d8] class FieldFactory { function &createField($name, $type, $nullAllowed,$maxLength) { switch ($type) { case 'text' : $class = 'TextField'; break; case 'number': $class = 'NumberField'; break; case 'email' : $class = 'EmailField'; break; case 'phone' : case 'fax': $class = 'PhoneField'; break; default : $class = 'TextField'; break; } return new $class($name, $nullAllowed,$maxLength); } } [/code:1:69bf0df2d8] 它的功能更是简单,只有一个switch把所谓的字段类型和要用到的类对应起来,然后生成一个这个类的实例并返回给它的调用者。这里的FieldFactory 用到的就是典型的Factory Method。它就像烤面包的师傅,你要什么面包他烤给你就好了,只要你要的面包而不是包子他就能给你做得出来。但是,这么大费周折的使用 Factory Method 有什么好处呢?哪些情形下该用Factory Method? 我今天实在写太多了,胳膊都酸了还是听听大家的讨论吧。 关于楼主问题的解答 好消息和坏消息 先说坏消息,java我不会,从你提供的代码中看估计是利用了一种java中叫做reflection的机制(我猜reflection和摸骨相面差不多看到你就知道你是谁的孩子,祖上有哪些看家的本事)。但是php中根本没有没有什么forname 的东西。 好消息是factory method和反射机制没有必然关系,反射机制只是java中实现factory method的一种方法。你完全可以用更简单更容易理解的办法来实现 Factory method 比如说在配置文件里人工建立类型和类名的对应关系,然后用FieldFactory中的方法来创建这样的一个实例返回给他的调用者就可以了。这也就是我们常说的OOP是思想而不是某种特定的语言特征。 如果你真的够勤奋,喜欢孜孜不倦的探索可以看一看在目前的php中有以下一些关于class的函数,有兴趣看看能不能用它们模拟出来java的反射机制。 [code:1:69bf0df2d8] class_exists - Checks if the class has been defined get_class_methods - Returns an array of class methods' names get_class_vars - Returns an array of default properties of the class get_class - Returns the name of the class of an object get_declared_classes - Returns an array with the name of the defined classes get_object_vars - Returns an associative array of object properties get_parent_class - Retrieves the parent class name for object or class is_a - Returns TRUE if the object is of this class or has this class as one of its parents is_subclass_of - Returns TRUE if the object has this class as one of its parents method_exists - Checks if the class method exists [/code:1:69bf0df2d8] 太累了,贴不动波霸了。大伙将就着看吧。 :em06: | ||||||||||||||||||||||||||||
longnetpro 回复于:2004-06-12 02:36:23 | ||||||||||||||||||||||||||||
shuke,你何必搞得这么累呢。你那么大一段代码,前面与Factory没有太大关系,就是一两个基类七搞八搞搞出N多子类出来。后面的才是关键,主要是根据类名生成不同类型的对象,被生成对象的类名才是Factory模式中最重要的,其它的参数只是辅助作用。至于说生成了对象之后的方法调用,就不是模式的问题,而涉及到OO的重载与多态的方面了。楼主还是看我的代码比较容易理解一些,shuke的代码适合于对OO理解比较深入一点的人看。还有什么reflection机制,说实话,我认为它只是一个术语,这个术语描述一套固定的东西,PHP4的这个机制不太好(其实就是支持度不够或是不完全),但是完全可以模拟出来,我的代码可以算是其中的一个部分。shuke说的核心内容,我看了一下,与我的几乎完全一致,只是他用更为具体的例子说明原理(但这个例子对一些初学者来说似乎难了一点)。其实说白了,还是那个面包师与客户的比喻最能说明问题——只要是面包,不管是包肉的还是包豆沙的,他都能做出来给你。我的例子就更大了一点,无论是什么,只要是个东西,这个人都能做出来给你——是个更广义的Factory。JAVA中的Factory也是广义的,当然,它也可以是更小一点的Factory。
在此提一个建议,在没有搞清楚OO的本质特点,没有搞清楚OO结构之间如何协调的,没有搞清楚OO的工作原理及实现原理的情况下,一下不要上升到抽象的设计模式,因为很多概念是建立在OO本质概念的情况下的,也是利用了OO的这些本质概念。因此,如果不了解OO的本质,看设计模式还是一样的云里雾里的。至于说如何了解,大家还是去看专门讲解OO的书吧(不是专门讲解JAVA或什么语言的)。这里我仍然强力推荐“thinking in Java”这本书,虽然它是以JAVA为语言载体,但它叙述的是OO的思想,因此书名为“thinking in Java”,绝对值得反复研读(我就曾一字不漏看过三遍之多,书店里站着看的英文原版,不过太贵没买:)),最好是看英文版并有中文对照(便于理解),你专业英语好也可以不看中文,但不推荐只看中文版,因为只看中文版的可能会因翻译问题带来误解。 | ||||||||||||||||||||||||||||
sleep_meng 回复于:2004-06-12 03:48:34 | ||||||||||||||||||||||||||||
谢谢大家的帮助.
开始我也想过用这样的代码实现: function &forClass($className){ if (class_exists($className)){ $obj = new $className; } } 但以前用的语言都很注重类型,由于以前的那种编程习惯.一时没有考虑到php的无类型特性.所以老是学得$className是一个字符串变量,应该不可以进行 new $className操作. 看了大家的帮助后,终于实现了,更从大家的帖子中学到很多的东西. 谢谢大家的帮助, 非常感谢! | ||||||||||||||||||||||||||||
leaper 回复于:2004-06-12 08:39:33 | ||||||||||||||||||||||||||||
模式设计的东西一定好吗,一个本来很小的东西,通过OO+模式的方法反倒变成复杂化,我觉得未毕是件好事。 | ||||||||||||||||||||||||||||
tonera 回复于:2004-06-12 09:49:42 | ||||||||||||||||||||||||||||
这么好的贴子,占个位先。 :D
觉得longnetpro的例子简单明了。 leaper的观点有实用价值,但是如果楼主专攻技术问题的话则不适用了。一般来说,来这里(技术论坛)讨论的目的应该是“如何做”,而不是“该不该做”。 经过这段时间的熏陶,对OO的思想的理解又升一级!这是一个比较抽象的概念,以前头脑中的“方法”和“属性”的概念跟现在的完全不同。 sleep_meng 的问题也给我启示,由变量函数(这东西很好,在我的一个验证类中就是这么干的)、变量的变量,还应该举一反三,联想到有“变量类”。 BTW:shuke的那个验证类扩展性不好,如果要新增一个验证规则,就要重写或是继承。还有,当要验证的值允许为空时你那个类就不行了。还是觉得dualface的那个类(值和验证规则)比较好,如果再加上我后面补充的一个用户自定义验证规则的接口,就可以满足任何苛刻用户的需求了。 | ||||||||||||||||||||||||||||
shukebeita 回复于:2004-06-12 11:15:43 | ||||||||||||||||||||||||||||
[quote:6d64aa021e="longnetpro"]shuke,你何必搞得这么累呢。[/quote:6d64aa021e]
那个是为了回应tonera看我的表单验证类的要求,合到一起写了。我在挑灯夜战,奋笔疾书的时候谁料你偷偷跑来抢了我的沙发,发帖的时候看到你的了。但是我的已经写好了,又不能倒回肚里去 :roll: 所以就发了。完全同意你的观点(怪事,我们的观点经常是一样的。) [quote:6d64aa021e]shuke的那个验证类扩展性不好...[/quote:6d64aa021e] 那个类设计上可以允许空值,可能是老版有bug。dualface的类当然也好。总之一个原则:能让你多赚钱少干活的就是好类。 [quote:6d64aa021e]模式设计的东西一定好吗,一个本来很小的东西,通过OO+模式的方法反倒变成复杂化,我觉得未毕是件好事。 [/quote:6d64aa021e] 我个人比较笨,基础又差,活又多所以能偷就偷,模式这个东西比较合适,前人的经验都在里面,遇到问题你只要照猫画虎套用就好了,比较适合我这样的懒人用。 这次多少也要来个素的。
|
|