「七天自製PHP架構」第三天:PHP實現的設計模式

來源:互聯網
上載者:User

標籤:href   參數   對象   factory   存在   建構函式   綁定   val   簡單   

往期回顧:「七天自製PHP架構」第二天:模型與資料庫,點擊此處

原文地址:http://www.cnblogs.com/sweng/p/6624845.html,歡迎關註:編程老頭

為什麼要使用設計模式?

設計模式,我的理解是為了達到“可複用”這個目標,而設計的一套相互協作的類。

感興趣的讀者可以閱讀《Design Patterns: Elements of Reusable Object-Oriented Software》,四位作者(Gang of Four)在書中列舉了業界聞名的23種設計模式。

這裡先介紹我們架構要涉及的三種設計模式。

單例模式(singleton)

單例模式可以保證一個類只有一個對象執行個體, 常用在資料庫存取類,從而節省硬體資源的消耗。

這裡,我們改寫上一章節的MySQL類

123456789101112131415161718192021222324252627282930313233343536373839404142 class MySQL extends DB{    private static $instance=null;    public static function getInstance(){        if(self::$instance==null){            self::$instance=new MySQL();        }        return self::$instance;    }    public function MySQL(){                 /*Config*/        $this->IP=‘*‘;        $this->ServerID=‘*‘;        $this->ServerPassword=‘*‘;        $this->DataBaseName=‘*‘;        /*End of Config*/                 $this->connection=mysqli_connect($this->IP,$this->ServerID,$this->ServerPassword,$this->DataBaseName);                 if(!$this->connection){            die(‘Could not connect‘.$this->connection);        }                 mysqli_query($this->connection,‘set names utf8‘);    }    public function Execute($sql){        return mysqli_query($this->connection,$sql);    }    public function Query($sql){        $result=mysqli_query($this->connection,$sql);        $arr=array();        while($row=mysqli_fetch_array($result)){            $arr[]=$row;        }        return $arr;    }    public function Close(){        mysqli_close($this->connection);    }}

這裡要注意的是,如果執行個體化一個MySQL類,我們不再寫

1 $db=new MySQL();

而是這樣:

1 $db=MySQL::getInstance();

因為只有getInstance這個靜態函數,才能保證只調用一次MySQL類的建構函式。

單例模式是很常用的設計模式,這裡不再贅述。

面板模式(Facade)

因為命名空間的問題,面板模式可以保證一個類的諸多方法看似是“一個類提供的”,這裡我們先設計一個簡單的服務提供者類

12345 class ServiceProvider{    public function Write($arg){        echo $arg;    }}

這個類只有一個Write方法,就是把參數列印出來

然後定義一個Facade類

1234567891011121314 class Facade{    public static function getInstance($classname,$args){        return new $classname($args);    }         public static function getFacadeAccessor(){        //    }         public static function __callstatic($method,$args){        $instance=static::getInstance(static::getFacadeAccessor(),$args);        return call_user_func_array(array($instance,$method),$args);    }}

要理解這個類,我們只要關注最後一個函數,就是__callstatic魔術方法。這個方法就是Facade類型對象或者其子類在調用他自身沒有定義過的函數時,就會調用__callstatic方法,而這個方法最後調用了call_user_func_array函數,就是把任務交給提供這項服務的類去完成,同時完成參數的傳遞。

我們再寫一個Facade子類

12345 class MyFacade extends Facade{    public static function getFacadeAccessor(){        return ServiceProvider::class;    }}

這裡注意,子類實現了父類沒有具體實現的getFacadeAccessor方法,這個方法就是要告訴父類的__callstatic方法:“我作為Facade,代表的是什麼哪個類,任務就由他來實現吧”,從文法上看,只是返回了一個表示類名的字串。所以父類起初並不知道它的子類都代表著什麼“服務提供者類”,只有當子類的靜態函數被調用後,因為子類沒有該靜態函數,所以父類的__callstatic方法被啟動了。

抽象工廠(Factory)

我對抽象工廠有一個粗俗的理解:“對象與字串的對應”,也就是用一個字串就可以創造一個類的對象。這種做法主要用在兩種情況下是很方便的:

1.類名不穩定,會在項目中頻繁修改

類名修改,很多時候並不是設計者的“命名潔癖”或者“命名強迫症”導致的修改,而是在項目的不斷迭代,發覺這個類設計的不合理。如果這個類用的不頻繁,那麼改個類名只要手工做一些小的修改即可,但是如果這個類通篇存在於代碼之中(假如是資料庫類),那修改工作量就大了,當然,我們也可以對代碼檔案使用“字串替換”,但是假如一個PHP寫成的項目,PHP檔案有幾十上百個,這也是不合理的事。

2.類的設計者並不是類的使用者

類的設計者和類的使用者不是同一個開發人員,那麼記憶一個字串或許比記憶一個類名要生動的多。我們都學過電腦網路原理,都知道記憶一個網域名稱要比記憶一個IP地址要生動的多,這就是DNS解決的問題。

因為抽象工廠很多教材都有涉及,不再贅述,本文將介紹一下目前非常流行的服務容器。

我們希望整個工程項目中,DB類,Session類,FileSystem類“拿來即用”,不用每次繁瑣的初始化,比如寫$db=new DB(arg1,arg2);這類語句,也希望DB等類型的對象像一個“全域”變數一般,在整個程式運行期間,隨時可以調用。

服務容器可以讓調用DB等類型的程式員不用知道這個類太多的細節,甚至可以用一個字串的別名來建立這樣一個對象。

我們定義一個服務容器類

123456789 class Container{    public $bindings;    public function bind($abstract,$concrete){        $this->bindings[$abstract]=$concrete;    }    public function make($abstract,$parameters=[]){        return call_user_func_array($this->bindings[$abstract],$parameters);    }}

可以把服務容器簡單的看成一個全域變數,bind方法就是用關聯陣列把字串和建構函式做綁定。

至此,有了服務容器,我們的Model類就要做修改了

12345678910111213141516171819202122232425262728 class Model implements IModel{    public static $table;    public static $container;         public static $db;    public function __construct(){        self::$container=new Container();        self::$container->bind(‘db‘,function(){            return MySQL::getInstance();        });                 self::$db=self::$container->make(‘db‘,[]);    }         public static function get($id){        return self::where(‘id‘,$id);    }         public static function where($condition,$value){        $sql=sprintf("select * from %s where %s=‘%s‘",self::$table,$condition,$value);        return self::$db->Query($sql);    }    public static function all(){        $sql=sprintf("select * from %s",self::$table);        return self::$db->Query($sql);    }}

觀察上面代碼,我們同時用了單例模式和服務容器。

總結:如果要做一個PHP架構,應該要做好代碼的複用。設計模式一直是很多爭論的焦點,“究竟該不該使用設計模式?”,本文開始,我也努力迴避“過於糾結這個問題”,我認為,設計模式有其存在的價值,至少在具體項目中,確實在很多版本迭代中節省了工作量,提高工作效率,但是如果在一個小項目中為了“秀一下我會設計模式”而使用設計模式,就不合理了。

「七天自製PHP架構」第三天:PHP實現的設計模式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.