在软件工程中,建立型设计模式承担着对象建立的职责,尝试建立适合程序上下文的对象,对象建立设计模式的产生是因为软件工程设计的问题,具体说是向设计中增长复杂度,建立型设计模式解决了程序设计中对象建立的问题。PHP的设计模式有不少种,本文取最简单的三种模式: 工厂模式、单例模式和注册树模式进行简单的讲解。php
用工厂方法或者类生成对象,而不是代码中直接使用 new 关键字mysql
好比我们要作一个链接数据库的操做,平时都是用mysql。那忽然有一天mysql收费了,咋办?那咱们就用Sqlite做来数据库。若是以传统的方式建立一个数据库链接类的话是须要使用关键字new MysqlDrive()
把这个数据库实例化,一旦数据库变动成的话就须要把全部的new MysqlDrive()
变动成new SqliteDrive()
这样替换比较麻烦,而且还容易出错。因此,下面的工厂模式能够很好的解决这一问题。git
废话很少说,演示一遍就明白了github
先看一下目录结构吧本文演示的所有是遵循PSR-4的编码规范,固然,是在vendor/
目录下,若是没有请本身行执行命令composer dumpatuoload
若是您没有安装composer那就请看下面文章:sql
如下是个人目录结构: bootstrap
自动忽略DependencyInjection 跟HttpFoundation目录,这是我计划下次写的东西,嘻嘻...设计模式
先看看主要文件的代码吧:composer
tests/
这个目录是为了让我们方便测试的目录,也就是单元测试目录框架
文件: tests/bootstrap.php
include_once __DIR__."/../../../composer/ClassLoader.php"; $loader = new \Composer\Autoload\ClassLoader(); $vendorDir = dirname(dirname(__FILE__)); $baseDir = dirname($vendorDir); $classMaps = [ 'Dudulu\\' => [$vendorDir."/src/"] ]; foreach ($classMaps as $namespace => $path) { $loader->setPsr4($namespace, $path); } $loader->register(true);
我们须要测试的驱动类MysqlDerive.php
文件: src/DesignPatterns\FactoryMode\DB\MysqlDrive.php
namespace Dudulu\DesignPatterns\FactoryMode; use Dudulu\DesignPatterns\DB\MysqlDrive; /** * Class Factory * @package Dudulu\DesignPatterns\FactoryMode */ class Factory { /** * @return MysqlDrive */ public static function database() { return new MysqlDrive(); } }
为了强制让每种驱动都要有一个链接方式,因此咱们须要一个Interface。
文件: src/DesignPatterns/DB/Interfaces/DriveInterface.php
namespace Dudulu\DesignPatterns\DB\Interfaces; /** * Interface DriveInterface * @package Dudulu\DesignPatterns\DB\Interfaces */ interface DriveInterface { /** * @return mixed */ public function connection(); }
先看一个普通demo,直接实例化MysqlDrive
对象
include_once "../bootstrap.php"; use Dudulu\DesignPatterns\DB\MysqlDrive; class FactoryTest extends PHPUnit_Framework_TestCase { public function testConnection() { $db = new MysqlDrive(); $this->assertEquals(true, $db->connection()); } }
进入src/tests/DesignPattens
目录.
命令行执行: phpunit FactoryTest.php
能够发现这样是好使的...
工厂模式 是一种类,它具备为您建立对象的某些方法。您可使用工厂类建立对象,而不直接使用 new。这样,若是您想要更改所建立的对象类型,只需更改该工厂便可。使用该工厂的全部代码会自动更改。
首先我们先建立一个Factory.php
做为工厂类,而后里边实现一个静态方法对相数据库驱动进行实例化。
文件: src/DesignPatterns/FactoryMode/Factory.php
namespace Dudulu\DesignPatterns\FactoryMode; use Dudulu\DesignPatterns\DB\MysqlDrive; /** * Class Factory * @package Dudulu\DesignPatterns\FactoryMode */ class Factory { /** * @return MysqlDrive */ public static function database() { return new MysqlDrive(); } }
建立完后,我们回到单元测试文件FactoryTest.php
文件: tests/DesignPattens/FactoryTest.php
include_once "../bootstrap.php"; use Dudulu\DesignPatterns\FactoryMode\Factory; class FactoryTest extends PHPUnit_Framework_TestCase { public function testConnection() { $db = Factory::database(); $this->assertEquals(true, $db->connection()); } }
再执行一下单元测试命令发现,也能返回成功,这样的话咱们就能很方便的修改任何驱动了。若是咱们从mysql换到sqlite了,那么,我们主要的代码都不须要更变,只要在Factory.php
把connection
方法的边接方式修改完就行了,很是方便。
使某个类的对象仅容许建立一个
某些应用程序资源是独占的,由于有且只有一个此类型的资源。好比,数据库的链接的独占。但愿在应用程序中共享数据库链接,由于在保持链接打开或关闭时,它是一种开销,在获取单个页面的过程当中更是如此。
好比我们链接数据库时,若是不使用单例模式,多个地方都对数据类进行了实例化,那么这样会造能不少资源浪费,为了解决这问题,对于数据库类咱们只须要实例化一次,后面再次调用它是若是已经实例化,那就直接返回。
来,咱们拿一个类来玩一玩...
文件: src/DesignPatterns/SingletonMode/SingletonDB.php
咱们建立这个类,而后将构造方法私有化,这样的话咱们就没法使用
new
关键字对这个类进行实例化了。
namespace Dudulu\DesignPatterns\SingletonMode; use Dudulu\DesignPatterns\DB\Interfaces\DriveInterface; /** * Class SingletonDB * @package Dudulu\DesignPatterns\SingletonMode */ class SingletonDB implements DriveInterface { /** * SingletonDB constructor. */ private function __construct() { } /** * @return bool */ public function connection() { return true; } }
那么咱们要如何使用这个对象呢?咱们须要一个受保护的成员和一个静态方法:
/** * @var SingletonDB */ protected static $db; /** * @return SingletonDB */ public static function getInstance() { if (self::$db) { return self::$db; } self::$db = new self(); return self::$db; }
这个方法很好理解,简单意思就是若是当前属性已经被设置过了,那就再也不进行实例化,而是直接返回,不然实例化当前对象并返回。
与测试上面的方法同样,测玩玩...
建立测试的文件: tests/DesignPattens/SingletonDBTest.php
include_once "../bootstrap.php"; use Dudulu\DesignPatterns\SingletonMode\SingletonDB; /** * Class SingletonDBTest */ class SingletonDBTest extends PHPUnit_Framework_TestCase { /** * @return void */ public function testConnection() { $db = SingletonDB::getInstance(); $this->assertEquals(true, $db->connection()); } }
执行命令: phpunit SingletonDBTest.php
发现也是能够执行成功的。
若是不信的话,你能够试试多执行几回SingletonDB::getInstance();
而后在 getInstance()
方法体里作一个计数器,看看它实例化过几回。
主要用来解决全局共享和交换对象
这个也很好理解,由于咱们在框架中常常用到。
注册树模式固然也叫注册模式,注册器模式。之因此我在这里矫情一下它的名称,是由于我感受注册树这个名称更容易让人理解。注册树模式经过将对象实例注册到一棵全局的对象树上,须要的时候从对象树上采摘的模式设计方法。
我们结合工厂模式及单例模式作一个小例子:
建立文件: src/DesignPatterns/RegisterMode/Register.php
namespace Dudulu\DesignPatterns\RegisterMode; /** * Class Register * @package Dudulu\DesignPatterns\RegisterMode */ class Register { /** * @var array */ protected static $classMaps = []; /** * @param $alias * @param $class * @return void */ public static function set($alias, $class ) { self::$classMaps[$alias] = $class; } /** * @param $alias * @return mixed */ public static function get($alias ) { return self::$classMaps[$alias]; } }
而后建立一个单例模式的DB类DemoDB.php
:
文件: src/DesignPatterns/DB/DemoDB.php
namespace Dudulu\DesignPatterns\DB; use Dudulu\DesignPatterns\DB\Interfaces\DriveInterface; class DemoDB implements DriveInterface { /** * @var DemoDB */ protected static $db; /** * SingletonDB constructor. */ private function __construct() { } /** * @return DemoDB */ public static function getInstance() { if (self::$db) { return self::$db; } self::$db = new self(); return self::$db; } /** * @return bool */ public function connection() { return true; } }
再工厂模式文件上加入注册方式代码:
文件: src/DesignPatterns/FactoryMode/Factory.php
use Dudulu\DesignPatterns\RegisterMode\Register; /** * @return DemoDB */ public static function testDb() { $db = DemoDB::getInstance(); Register::set('DB', $db); return $db; }
最后我们验证一下:
在tests/
目录下建立RegisterTest.php
文件
include_once "../bootstrap.php"; use Dudulu\DesignPatterns\RegisterMode\Register; use Dudulu\DesignPatterns\DB\DemoDB; /** * Class RegisterTest */ class RegisterTest extends PHPUnit_Framework_TestCase { /** * @return void */ public function testConnection() { Register::set('DB', DemoDB::getInstance()); $db = Register::get('DB'); $this->assertEquals(true, $db->connection()); } }
OK 走一个phpunit RegisterTest.php
返回OK,好棒好棒...☆〜(ゝ。∂)
__ / ))) _ `/ イ~ (((ヽ ( ノ  ̄Y\ | (\ ∧_∧ | ) ヽ ヽ`( `o´ )/ノ/ \ | ⌒Y⌒ / / |ヽ | ノ/ \トー仝ーイ |
GitHub: https://github.com/icowan/dudulu
若是有时间,下次我再写些关于其余设计模式的文章...
原文地址: 设计模式之工厂模式、单例模式、注册树模式
欢迎关注我站点: LatteCake
欢迎关注公众号: 聪聪实验室