小二所在的公司最近出了不少線上bug,痛定思痛,因而老大們紛紛決定落實code review機制...
很走運,C哥負責review小二消息中心的代碼php
"小二,咱們開始吧,讓我看看前幾天你寫的代碼"。C哥微笑道。
"好的,C哥!"設計模式
小二熟練的打開電腦,找到消息中心的代碼。spa
"C哥,這是你以前告訴我用的橋接模式寫的!"
"嗯,寫的不錯,這樣抽象與實現就能各自獨立的變化了。"設計
"等等,小二,讓我看看你這段代碼是怎麼回事?"
"稍等,C哥,我放大一些。"code
小二將鼠標聚焦在這段代碼上...對象
switch ($mes_type){ case 'Sms': $obj = new SmsMessage(); break; case 'Email': $obj = new EmailMessage(); break; default: throw new Exception('NO Message Type Found'); } $obj->send();
"小二,你講講這段代碼的邏輯。"
"好的,C哥,這段代碼是調用端的代碼,根據不一樣的消息類型($mes_type
),實例化不一樣的消息類。好比消息類型傳入Sms
的時候,這時候就會實例化SmsMessage
類。"blog
"哦,我知道了。好一段 switch...case...,但這段代碼有問題!"
"嗯?什麼問題啊?"小二好奇的問道開發
"好比我如今新增一種消息類 AppPushMessage
,你想一想,你調用端的代碼是否是要改啊?"
"的確是,switch...case...這塊要變更。"rem
"對嘛,我只是新增了消息類型,不該該對調用端的代碼產生影響!"
"是,C哥。有沒有什麼辦法能夠解決這個問題呢?"get
C哥微微笑道:"辦法是有的,還不止一種呢!"
小二雙手抱拳:"請C哥不吝賜教!"
"小二,你以爲剛纔問題的本質是什麼?"
"嗯...想不出來..."
"好吧,不難爲你了。問題的本質是:調用端負責了太多的事情!"
"哦哦,違背了'單一職責'原則?"
"是啊,調用端只是負責實質的調用,發送出實質的消息而已。這纔是他的職責。"
"對!我明白了,我代碼裏調用端既負責調用相關的消息類完成發送消息,又負責根據不一樣的參數實例化不一樣的消息類。他的責任究竟是負責調用發送呢?仍是負責實例化不一樣的消息類呢?責任不明確,因此會產生耦合性的問題!"
"嗯嗯,小二悟性長進很多啊!"
"哈哈,多蒙C哥指教!"
"C哥,咱們知道了問題的本質。怎麼解決呢?"
"好,下面咱們就用簡單工廠模式來解決。"
"簡單工廠模式?我好像據說過。"
"簡單工廠,用的人挺多的,但不屬於23種GOF設計模式之一。"
"哦哦,這樣啊。"
"你看,上面的代碼利用簡單工廠能夠改寫一下。"
/****************File:MessageFactory.php*******************/ <?php class MessageFactory{ public static function get_instance($mes_type){ switch ($mes_type){ case 'Sms': $obj = new SmsMessage(); break; case 'Email': $obj = new EmailMessage(); break; default: throw new Exception('NO Message Type Found'); } return $obj; } }
/********************File:Client.php**********************/ class Client{ public function main(){ $obj=MessageFactory::get_instance($mes_type); $obj->send(); } }
"你看看,調用端只負責調用消息類進行發送。而具體實例化哪一個消息類,這就不是調用端關心的了。"
"對,是啊!調用端只須要向簡單工廠發送請求,簡單工廠就返回相應實例化好的對象。"
"這樣,調用端負責實際的消息發送,簡單工廠負責製造(實例化)相應的消息對象。他們的職責分明!"
"對,像您剛纔說的,我再增長一種消息AppPush
,我也不用改調用端的代碼了!"
"小二,還記不記得我剛纔說的,解決上面問題的辦法不止一種。"
"嗯嗯,記得記得!還有什麼好辦法嗎?"
"有的。上面的簡單工廠,你以爲有什麼缺點嗎?"
"嗯...找不出來。要實在找缺點的話,還真有一個。好比我剛纔新增了AppPush
消息類型,就要修改上面的MessageFactory
工廠類。"
"是,這樣就違背了 開放-封閉 原則。咱們應該對擴展開發,而對修改關閉。由於修改,可能會帶來意想不到的bug。"
"對,確實是。但簡單工廠確實解決了單一職責的問題,也不失爲一種好的模式。那怎麼才能既解決單一職責的問題,又不違背開閉原則呢?"
"有一種設計模式:工廠方法模式。能夠解決你說的問題。"
"太好了!C哥你能簡單介紹一下嗎?"
"首先看一下工廠方法模式的類圖吧!"
"好的,C哥!"
"看這個類圖,你能明白工廠方法模式大體的意圖嗎?"
"我看看。C哥,工廠方法模式,是否是將對象的建立,延遲到了子類中去執行?也就是每一個子類工廠去負責建立相關的對象?"
"對,這樣的話,我就不用在工廠類中寫一大堆 switch...case... 了。當出現一種新消息類的時候,我只須要擴展出一個相應的工廠類來就好了。"
"嗯嗯,明白了,這就符合開閉原則了!"
"可是,C哥,我還有一個疑問。雖然工廠方法模式符合了開閉原則,可是,我要在調用端決定使用哪一個工廠啊?"
"對,這的確是個問題。可是咱們有不少種解決的辦法:你能夠寫一個配置文件,每次去讀這個配置文件來決定使用哪一個工廠。"
"寫一個配置文件,能夠是能夠,但總以爲不優雅。"
"哈哈,有沒有據說過反射?反射也能夠解決這個問題。"
"哦哦,這樣啊!厲害!"
"小二,用工廠方法模式,你畫一下上面代碼的UML類圖。"
不一會,小二就畫出了工廠方法模式的類圖。
"不錯嘛,小二,我這裏先用反射,給你看看代碼的實現。具體反射的機制、原理,你本身去查一下吧!"
"好的,C哥!"
/*************抽象類:MessageFactory.php*******************/ <?php abstract class MessageFactory{ abstract public function get_instance(); } /*************EmailFactory.php*******************/ <?php class EmailFactory extends MessageFactory { public function get_instance() { return new EmailMessage(); } } /*************SmsFactory.php*******************/ <?php class SmsFactory extends MessageFactory { public function get_instance() { return new SmsMessage(); } } /*************調用端:Client.php***************/ <?php class Client{ public function main($mes_type){ //利用反射,消除調用端的邏輯判斷 $reflection=new ReflectionClass($mes_type.'Factory'); $factory=$reflection->newInstance(); $mes_obj=$factory->get_instance(); $mes_obj->send(); } }
"哇塞!C哥太棒了。這解決辦法很是好!"
"不能說很是好,但解決了問題。"
不知不覺中,2個小時過去了,code review也接近尾聲了。
小二望向窗外,看着天邊的雲彩慢悠悠的飄着,想一想本身剛學到的設計模式,嘴角不自覺的露出了微笑,coder的快樂,或許就是這麼簡單...
轉載聲明:本文轉載自「聊聊代碼」,搜索「talkpoem」便可關注。
關注「聊聊代碼」,讓咱們一塊兒聊聊「左手代碼右手詩」的事兒。