浅谈thinkphp5 instance 的简单实现

最近学习 ThinkPHP5,第一次看到 TestClass::instance() 就能创建 TestClass 实例的方法。感到很好奇,翻阅 ThinkPHP 的源代码,大体理解了 它的 设计思想,非常的先进。

老规矩,直接上代码:


<?php
class TestClass {

 public static function instance() {
  return new self();
 }

 public $data = [];

 public function __set($name, $val) {
  return $this->data[$name] = $val;
 }

 public function __get($name) {
  return $this->data[$name];
 }
}

$app1 = TestClass::instance();
$app1->key = 'Application 1';
echo $app1->key . '<br />';
?>

为了方便调用,也模仿 ThinkPHP 写了一个助手函数

<?php
function app() {
 return TestClass::instance();
}

$app2 = app();
$app2->key = 'Application 2';
echo $app2->key . '<br />';
?>

这样就简单的实现了 instance。

不过这种方法还有一个小问题,试想以下,调用100次,就需要创建100个实例,想想都觉得可怕。

给 Test 类 增加一个 静态属性,将创建的实例保存到这里。下次如果需要调用,则直接调用这个实例。

<?php
class TestClass {

 public static $instance; //用于缓存实例

 public $data = [];

 public static function instance() {
  //如果不存在实例,则返回实例
  if (empty(self::$instance)) {
   self::$instance = new self();
  }
  return self::$instance;
 }

 public function __set($name, $val) {
  return $this->data[$name] = $val;
 }

 public function __get($name) {
  return $this->data[$name];
 }

}

function app($option = []) {
 return TestClass::instance($option);
}

header('content-type:text/plain');

$result = [];
$app1 = app();
$app1->key = "Application 1"; //修改 key 为 Application 1
$result['app1'] = [
 'app1' => $app1->key, //实例中 key 为 Application 1
];

// 创建 app2,因为 instance 已经存在实例,直接返回 缓存的实例
$app2 = app();
$result['app2'] = [
 'setp1' => [
  'app1' => $app1->key, // Application 1
  'app2' => $app2->key, //因为直接调用的实例的缓存,所以 key 也是 Application 1
 ],
];

// 无论 app1,app2 都对在内存中 对应的同一个实例,无论通过谁修改,都能改变值
$app1->key = "Application 2";
$result['app2']['setp2'] = [
 'app1' => $app1->key, // Application 2
 'app2' => $app2->key, // Application 2
];
print_r($result);
?>

通过上边的实验,可以看到 无论调用多少次,都会使用同一个实例。这样就解决了效率低的问题。

到现在基本就满足大多数情况了,唯一的小缺陷,就是 可能 实例的 初始参数不同,这样没法灵活调用(常见的比如同一个程序调用两个数据库)。在 上边的 例子中稍作改造,以传入的参数为key,将不通的 实例缓存到数组中 就可以解决。

<?php
class TestClass {

 public static $instance = []; //用于缓存实例数组
 public $data = [];

 public function __construct($opt = []) {
  $this->data = $opt;
 }

 public static function instance($option = []) {
  // 根据传入的参数 通过 serialize 转换为字符串,md5 后 作为数组的 key
  $instance_id = md5(serialize($option));
  //如果 不存在实例,则创建
  if (empty(self::$instance[$instance_id])) {
   self::$instance[$instance_id] = new self($option);
  }
  return self::$instance[$instance_id];
 }

 public function __set($name, $val) {
  return $this->data[$name] = $val;
 }

 public function __get($name) {
  return $this->data[$name];
 }

}

function app($option = []) {
 return TestClass::instance($option);
}

header('content-type:text/plain');

$result = [];
//传入 初始数据
$app1 = app(['key' => '123']);
$result['init'] = $app1->key; // 使用 传入的数据,即:123
$app1->key = "app1";
$result['app'] = $app1->key; // 现在值改为了 自定义的 app1了
print_r($result);

$result = [];
// 创建 app2,注意 初始参数不一样
$app2 = app();
// 因为初始参数不一样,所以还是创建新的实例
$app2->key = "app2";
$result['app1'] = $app1->key; // app1
$result['app2'] = $app2->key; // app2
print_r($result);

$result = [];
// 创建 app3,传入的参数 和 app1 一样,所以会直接返回 和app1相同 的 实例
$app3 = app(['key' => '123']);
$result['log'] = [
 'app1' => $app1->key, // app1
 'app2' => $app2->key, // app2
 'app3' => $app3->key, // app1
];

// 设置 app3 的key,会自动修改 app1 的值,因为他们两个是同一个实例
$app3->key = 'app3';
$result['app3_set'] = [
 'app1' => $app1->key, // app3
 'app2' => $app2->key, // app2
 'app3' => $app3->key, // app3
];

// 同理,设置 app1 的key,app3 的 key 也会修改
$app1->key = 'app1';
$result['app1_set'] = [
 'app1' => $app1->key, // app1
 'app2' => $app2->key, // app2
 'app3' => $app3->key, // app1
];
print_r($result);
?>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 浅谈thinkphp5 instance 的简单实现

    最近学习 ThinkPHP5,第一次看到 TestClass::instance() 就能创建 TestClass 实例的方法.感到很好奇,翻阅 ThinkPHP 的源代码,大体理解了 它的 设计思想,非常的先进. 老规矩,直接上代码: <?php class TestClass { public static function instance() { return new self(); } public $data = []; public function __set($name, $

  • 浅谈ThinkPHP5.0版本和ThinkPHP3.2版本的区别

    5.0版本和之前版本的差异较大,本篇对熟悉3.2版本的用户给出了一些5.0的主要区别. URL和路由 5.0的URL访问不再支持普通URL模式,路由也不支持正则路由定义,而是全部改为规则路由配合变量规则(正则定义)的方式: 主要改进如下: 增加路由变量规则: 增加组合变量支持: 增加资源路由: 增加路由分组: 增加闭包定义支持: 增加MISS路由定义: 支持URL路由规则反解析: 请求对象和响应对象 5.0新增了请求对象Request和响应对象Response,Request统一处理请求和获取请

  • 浅谈iphone X的简单适配问题(推荐)

    上周Apple大大发布了新的设备,其中最引人注目的莫过于iphone X,对于这款设备官方有详尽的解说官方文档,除了最新的若干AR.人工智能等功能外,我们发现这也是一款全新尺寸的设备. 官方文档指出这款设备的没有home键,当然有若干手势操作代替了home键的功能,同时该设备也是基本的全面屏设备.当然听到了不少吐槽该设备的"前刘海",但是我认为相对于刘海上添加的功能,这个刘海也无伤大雅,毕竟相比美观,对于我这种务实者来说更看重功能,闲话不多说,我们在苹果发布设备的第一时间对我们的工程做

  • 浅谈C# AOP的简单实现

    前言:为了弄清楚AOP,博主也是拼了.这篇打算写写AOP,说起AOP,其实博主接触这个概念也才几个月,了解后才知道,原来之前自己写的好多代码原理就是基于AOP的,比如MVC的过滤器Filter,它里面的异常捕捉可以通过FilterAttribute,IExceptionFilter去处理,这两个对象的处理机制内部原理应该就是AOP,只不过之前没有这个概念罢了. 一.AOP概念 老规矩,还是先看官方解释:AOP(Aspect-Oriented Programming,面向切面的编程),它是可以通过

  • 浅谈Java slf4j日志简单理解

    一.理解 slf4j(Simple Logging Facade for Java),表示为java提供的简单日志门面,更底层一点说就是接口.通过将程序中的信息导入到日志系统并记录,实现程序和日志系统的解耦 日志门面接口本身通常并没有实际的日志输出能力,它底层还是需要去调用具体的日志框架API的,也就是实际上它需要跟具体的日志框架结合使用.由于具体日志框架比较多,而且互相也大都不兼容,日志门面接口要想实现与任意日志框架结合可能需要对应的桥接器,就好像JDBC与各种不同的数据库之间的结合需要对应的

  • 浅谈redux以及react-redux简单实现

    写在前头 redux 简介 随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state (状态). 这些 state 可能包括服务器响应.缓存数据.本地生成尚未持久化到服务器的数据,也包括 UI 状态,如激活的路由,被选中的标签,是否显示加载动效或者分页器等等. 管理不断变化的 state 非常困难.如果一个 model 的变化会引起另一个 model 变化,那么当 view 变化时,就可能引起对应 model 以及另一个 model 的变化,

  • 浅谈mvvm-simple双向绑定简单实现

    mvvm模式解放DOM枷锁 mvvm原理分析 JavaScript在浏览器中操作HTML经历了几个不同阶段 第一阶段 直接用浏览器提供的原生API操作DOM元素 var dom = document.getElementById('id'); dom.innerHTML = 'hello mvvm'; 第二阶段 jQuery的出现解决了原生API的复杂性和浏览器间的兼容性等问题,提供了更加简易方便的API $('#id').text('hello mvvm') 第三阶段 MVC模式使前端可以和后

  • 浅谈python字符串方法的简单使用

    学习python字符串方法的使用,对书中列举的每种方法都做一个试用,将结果记录,方便以后查询. (1) s.capitalize() ;功能:返回字符串的的副本,并将首字母大写.使用如下: >>> s = 'wwwwww' >>> scap = s.capitalize() >>> scap 'Wwwwww' (2)s.center(width,char); 功能:返回将s字符串放在中间的一个长度为width的字符串,默认其他部分用空格填充,否则使用c

  • 浅谈线性表的原理及简单实现方法

    一.线性表 原理:零个或多个同类数据元素的有限序列 原理图: 特点 : 1.有序性 2.有限性 3.同类型元素 4.第一个元素无前驱,最后一个元素无后继,中间的元素有一个前驱并且有一个后继 线性表是一种逻辑上的数据结构,在物理上一般有两种实现 顺序实现和链表实现 二.基于数组的 线性表顺序实现 原理 : 用一段地址连续的存储单元依次存储线性表数据元素. 原理图: 算法原理: 1.初始化一个定长的数组空间 elementData[] , size 存储长度 存储元素 2.通过索引来快速存取元素 3

  • 浅谈Linux系统中的异常堆栈跟踪的简单实现

    在Linux中做C/C++开发经常会遇到一些不可预知的问题导致程序崩溃,同时崩溃后也没留下任何代码运行痕迹,因此,堆栈跟踪技术就显得非要重要了.本文将简单介绍Linux中C/C++程序运行时堆栈获取,首先来看backtrace系列函数--使用范围适合于没有安装GDB或者想要快速理清楚函数调用顺序的情况 ,头文件execinfo.h int backtrace (void **buffer, int size); 该函数用来获取当前线程的调用堆栈,获取的信息将会被存放在buffer中,它是一个指针

随机推荐