@phper
2015-04-10T16:30:38.000000Z
字数 5329
阅读 6947
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);