问题烦人的全局变量在PHP中,甚至不仅仅是在PHP中,我们都会使用全局变量来保存全局状态。然而,全局变量通常是全局共享的,任何地方的任何代码都可能覆盖它们。例如,我们定义一个名为PHONE的全局变量。我们在一行代码中将其定义为iPhone,但我们不小心在另一行代码中将其覆盖为Nokia。这很尴尬,因为我们不希望它被覆盖。繁琐的参数传递在一个系统中,我们会定义很多方法,生成很多对象。有时,我们会使用多种方法对同一个对象进行操作。在不使用全局变量的情况下,我们需要将对象作为参数传递给方法。但是以这种方式传递同一个对象可能会引起混淆,并可能导致不必要的依赖。其实我们只需要一个全局可访问的对象就可以解决这个问题,但是全局变量会有我们上面提到的问题。解决目标我们要解决这些问题,我们有以下针对此类对象的目标。这个对象无论在哪里都可以被访问,就像一个全局变量一样。与全局变量不同,此对象不能被覆盖。整个系统只有一个对象,对它的修改全系统都能感知。以上目标就是我们需要的,也就是单例模式的特性。UML实现类Preference{privatestatic$instance;私人$props=[];private__construct(){}publicstaticfunctiongetInstance(){if(empty(self::$instance)){self::$instance=newPreference();}返回自我::$实例;}publicfunctionsetProperty($key,$value){$this->props[$key]=$value;}publicfunctiongetProperty($key){return$this->props[$key];}privatefunction__clone(){}privatefunction__sleep(){}privatefunction__wakeup(){}}我们这里引入了私有构造函数,使得对象无法在外部被实例化。同时,我们使用getInstance方法获取具体的实例,且无法重写,达到了第二个目的。由于$instance和getInstance都是静态的,我们可以通过Preference::getInstance()来访问具体的实例。这样就可以全局访问了,就好像一个全局变量一样,达到了第一个目的。对于这个类,我们不能生成第二个对象,因为它的构造函数是private的,__clone方法是private的,getInstance如果判断已经有实例,默认返回实例。这实现了第三个目标。同时,我们也尽量避免序列化这个实例,所以我们将__wakeup和__sleep这两个魔术方法私有化。这就是单例模式。后记对于单例模式来说,其实并没有那么高大上。无非就是对象的访问范围发生了变化,而对象一直存在,仅此而已。最后,这篇文章是笔者在学习设计模式时的感想。部分参考《深入 PHP 面向对象、模式与实践(第 3 版)》。如有错误,感谢大神的开导。
