@phper
2015-04-10T08:30:38.000000Z
字数 5329
阅读 7536
php
在php完全支持面向对象之后,已经是高级的面向对象的语言了,面向对象的语言一般都会有各种设计模式,好让代码更简洁效率更高,比如java里面就有很多种模式,php的很多模式也在慢慢靠拢java。今天仔细来看一下我们平时可以用到的、或者一些开源的框架里用的最多的设计模式。
在文章最后会有每个模式的代码下载。可以参考下。
单例模式可以说是面向对象语言里最常用、也是最简单的一种模式。单例就是单个实例,单个对象的意思,就是说我们去实例化一个类的时候,不管调用多少次,都永远只有一个实例, 不会有多个,这样就节省了内存分配开支。
先简单说下单例模式的原理:将构造函数__construct设置为私有的private, 对外提供一个static静态方法比如:getInstance获得实例,在getInstance方法里来做统一判断是否有new一个实例,有的话直接返回,没有就new一个,这样就保证了不管外面调用多少次,只保证new了一次。
直接上代码,举例来说明下:
<?php/*** 1.单例模式 Single.php*/namespace Lib;class Single {static private $instance;/*** 在单例模式下,要将构造方法设为私有的,这样在外部就不能实例化*/private function __construct(){}/*** 这个是获取实例化对象的入口,是静态方法,不需要new,可以直接调用*/static public function getInstance(){//判断一下$instance是否已经存在if (!isset(self::$instance)) {self::$instance = new Single();}return self::$instance;}public function get(){echo 'you get it!';}}
在index.php中,我们来实例化调用一下。
define('WWWDIR', __DIR__);include WWWDIR . '/Lib/Load.php';spl_autoload_register('\\Lib\\Load::autoload');// 1. 单例模式$single = Lib\Single::getInstance();$single2 = Lib\Single::getInstance();$single->get(); //you get it!$single2->get(); //you get it!var_dump($single === $single2); //boolean true
从打印结果我们看出,$single1和$single2其实是同一个引用的,这样就达到了我们的目的。而且也节约了内存。在同一个生命周期中,不管调用多次,永远只有一个实例。
先简单说下工厂模式:当我要实例化类的时候,不直接new这个类,而是通过调用另一个类的一个方法来实例化。这就是工厂模式的核心原理。
这样的好处有啥呢?
class_a,代码就会这样子创建一个实例:new class_a(), 假设某天需要把class_a类的名子修改成class_b,意味着很多调用的代码都要修改。如果你用工厂模式,就你只需要改一处就可以了。当然这只是一个很极端的例子,没人会吃饱了没事干会去修改类名。这也是工厂模式最简单的用法。switch干的活。这样就简化了逻辑,统一控制,代码也比较简化。还是直接上代码来说明:
<?php/*** 2.Factory.php 工厂模式* User: tonyyang* Date: 4/2 0002* Time: 15:19*/namespace Lib;class Factory {static public function create(){/*** 在同样一个命名空间下,要这样直接调用。这样调用是会报错:Lib\FactoryTest1();* 它去找Lib\Lib\FactoryTest1.php 这个文件去了。*/// return new FactoryTest1();return new FactoryTest2();}}
<?php/*** Created by PhpStorm.* User: tonyyang* Date: 4/2 0002* Time: 15:22*/namespace Lib;class FactoryTest2 {public function __construct(){}public function get(){echo 'factory get it!';}}
//2.工厂模式//不用工厂模式,我在很多地方调用一个类是这样的。new Lib\FactoryTest1();new Lib\FactoryTest1();new Lib\FactoryTest1();new Lib\FactoryTest1();//用工厂模式,我不直接去new FactoryTest1这个类,我是通过Factory这个类的create方法来$test1 = Lib\Factory::create();$test1->get();//假如我现在吃饱了犯傻,将FactoryTest1的类名改成FactoryTest2了,普通的写法是这样:new Lib\FactoryTest2();new Lib\FactoryTest2();new Lib\FactoryTest2();new Lib\FactoryTest2();//用了工厂模式,还是一模一样的写法,我通过修改Factory类里的create方法这一处就可以了。$test1 = Lib\Factory::create();$test1->get();
工厂模式的第二张方式就是统一管理,通过传参的方式来实例化,最常见的DB,也可能有Mysql,也有可能有SqlServer:
static public function db($dbName){if (empty($dbName)) {return new Db\Mysql();}//选择判断switch ($dbName) {case 'mysql':return new Db\Mysql();break;case 'sql_server':return new Db\SqlServer();break;}}
这样,我就可以通过参数传递的方式,来选择数据库连接了。
//通过传参数的方式来实现切换不同的数据库实例$mysql = Lib\Factory::db('mysql');$sqlServer = Lib\Factory::db('sql_server');
注册模式在单例模式的基础上进一步拓展了一步,他把所有单例模式的对象全部保存在一个索引数组中,下次取得时候,直接去数组里按照索引去取。这样的好处是在程序中有条理的存放并管理对象。所以,肯定有一个存(set)和一个取(get)。一般是先去取如果没有,就重新初始化然后存起来,这样这个周期中的其他代码就可以直接取了。和redis缓存的道理是一样的。
我先来实现注册模式的代码:
<?php/*** 3. 注册模式** @package Register.php*/namespace Lib;class Register{protected static $_config;/*** 获取变量值** @param string $name* @return string*/public static function get($name){if (self::isRegister($name)) {return self::$_config[$name];}else {return false;}}/*** 设置变量值** @param string $name* @param string $value* @return string*/public static function set($name, $value){if (!isset($name) || !isset($value)) {return false;}self::$_config[$name] = $value;return self::$_config[$name];}/*** 判断变量是否注册** @param string $name* @return bool*/public static function isRegister($name){return isset(self::$_config[$name]);}}
上面代码我们定义了get和set方法。这样在一个存取模式就定义好了。
在我们的项目当中,像数据库连接,redis连接,配置文件等,都是在各个地方被大量使用的,我们完全可以使用注册模式,将这些变量对象写到注册器上面去。
下面我们来调用一下:
// 3. 注册模式$register = Lib\Register::getInstance();//数据库链接提前写到注册树上$register->set('mysql', new Lib\Db\Mysql());$register->set('sql_server', new Lib\Db\SqlServer());//读取db类$register->get('mysql')->select('select * from user');$register->get('sql_server')->select('select * from user');//redis类写到注册树上$register->set('redis', new Lib\Cache\Redis());//读取redis类$register->get('redis')->get('redis-cache-2012');//配置文件写到注册树上$config = array('db_name' => '127.0.0.1',);$register->set('config', $config);echo $register->get('config')['db_name']; // php > 5.4
这样既实现了单例模式,又使得所有的变量类都统一管理,实现起来更简洁,而且代码效率更高,更省内存。
就是将一些截然不同的函数接口封装成统一的API,最简单的例子就是DB类了。我们有mysql,mysqli,pdo等。如果我们项目中同时有这几种数据库存在,那么做一些操作是很蛋疼的,因为它们的都有各自额api.所以,这个时候,我们可以用适配器模式将3种不同的数据库封装成统一的api, 对外就是统一的方式调用。再比如我们项目中可能会用到的缓存有redis,memcache等,我们也同样可以把他们封装成统一的API接口。
我们用db类为例子来说明下。
//4 . 适配器模式$register = Lib\Register::getInstance();$config = array('host' => '127.0.0.1','db_name' =>'test','user_name' => 'root','password' => '',);//用注册模式将db配置文件注册。$register->set('db', $config);//$register->set('mysql', new Lib\Db\MySQL());$register->set('mysqli', new Lib\Db\MySQLi());$register->set('pdo', new Lib\Db\PDO());//api接口一模一样$mysqlInfo = $register->get('mysql')->select('select 1+1;');$mysqliInfo = $register->get('mysqli')->select('select 1+1;');$pdoInfo = $register->get('pdo')->select('select 1+1;');var_dump($mysqlInfo, $mysqliInfo, $pdoInfo);//工厂模式来做也一样$mysql = Lib\Factory::db('mysql');$mysqli = Lib\Factory::db('mysqli');$pdo = Lib\Factory::db('pdo');//api接口一模一样$mysqlInfo = $mysql->select('select 1+1;');$mysqliInfo = $mysqli->select('select 1+1;');$pdoInfo = $pdo->select('select 1+1;');var_dump($mysqlInfo, $mysqliInfo, $pdoInfo);