1. 程式人生 > >php中常見的幾種設計模式

php中常見的幾種設計模式


1. 單例模式

單例模式可以說是面嚮物件語言裡最常用、也是最簡單的一種模式。單例就是單個例項,單個物件的意思,就是說我們去例項化一個類的時候,不管呼叫多少次,都永遠只有一個例項, 不會有多個,這樣就節省了記憶體分配開支。

先簡單說下單例模式的原理:將建構函式__construct設定為私有的private, 對外提供一個static靜態方法比如:getInstance獲得例項,在getInstance方法裡來做統一判斷是否有new一個例項,有的話直接返回,沒有就new一個,這樣就保證了不管外面呼叫多少次,只保證new了一次

直接上程式碼,舉例來說明下:

  1. <?php
  2. /**
  3. * 1.單例模式 Single.php
  4. */
  5. namespaceLib;
  6. classSingle{
  7. staticprivate $instance;
  8. /**
  9. * 在單例模式下,要將構造方法設為私有的,這樣在外部就不能例項化
  10. */
  11. privatefunction __construct()
  12. {
  13. }
  14. /**
  15. * 這個是獲取例項化物件的入口,是靜態方法,不需要new,可以直接呼叫
  16. */
  17. staticpublicfunction getInstance()
  18. {
  19. //判斷一下$instance是否已經存在
  20. if(!isset(self::$instance)){
  21. self::$instance =newSingle();
  22. }
  23. returnself::$instance
    ;
  24. }
  25. publicfunctionget()
  26. {
  27. echo 'you get it!';
  28. }
  29. }

在index.php中,我們來例項化呼叫一下。

  1. define('WWWDIR', __DIR__);
  2. include WWWDIR .'/Lib/Load.php';
  3. spl_autoload_register('\\Lib\\Load::autoload');
  4. // 1. 單例模式
  5. $single =Lib\Single::getInstance();
  6. $single2 =Lib\Single::getInstance();
  7. $single->get();//you get it!
  8. $single2->
    get();//you get it!
  9. var_dump($single === $single2);//boolean true

從列印結果我們看出,$single1和$single2其實是同一個引用的,這樣就達到了我們的目的。而且也節約了記憶體。在同一個生命週期中,不管呼叫多次,永遠只有一個例項。

2. 工廠模式

先簡單說下工廠模式:當我要例項化類的時候,不直接new這個類,而是通過呼叫另一個類的一個方法來例項化。這就是工廠模式的核心原理。

這樣的好處有啥呢?

  1. 假設不使用工廠模式:比如很多地方呼叫類class_a,程式碼就會這樣子建立一個例項:new class_a(), 假設某天需要把class_a類的名子修改成class_b,意味著很多呼叫的程式碼都要修改。如果你用工廠模式,就你只需要改一處就可以了。當然這只是一個很極端的例子,沒人會吃飽了沒事幹會去修改類名。這也是工廠模式最簡單的用法。
  2. 工廠模式最多的用法,就是根據條件來建立不同的例項,比如你傳入一個mysql,我去例項化mysql類給你,你傳入sql server,那我就例項化sql server類給你。有點像switch乾的活。這樣就簡化了邏輯,統一控制,程式碼也比較簡化。

還是直接上程式碼來說明:

  1. <?php
  2. /**
  3. * 2.Factory.php 工廠模式
  4. * User: tonyyang
  5. * Date: 4/2 0002
  6. * Time: 15:19
  7. */
  8. namespaceLib;
  9. classFactory{
  10. staticpublicfunction create()
  11. {
  12. /**
  13. * 在同樣一個名稱空間下,要這樣直接呼叫。這樣呼叫是會報錯:Lib\FactoryTest1();
  14. * 它去找Lib\Lib\FactoryTest1.php 這個檔案去了。
  15. */
  16. // return new FactoryTest1();
  17. returnnewFactoryTest2();
  18. }
  19. }
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: tonyyang
  5. * Date: 4/2 0002
  6. * Time: 15:22
  7. */
  8. namespaceLib;
  9. classFactoryTest2{
  10. publicfunction __construct()
  11. {
  12. }
  13. publicfunctionget()
  14. {
  15. echo 'factory get it!';
  16. }
  17. }
  1. //2.工廠模式
  2. //不用工廠模式,我在很多地方呼叫一個類是這樣的。
  3. newLib\FactoryTest1();
  4. newLib\FactoryTest1();
  5. newLib\FactoryTest1();
  6. newLib\FactoryTest1();
  7. //用工廠模式,我不直接去new FactoryTest1這個類,我是通過Factory這個類的create方法來
  8. $test1 =Lib\Factory::create();
  9. $test1->get();
  10. //假如我現在吃飽了犯傻,將FactoryTest1的類名改成FactoryTest2了,普通的寫法是這樣:
  11. newLib\FactoryTest2();
  12. newLib\FactoryTest2();
  13. newLib\FactoryTest2();
  14. newLib\FactoryTest2();
  15. //用了工廠模式,還是一模一樣的寫法,我通過修改Factory類裡的create方法這一處就可以了。
  16. $test1 =Lib\Factory::create();
  17. $test1->get();

工廠模式的第二張方式就是統一管理,通過傳參的方式來例項化,最常見的DB,也可能有Mysql,也有可能有SqlServer:

  1. staticpublicfunction db($dbName)
  2. {
  3. if(empty($dbName)){
  4. returnnewDb\Mysql();
  5. }
  6. //選擇判斷
  7. switch($dbName){
  8. case'mysql':
  9. returnnewDb\Mysql();
  10. break;
  11. case'sql_server':
  12. returnnewDb\SqlServer();
  13. break;
  14. }
  15. }

這樣,我就可以通過引數傳遞的方式,來選擇資料庫連線了。

  1. //通過傳引數的方式來實現切換不同的資料庫例項
  2. $mysql =Lib\Factory::db('mysql');
  3. $sqlServer =Lib\Factory::db('sql_server');

3. 註冊模式

註冊模式單例模式的基礎上進一步拓展了一步,他把所有單例模式的物件全部儲存在一個索引陣列中,下次取得時候,直接去數組裡按照索引去取。這樣的好處是在程式中有條理的存放並管理物件。所以,肯定有一個存(set)和一個取(get)。一般是先去如果沒有,就重新初始化然後起來,這樣這個週期中的其他程式碼就可以直接了。和redis快取的道理是一樣的。

我先來實現註冊模式的程式碼:

  1. <?php
  2. /**
  3. * 3. 註冊模式
  4. *
  5. * @package Register.php
  6. */
  7. namespaceLib;
  8. classRegister
  9. {
  10. protectedstatic $_config;
  11. /**
  12. * 獲取變數值
  13. *
  14. * @param string $name
  15. * @return string
  16. */
  17. publicstaticfunctionget($name)
  18. {
  19. if(self::isRegister($name)){
  20. returnself::$_config[$name];
  21. }
  22. else{
  23. returnfalse;
  24. }
  25. }
  26. /**
  27. * 設定變數值
  28. *
  29. * @param string $name
  30. * @param string $value
  31. * @return string
  32. */
  33. publicstaticfunctionset($name, $value)
  34. {
  35. if(!isset($name)||!isset($value)){
  36. returnfalse;
  37. }
  38. self::$_config[$name]= $value;
  39. returnself::$_config[$name];
  40. }
  41. /**
  42. * 判斷變數是否註冊
  43. *
  44. * @param string $name
  45. * @return bool
  46. */
  47. publicstaticfunction isRegister($name)
  48. {
  49. return isset(self::$_config[$name]);
  50. }
  51. }

上面程式碼我們定義了getset方法。這樣在一個存取模式就定義好了。

在我們的專案當中,像資料庫連線redis連線配置檔案等,都是在各個地方被大量使用的,我們完全可以使用註冊模式,將這些變數物件寫到註冊器上面去。

下面我們來呼叫一下:

  1. // 3. 註冊模式
  2. $register =Lib\Register::getInstance();
  3. //資料庫連結提前寫到註冊樹上
  4. $register->set('mysql',newLib\Db\Mysql());
  5. $register->set('sql_server',newLib\Db\SqlServer());
  6. //讀取db類
  7. $register->get('mysql')->select('select * from user');
  8. $register->get('sql_server')->select('select * from user');
  9. //redis類寫到註冊樹上
  10. $register->set('redis',newLib\Cache\Redis());
  11. //讀取redis類
  12. $register->get('redis')->get('redis-cache-2012');
  13. //配置檔案寫到註冊樹上
  14. $config = array(
  15. 'db_name'=>'127.0.0.1',
  16. );
  17. $register->set('config', $config);
  18. echo $register->get('config')['db_name'];// php > 5.4

這樣既實現了單例模式,又使得所有的變數類都統一管理,實現起來更簡潔,而且程式碼效率更高,更省記憶體。

4. 介面卡模式

就是將一些截然不同的函式介面封裝成統一的API,最簡單的例子就是DB類了。我們有mysql,mysqli,pdo等。如果我們專案中同時有這幾種資料庫存在,那麼做一些操作是很蛋疼的,因為它們的都有各自額api.所以,這個時候,我們可以用介面卡模式將3種不同的資料庫封裝成統一的api, 對外就是統一的方式呼叫。再比如我們專案中可能會用到的快取有redis,memcache等,我們也同樣可以把他們封裝成統一的API介面。

我們用db類為例子來說明下。

  1. //4 . 介面卡模式
  2. $register =Lib\Register::getInstance();
  3. $config = array(
  4. 'host'=>'127.0.0.1',
  5. 'db_name'=>'test',
  6. 'user_name'=>'root',
  7. 'password'=>'',
  8. );
  9. //用註冊模式將db配置檔案註冊。
  10. $register->set('db', $config);
  11. //
  12. $register->set('mysql',newLib\Db\MySQL());
  13. $register->set('mysqli',newLib\Db\MySQLi());
  14. $register->set('pdo',newLib\Db\PDO());
  15. //api介面一模一樣
  16. $mysqlInfo = $register->get('mysql')->select('select 1+1;');
  17. $mysqliInfo = $register->get('mysqli')->select('select 1+1;');
  18. $pdoInfo = $register->get('pdo')->select('select 1+1;');
  19. var_dump($mysqlInfo, $mysqliInfo, $pdoInfo);
  20. //工廠模式來做也一樣
  21. $mysql =Lib\Factory::db('mysql');
  22. $mysqli =Lib\Factory::db('mysqli');
  23. $pdo =Lib\Factory::db('pdo');
  24. //api介面一模一樣
  25. $mysqlInfo = $mysql->select('select 1+1;');
  26. $mysqliInfo = $mysqli->select('select 1+1;');
  27. $pdoInfo = $pdo->select('select 1+1;');
  28. var_dump($mysqlInfo, $mysqliInfo, $pdoInfo);

5. 策略模式

6. 資料物件對映模式

7. 觀察者模式

8. 原型模式

9. 裝飾器模式

10. 迭代器模式

11. 代理模式