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] 我個人比較笨,基礎又差,活又多所以能偷就偷,模式這個東西比較合適,前人的經驗都在里面,遇到問題你只要照貓畫虎套用就好了,比較適合我這樣的懶人用。 這次多少也要來個素的。
|
原文轉自:http://www.anti-gravitydesign.com |