乌大湿胡扯——单例模式


在开扯之前,要说一下,这里提高一些相对深一点的东西,新手看不懂的话,可以边看边查手册相关的章节。

开扯。
大家会觉得,php进阶编程,必须掌握一些“模式”了,在“模式”里,单例模式就是家喻户晓的一个了。
很多新手同学会说,单例模式我懂,就是一个类,三私一公... ... 只能产生一个实例。
很多讲师和神级高手都是这么教的,第一步,先建个类,第二步,写个XX属性,设为私有,写个构造函数,设为私有,写个XXX方法,设为静态。。。。。
只有少数会跟新手说为什么要这样写,即使有说的,也不会说得太深。

今天,大湿就来胡扯一下:都扯蛋~!都教坏学生的。
声明一下,乌大湿不是说他们的实现方式是错的,方式是对的。错的是思维方式,把1、2、3步公式化,让学生们死记硬背,误导学生这是唯一做法,封锁了学生很多思维。

举个例子,我们有一个数据库操作类,方便我们简捷地使用调取数据库里的数据,当然,它里面带着一个数据库连接,我们现在要说的是这个数据库操作类。
如果在不同的逻辑里,要操作数据库都new一个这个操作类,可能这样会增加很多开销,对,这个类很适合做单例模式。
使用单例模式后,在不同的逻辑里,都不会重新创建一个对象,使用同一个对象就可以了。
现在要说为什么个“三私一公”是教坏学生的。为什么要将构造方法和克隆方法设为私有?讲师可能会跟你们说,这是不允许使用new和clone来创建对象,只能通过单例方法获得对象。
我继续问,为什么不允许用new和clone来创建对象?讲师说,允许了就不是单例模式了?
错了。是使用了new和clone来创建对象,这个操作就不是单例思维了。
但并不说因为允许了使用new和clone,就破坏了这个类的单例特性,只要单例方法还在,通过调用单例方法获得的对象,都是同一个对象,还是单例思维。
为什么一定要限定学生的思维?谁规定了写了单例方法实现单例模式,这个类就不能是“多例模式”?是谁规定了单例模式不能与“多例”共存的?
好吧,我不是大神,我不是讲师,喜欢被那些讲师、大神限死了自己思维的人,坚持自己的信念,坚持就是胜利。
我说一下有这么一个需求,还是刚才那个数据库操作类,我们有一部分逻辑,是连接A服务器的数据库,有一部分是连接B服务器的数据库。如果用他们说的不能用new和clone,那只能另外写一个类了,又或者,在这个类里,写个切换数据库连接的方法。写另外一个类我们就不说了,简直是脑残。我们扯一下写个切换数据库连接的做法,每个逻辑需要使用这个对象的方法时,是不是要先判断当前是连接哪个服务器的?每次都要切换到需要的数据库!蛋会疼吗?

再扯一个,谁规定了一个类的单例方法一定要写到这个类里?谁规定了单例对象一定要保存在这个类里的静态成员变量里?还要私有?
乌大湿告诉大家,没人规定,也没人有权规定。
只要思维是对的,实现方式并不是唯一的。相信乌大湿的同学,我们先把上面说的放一边,我们从另外一个地方开始扯,上面的疑问后面都会一一得到答复。


我们扯一个无关又有关的东西:引用。

先上码,


  1. <?php
  2. $a = 'a';
  3. $b = &$a;
  4. $a = 'aa';
  5. echo $b;



这里的结果,是输出aa。了解了引用后,我们知道$a和$b都指向了同一个变量。大家思考一下,这个跟我们“单例”是不是有点像?
我们对上面的代码改一下,


  1. <?php
  2. $a = 'a';
  3. function &fun(){
  4.     return $GLOBALS['a'];
  5. }
  6. $b = &fun();
  7. $c = &$a;
  8. $a = 'aaa';
  9. echo "$a, $b, $c";


这个例子输出的是aaa, aaa, aaa,我们可以通过fun方法得到这个变量的“单例”。
到这里,不知道大家有没有体会到了单例是一个思维,不是一段代码。

我们再来对这个简单的代码改一改。

  1. <?
  2. $obj = null;
  3. function &fun(){
  4.     if($GLOBALS['obj'] == null){
  5.         $GLOBALS['obj'] = 'a';
  6.     }
  7.     return $GLOBALS['obj'];
  8. }
  9. $a = &fun();
  10. $b = &fun();
  11. echo "$a, $b<br />\r\n";
  12. $a = 'b';
  13. echo "$a, $b<br />\r\n";


再来一个

  1. <?
  2. function &fun(){
  3.     static $obj = null;
  4.     if($obj == null){
  5.         $obj = 'a';
  6.     }
  7.     return $obj;
  8. }
  9. $a = &fun();
  10. $b = &fun();
  11. echo "$a, $b<br />\r\n";
  12. $a = 'b';
  13. echo "$a, $b<br />\r\n";

两个栗子的结果是一样的,$a和$b都是同个变量,这个方法fun,实现的就是单例。

这两个例子,我想说的是,这个“单例”保存在哪里,只要能保证是这个东西,就能实现我们想实现的效果。重点是我们“单例”的思维。

其实,我已经说完了上面说的“一一得到答复”。


或者,有些同学领悟能力不太好,那我就上码吧。真正想学东西的同学,不急着看我上的代码,自己思考一下,写一下。写一个类,不用三私一公的,单例方式不需要在本类里的。
自认领悟力不好,认为自己能力差到大湿不想理的程序,来烤马(拷码)。

  1. <?php
  2. class DB(){
  3. //这个类实现单例、具体代码自己随便写。
  4. }
  5. function DB单例($name='default', $config=xxxx){
  6.     static $dbs = array();
  7.     if(!isset($dbs[$name])){
  8.         $dbs[$name] = new DB();
  9.         $dbs[$name]->连接服务器($config);
  10.     }
  11.     return $dbs[$name];
  12. }

可能有同学会问,怎样验证我新建的两个对象是不是同一个?
其实大湿真的不想说的,上面的例子有说到的。但群里也有朋友问这样的问题,我就说了吧:在类里弄个仅有属性,创建了两个对象,把一个对象的属性设一个值,在另外一个对象输出。

估计自己动手写的朋友也回来看了。
大湿就对以上扯的东西做个总结吧:
1、不要迷信讲师、大神,包括乌大湿。
2、不要抄代码,不要记公式。一旦有了公式,思维就会被限制。
3、代码完全不重要,重要的是思维,思维对了,代码都是浮动。

看到这里的新手,估计也不是对大湿说的话非常排斥,我说一个学习方法吧。
不要抄代码,不要抄代码,不要抄代码,重要的话要说三次,别人的代码你可以看,看完后,分析一下流程,理解别人的思路,有了思路后,自己再把思路写成代码,是自己写,不是凭借过目不忘的本领把刚才看到的代码默写出来。

好,今天的胡扯到这里,谢谢大家。
欢迎大家继续关注乌大湿的胡扯教程,胡扯不犯罪,欢迎加入Q裙跟大伙一直胡扯瞎扯。93701629。