单例模式(Singleton Pattern):顾名思义,就是只有一个实例。作为对象的创建模式,单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

为什么要使用单例模式

1、PHP语言本身的局限性
PHP语言是一种解释型的脚本语言,这种运行机制使得每个PHP页面被解释执行后,所有的相关资源都会被回收。也就是说,PHP在语言级别上没有办法让某个对象常驻内存,这和asp.NET、Java等编译型是不同的,比如在Java中单例会一直存在于整个应用程序的生命周期里,变量是跨页面级的,真正可以做到这个实例在应用程序生命周期中的唯一性。然而在PHP中,所有的变量无论是全局变量还是类的静态成员,都是页面级的,每次页面被执行时,都会重新建立新的对象,都会在页面执行完毕后被清空,这样似乎PHP单例模式就没有什么意义了,所以PHP单例模式我觉得只是针对单次页面级请求时出现多个应用场景并需要共享同一对象资源时是非常有意义的。

2、应用场景
一个应用中会存在大量的数据库操作,比如过数据库句柄来连接数据库这一行为,使用单例模式可以避免大量的new操作,因为每一次new操作都会消耗内存资源和系统资源。
如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现.

要点

  1. 一个类只能有一个对象
  2. 必须是自行创建这个类的对象
  3. 要想整个系统提供这一个对象

具体实现的重点

  1. 单例模式的类只提供私有的构造函数,
  2. 类定义中含有一个该类的静态私有对象,
  3. 该类提供了一个静态的公有的函数用于创建或获取它本身的静态私有对象。

代码实现


class Singleton{
//存放实例 私有静态变量
private static $_instance = null; //私有化构造方法、
private function __construct(){
echo "单例模式的实例被构造了";
}
//私有化克隆方法
private function __clone(){ } //公有化获取实例方法
public static function getInstance(){
if (!(self::$_instance instanceof Singleton)){
self::$_instance = new Singleton();
}
return self::$_instance;
}
} $singleton=Singleton::getInstance();

OOP知识补习

类型运算符instanceof


<?php
class MyClass
{
} class NotMyClass
{
}
$a = new MyClass; var_dump($a instanceof MyClass);
var_dump($a instanceof NotMyClass);
?>

以上例程会输出:


bool(true)
bool(false)

instanceof用于确定一个变量是不是实现了某个类,继承类,接口的对象的实例。
如果被检测的变量不是对象,instanceof 并不发出任何错误信息而是返回 FALSE。不允许用来检测常量。

魔术方法__construct()

构造方法声明为private,防止直接创建对象 ,这样new Singleton() 会报错。

private function __construct()
{


echo 'Iam constructed';

}

魔术方法__clone()

当类的复制完成时,如果定义了__clone()方法,则新创建的对象(复制生成的对象)中的__clone() 方法会被调用,可用于修改属性的值(如果有必要的话)。私有化__clone可以防止克隆该类的对象。
注意一点:clone的对象不执行__construct里的方法

所以我们在防止单例模式的 $singleton对象被clone,有两种方法可以做到。

第一种方法:设置魔术方法__clone();访问权限为private
第二种方法:若__clone()为公用方法,则在函数中加上自定义错误。


// 阻止用户复制对象实例
public function __clone(){
trigger_error('Clone is not allowed.',E_USER_ERROR);
}

关于 __clone() , PHP官方的文档: Once the cloing is complete, if a __clone() method is defined, then the newly created object’s __clone() method will be called, to allow any necessary properties that need to be changed.

关键字clone和赋值


class foo {
public $bar = 'php';
}
$foo = new foo(); $a = $foo; // 标识符赋值(把$a赋值为null,原来的$foo并不会变成null,但通过$a能够修改$foo的成员$bar)
$a = &$foo; // 引用赋值(把$a赋值为null,原来的$foo也会跟着变成null)
$a = clone $foo; // 值赋值(赋值后互不影响,在计算机内存上的体现属于浅复制)

对象复制

在PHP中, 对象间的赋值操作实际上是引用操作 (事实上,绝大部分的编程语言都是如此! 主要原因是内存及性能的问题) , 比如 :


class myclass {
public $data;
}
$obj1 = new myclass();
$obj1->data = "aaa";
$obj2 = $obj1;
$obj2->data ="bbb"; //$obj1->data的值也会变成"bbb"

因为$obj1和$obj2都是指向同一个内存区的引用,所以修改任何一个对象都会同时修改另外一个对象。

在有些时候,我们其实不希望这种reference式的赋值方式, 我们希望能完全复制一个对象,这是侯就需要用到 Php中的clone (对象复制)。


class myclass {
public $data;
}
$obj1 = new myclass();
$obj1->data ="aaa";
$obj2 = clone $obj1;
$obj2->data ="bbb"; // $obj1->data的值仍然为"aaa"

因为clone的方式实际上是对整个对象的内存区域进行了一次复制并用新的对象变量指向新的内存, 因此赋值后的对象和源对象相互之间是基本来说独立的。

浅复制

什么? 基本独立?!这是什么意思? 因为PHP的object clone采用的是浅复制(shallow copy)的方法, 如果对象里的属性成员本身就是reference类型的,clone以后这些成员并没有被真正复制,仍然是引用的。 (事实上,其他大部分语言也是这样实现的, 如果你对C++的内存,拷贝,copy constructor等概念比较熟悉,就很容易理解这个概念), 下面是一个例子来说明:


class myClass{
public $data;
} $sss ="aaa";
$obj1 = new myClass();
$obj1->data =&$sss; //注意,这里是个reference!
$obj2 = clone $obj1;
$obj2->data="bbb"; //这时,$obj1->data的值变成了"bbb" 而不是"aaa"! var_dump($obj1);
var_dump($obj2);

我们再举一个更实用的例子来说明一下PHP clone这种浅复制带来的后果:


class testClass
{
public $str_data;
public $obj_data;
} $dateTimeObj = new DateTime("2014-07-05", new DateTimeZone("UTC")); $obj1 = new testClass();
$obj1->str_data ="aaa";
$obj1->obj_data = $dateTimeObj; $obj2 = clone $obj1; var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00"
var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" $obj2->str_data ="bbb";
$obj2->obj_data->add(new DateInterval('P10D')); //给$obj2->obj_date 的时间增加了10天 var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-15 00:00:00" !!!!
var_dump($obj2); // str_data:"bbb" obj_data:"2014-07-15 00:00:00"
var_dump($dateTimeObj) // 2014-07-15 00:00:00"

这一下可以更加清楚的看到问题了吧。 一般来讲,你用clone来复制对象,希望是把两个对象彻底分开,不希望他们之间有任何关联, 但由于clone的shallow copy的特性, 有时候会出现非你期望的结果.

深复制

1) $obj1->obj_data =$dateTimeObj 这句话实际上是个引用类型的赋值. 还记得前面提到的PHP中对象直接的赋值是引用操作么?除非你用$obj1->obj_dat = clone $dataTimeObj!

2) $obj2 = clone $obj1 这句话生成了一个obj1对象的浅复制对象,并赋给obj2. 由于是浅复制,obj2中的obj_data也是对$dateTimeObj的引用!

3)$dateTimeObj, $obj1->obj_data, $obj2->obj_data 实际上是同一个内存区对象数据的引用,因此修改其中任何一个都会影响其他两个!

如何解决这个问题呢? 采用PHP中的 __clone方法 把浅复制转换为深复制(这个方法给C++中的copy constructor概念上有些相似,但执行流程并不一样)


class testClass
{
public $str_data;
public $obj_data; public function __clone() {
$this->obj_data = clone $this->obj_data;
} $dateTimeObj = new DateTime("2014-07-05", new DateTimeZone("UTC")); $obj1 = new testClass();
$obj1->str_data ="aaa";
$obj1->obj_data = $dateTimeObj; $obj2 = clone $obj1;
var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00"
var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-05 00:00:00"
$obj2->str_data ="bbb";
$obj2->obj_data->add(new DateInterval('P10D')); var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00"
var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-15 00:00:00"
var_dump($dateTimeObj); //"2014-07-05 00:00:00"

原文地址:https://segmentfault.com/a/1190000016160689

单例模式的理解【php】的更多相关文章

  1. javascript单例模式的理解

    javascript单例模式的理解 阅读目录 理解单例模式 使用代理实现单例模式 理解惰性单例 编写通用的惰性单例 单例模式使用场景 回到顶部 理解单例模式 单例模式的含义是: 保证一个类只有一个实例 ...

  2. JAVA Static方法与单例模式的理解

    近期用sonar測评代码质量的时候,发现一个问题,project中一些util类,曾经写的static方法都提示最好用单例的方式进行改正. 为此,我细致想了想,发现还是非常有道理的.这里谈谈我个人对s ...

  3. JAVA设计模式 1 设计模式介绍、单例模式的理解与使用

    数据结构我们已经学了一部分了.是该了解了解设计模式了.习惯了CRUD的你,也该了解了解这一门神器.我为啥要说是神器呢? 因为在大厂的面试环节.以及很多的比如 Springboot Mybatis 等开 ...

  4. python中对单例模式的理解

    class Foo(object): instance = None def __init__(self): pass def process(self): ' @classmethod #版本1单例 ...

  5. 对C#单例模式的理解

    2018年11月6日       小雨 一.单例模式的定义 确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一的实例,是一种对象创建型模式,有如下3个要点: 只能有一个实例 必须是自行创建这个 ...

  6. 深入理解JavaScript系列

    转自http://www.cnblogs.com/TomXu/archive/2011/12/15/2288411.html 深入理解JavaScript系列(1):编写高质量JavaScript代码 ...

  7. 大熊君说说JS与设计模式之------单例模式Singleton()

    一,总体概要 1,笔者浅谈 顾名思义单例模式并不难理解,是产生一个类的唯一实例,在我们实际开发中也会使用到这种模式,它属于创建模式的一种,基于JS语言本身的语法特征, 对象直接量“{}”,也可以作为单 ...

  8. iOS设计模式之单例模式

    单例模式 基础理解 所有类都有构造方法,不编码则系统默认生成空的构造方法,若有显示定义的构造方法,默认的构造方法就会失效. 单例模式(Singleton):保证一个类仅有一个实例,并提供一个访问它的全 ...

  9. 深入理解JavaScript系列(转自汤姆大叔)

    深入理解JavaScript系列文章,包括了原创,翻译,转载,整理等各类型文章,如果对你有用,请推荐支持一把,给大叔写作的动力. 深入理解JavaScript系列(1):编写高质量JavaScript ...

随机推荐

  1. Zepto Code Rush 2014-A. Feed with Candy(HACK)

    A. Feed with Candy time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  2. 学习笔记——DISTINCT

    DISTINCT印象中向来被人诟病,说它效率低下.但网上那些SQL 面试题答案,却时有用之.其中 COUNT(DISTINCT 句式,我以前很少用,这里做个笔记. 为管理岗位业务培训信息,建立3个表: ...

  3. C语言/C++中如何产生随机数

    C语言/C++中如何产生随机数 作者: 字体:[增加 减小] 类型:转载 时间:2013-10-14我要评论 这里要用到的是rand()函数, srand()函数,和time()函数.需要说明的是,i ...

  4. Git 少用 Pull 多用 Fetch 和 Merge 【已翻译100%】【转】

    本文转载自:https://www.oschina.net/translate/git-fetch-and-merge?lang=chs&page=1# 本文有点长而且有点乱,但就像Mark ...

  5. everything的使用

    https://www.voidtools.com/support/everything/searching/ 打开多个everything进程 https://www.voidtools.com/s ...

  6. ES transport client批量导入

    从bulk.txt文件中按行读取,然后bulk导入.首先通过调用client.prepareBulk()实例化一个BulkRequestBuilder对象,调用BulkRequestBuilder对象 ...

  7. C# Task 源代码阅读(2)

    上篇已经讲到Task 的默认的TaskScheduler 为ThreadPoolTaskScheduler. 这时我们回到原来的task 的start方法,在代码最后,调用了 ScheduleAndS ...

  8. Quartz实现执行任务记录数据库,方便计算任务的执行次数以及成功次数

    任务执行实体 /** * 任务执行情况详情 */ public class JobExecuteDetail implements Serializable{ /** * */ private sta ...

  9. C# 导出word 表格代码

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  10. HDU 4901 DP

    我觉得这个DP挺难的...然而这只是lydrainbowcat学长幻灯片上的第一题-- 明天考试要GG. 题意: 给你一个序列,让你选出两个集合S和T.保证S里的数都在T里的数的左边.求一共有多少个集 ...