常用设计模式
本文涉及的常用设计模式:单例模式、工厂模式、注册树模式
单例模式(Singleton Pattern):顾名思义,就是只有一个实例。作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
(1)为什么要使用PHP单例模式
1,php的应用主要在于数据库应用, 一个应用中会存在大量的数据库操作, 在使用面向对象的方式开发时, 如果使用单例模式,
则可以避免大量的new 操作消耗的资源,还可以减少数据库连接这样就不容易出现 too many connections情况。
2,如果系统中需要有一个类来全局控制某些配置信息, 那么使用单例模式可以很方便的实现. 这个可以参看zend Framework的FrontController部分。
3,在一次页面请求中, 便于进行调试, 因为所有的代码(例如数据库操作类db)都集中在一个类中, 我们可以在类中设置钩子, 输出日志,从而避免到处var_dump, echo
(2)单例模式的实现
1,私有化一个属性用于存放唯一的一个实例
2,私有化构造方法,私有化克隆方法,用来创建并只允许创建一个实例
3,公有化静态方法,用于向系统提供这个实例
工厂模式(Factor Pattern),就是负责生成其他对象的类或方法,也叫工厂方法模式
抽象工厂模式( Abstract Factor Pattern),可简单理解为工厂模式的升级版
(1)为什么需要工厂模式
1,工厂模式可以将对象的生产从直接new 一个对象,改成通过调用一个工厂方法生产。这样的封装,代码若需修改new的对象时,不需修改多处new语句,只需更改生产对象方法。
2,若所需实例化的对象可选择来自不同的类,可省略if-else多层判断,给工厂方法传入对应的参数,利用多态性,实例化对应的类。
注册树模式
注册树模式又称注册模式或注册器模式。注册树模式通过将对象实例注册到一棵全局的对象树上,需要的时候从对象树上采摘的模式设计方法。和果树不同的是,果子只能采摘一次,而注册树上的实例却可以无数次获取。
使用了注册树模式后,对于实例,我们能够更好地统筹管理安排,就像使用全局变量一样的方便实用。
接下来通过一段tp5中例子来实现上面的三个经典模式:
<?php
/**
* 单例模式
*/
class Site {
// 属性
public $siteName;
// 本类的静态实例
protected static $instance = null;
// 禁用掉构造器
private function __construct($siteName) {
$this->siteName = $siteName;
}
// 获取本类的唯一实例
public static function getInstance($siteName = 'wuyudong') {
if (! self::$instance instanceof self) {
self::$instance = new self ( $siteName );
}
return self::$instance;
}
}
// 用工厂模式来生成本类的单一实例
class Factory {
// 创建指定类的实例
public static function create() {
return Site::getInstance ( "wu" );
}
}
/**
* 对象注册树
* 1.注册:set(),把对象挂到树上
* 2.获取:get(), 把对象取下来用
* 3.注销:_unset(),把对象吃掉
*/
class Register {
// 创建对象池:数组
protected static $objects = [ ];
// 生成对象并上树
public static function set($alias, $object) {
self::$objects [$alias] = $object;
}
// 从树上取下对象
public static function get($alias) {
return self::$objects [$alias];
}
// 把树上的对象吃掉
public static function _unset($alias) {
unset ( self::$objects [$alias] );
}
}
// 将Site类的实例上树,放进对象池
Register::set ( 'site', Factory::create () );
// 从树上取一个对象
$obj = Register::get ( 'site' );
// 查看这个对象
var_dump ( $obj );
echo '<br/>';
容器与依赖注入的原理
1、任何的URL访问,最终都是定位到控制器,由控制器中某个具体的方法去执行
2、一个控制器对应一个类,如果这些类进行统一管理,怎么办?
通过容器来进行类管理,还可以将类的实例(对象)作为参数,传递给类方法,自动触发依赖注入
依赖注入:将对象类型的数据,以参数的方式传到方法的参数列表
URL访问:通过get方式将数据传到控制器指定的方法中(只能穿字符串、数值)
如果要传一个对象呢?
依赖注入:解决了向类中的方法传对象的问题
新建一个common目录,接着新建一个Temp.php文件,代码如下:
<?php
namespace app\common;
class Temp {
private $name;
public function __construct($name = 'wuyudong') {
$this->name = $name;
}
public function setName($name) {
$this->name = $name;
}
public function getName() {
return '方法是:' . __METHOD__.'属性是:'.$this->name;
}
}
接着实现部分:
<?php
namespace app\index\controller;
use app\common\Temp;
class Demo1 {
// 通过字符串、数值用GET方式传值到类方法中
public function getName($name = 'Wu') {
return 'Hello ' . $name;
}
public function getMethod1() {
$temp = new Temp ();
$temp->setName ( 'hezhijuan' );
return $temp->getName ();
}
// app\common\Temp $temp:将会触发依赖注入
public function getMethod2(Temp $temp) {
$temp->setName ( 'hezhijuan' );
return $temp->getName ();
}
// 绑定一个类到容器
public function bindClass1() {
// 把一个类放到容器中,相当于注册到容器
\think\Container::set ( 'temp', 'app\common\Temp' );
// 将容器中的类实例化并取出来使用:实例化时可调用构造器进行初始化
$temp = \think\Container::get ( 'temp', [ 'name' => 'hehehe' ] );
return $temp->getName ();
}
// 绑定一个类到容器(使用助手函数:bind和app)
public function bindClass2() {
// 把一个类放到容器中,相当于注册到容器
bind ( 'temp', 'app\common\Temp' );
// 将容器中的类实例化并取出来使用:实例化时可调用构造器进行初始化
$temp = app ( 'temp', [ 'name' => 'qwqw' ] );
return $temp->getName ();
}
// 绑定一个闭包到容器
public function bindClosure() {
// 把一个闭包放到容器中
\think\Container::set ( 'demo', function ($domain) {
return 'my blog site is:' . $domain;
} );
// 将容器中的闭包取出来使用:实例化时可调用构造器进行初始化
$temp = \think\Container::get ( 'demo', [
'domain' => 'www.wuyudong.com'
] );
return $temp;
}
}
Facade静态代理
门面(facade)为容器中的类提供了一个静态调用接口,相比于传统的静态方法调用, 带来了更好的可测试性和扩展
性,你可以为任何的非静态类库定义一个 facade 类。
在common目录下新建一个Test.php文件,代码如下:
<?php
namespace app\common;
class Test {
public function hello($name) {
return 'Hello ' . $name;
}
}
常规动态调用代码如下:
namespace app\index\controller;
class Demo2 {
public function index($name = 'ThinkPHP') {
$test = new \app\common\Test ();
return $test->hello ( $name );
}
}
如果想静态调用一个动态方法,需要给当前的类绑定一个静态代理类
新建目录facade,新建代理类Test.php,代码如下:
<?php
namespace app\facade;
use think\Facade;
class Test extends Facade {
protected static function getFacadeClass(){
return 'app\common\Test';
}
}
这样
<?php
namespace app\index\controller;
use app\facade\Test;
class Demo2 {
public function index($name = 'ThinkPHP') {
return Test::hello('wuyudong');
}
}
即:app\facade\Test类代理了app\common\Test类
上面的实现还可以使用动态绑定:
修改facade\Test.php中的内容为:
<?php
namespace app\facade;
use think\Facade;
class Test extends Facade {
}
同样修改相应的代码,使用Facade的bind方法:
<?php
namespace app\index\controller;
use app\facade\Test;
use think\Facade;
class Demo2 {
//动态绑定
public function index($name = 'ThinkPHP') {
Facade::bind('app\facade\Test', 'app\common\Test');
return Test::hello('wuyudong');
}
}
Request请求对象
方法1:可以使用上面所学的知识来进行Request对象的静态代理
<?php
namespace app\index\controller;
use think\Controller;
use think\facade\Request;
class Demo3 extends Controller
{
//创建一个请求对象Request的静态代理
public function test(){
dump(Request::get());
}
}
方法2:常规方式:
<?php
namespace app\index\controller;
use think\Controller;
use think\facade\Request;
class Demo3 extends Controller
{
public function test(){
$request= new \think\Request();
dump($request->get());
}
}
方法3:使用依赖注入的方式:
public function test(\think\Request $request){
dump($request->get());
}
方法4:使用Request类中的request属性:
public function test(){
dump($this->request->get());
}
方法5:使用助手函数request()来实现:
public function test(){
dump(request()->get());
}
这里使用5种方法实现Request对象的使用
Comments