[关闭]
@phper 2015-04-10T08:30:38.000000Z 字数 5329 阅读 6588

php中常见的几种设计模式

php


在php完全支持面向对象之后,已经是高级的面向对象的语言了,面向对象的语言一般都会有各种设计模式,好让代码更简洁效率更高,比如java里面就有很多种模式,php的很多模式也在慢慢靠拢java。今天仔细来看一下我们平时可以用到的、或者一些开源的框架里用的最多的设计模式。

在文章最后会有每个模式的代码下载。可以参考下。

1. 单例模式

单例模式可以说是面向对象语言里最常用、也是最简单的一种模式。单例就是单个实例,单个对象的意思,就是说我们去实例化一个类的时候,不管调用多少次,都永远只有一个实例, 不会有多个,这样就节省了内存分配开支。

先简单说下单例模式的原理:将构造函数__construct设置为私有的private, 对外提供一个static静态方法比如:getInstance获得实例,在getInstance方法里来做统一判断是否有new一个实例,有的话直接返回,没有就new一个,这样就保证了不管外面调用多少次,只保证new了一次

直接上代码,举例来说明下:

  1. <?php
  2. /**
  3. * 1.单例模式 Single.php
  4. */
  5. namespace Lib;
  6. class Single {
  7. static private $instance;
  8. /**
  9. * 在单例模式下,要将构造方法设为私有的,这样在外部就不能实例化
  10. */
  11. private function __construct()
  12. {
  13. }
  14. /**
  15. * 这个是获取实例化对象的入口,是静态方法,不需要new,可以直接调用
  16. */
  17. static public function getInstance()
  18. {
  19. //判断一下$instance是否已经存在
  20. if (!isset(self::$instance)) {
  21. self::$instance = new Single();
  22. }
  23. return self::$instance;
  24. }
  25. public function get()
  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. namespace Lib;
  9. class Factory {
  10. static public function create()
  11. {
  12. /**
  13. * 在同样一个命名空间下,要这样直接调用。这样调用是会报错:Lib\FactoryTest1();
  14. * 它去找Lib\Lib\FactoryTest1.php 这个文件去了。
  15. */
  16. // return new FactoryTest1();
  17. return new FactoryTest2();
  18. }
  19. }
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * User: tonyyang
  5. * Date: 4/2 0002
  6. * Time: 15:22
  7. */
  8. namespace Lib;
  9. class FactoryTest2 {
  10. public function __construct()
  11. {
  12. }
  13. public function get()
  14. {
  15. echo 'factory get it!';
  16. }
  17. }
  1. //2.工厂模式
  2. //不用工厂模式,我在很多地方调用一个类是这样的。
  3. new Lib\FactoryTest1();
  4. new Lib\FactoryTest1();
  5. new Lib\FactoryTest1();
  6. new Lib\FactoryTest1();
  7. //用工厂模式,我不直接去new FactoryTest1这个类,我是通过Factory这个类的create方法来
  8. $test1 = Lib\Factory::create();
  9. $test1->get();
  10. //假如我现在吃饱了犯傻,将FactoryTest1的类名改成FactoryTest2了,普通的写法是这样:
  11. new Lib\FactoryTest2();
  12. new Lib\FactoryTest2();
  13. new Lib\FactoryTest2();
  14. new Lib\FactoryTest2();
  15. //用了工厂模式,还是一模一样的写法,我通过修改Factory类里的create方法这一处就可以了。
  16. $test1 = Lib\Factory::create();
  17. $test1->get();

工厂模式的第二张方式就是统一管理,通过传参的方式来实例化,最常见的DB,也可能有Mysql,也有可能有SqlServer:

  1. static public function db($dbName)
  2. {
  3. if (empty($dbName)) {
  4. return new Db\Mysql();
  5. }
  6. //选择判断
  7. switch ($dbName) {
  8. case 'mysql':
  9. return new Db\Mysql();
  10. break;
  11. case 'sql_server':
  12. return new Db\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. namespace Lib;
  8. class Register
  9. {
  10. protected static $_config;
  11. /**
  12. * 获取变量值
  13. *
  14. * @param string $name
  15. * @return string
  16. */
  17. public static function get($name)
  18. {
  19. if (self::isRegister($name)) {
  20. return self::$_config[$name];
  21. }
  22. else {
  23. return false;
  24. }
  25. }
  26. /**
  27. * 设置变量值
  28. *
  29. * @param string $name
  30. * @param string $value
  31. * @return string
  32. */
  33. public static function set($name, $value)
  34. {
  35. if (!isset($name) || !isset($value)) {
  36. return false;
  37. }
  38. self::$_config[$name] = $value;
  39. return self::$_config[$name];
  40. }
  41. /**
  42. * 判断变量是否注册
  43. *
  44. * @param string $name
  45. * @return bool
  46. */
  47. public static function 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', new Lib\Db\Mysql());
  5. $register->set('sql_server', new Lib\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', new Lib\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', new Lib\Db\MySQL());
  13. $register->set('mysqli', new Lib\Db\MySQLi());
  14. $register->set('pdo', new Lib\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. 代理模式

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注