单例模式,就是保持一个对象只存在一个实例。并且为该唯一实例提供一个全局访问点(一般是一个静态的getInstance方法),单例模式使用的地方很多,一般在数据库操作对象、日志写入对象、全局配置解析对象等用的比较多。
他们的共同特征我决定有以下3点:
1.只需要一个实例
2.不断new多个实例会不断增加资源的消耗
3.全局调用的话会很便利
单例模式具备的三个关键点:
① 需要一个保存类的唯一实例的静态成员变量;
②构造函数和克隆函数必须声明为私有的,防止外部程序new类从而失去单例模式的意义;
③必须提供一个访问这个实例的公共的静态方法(通常为getInstance方法),从而返回唯一实例的一个引用 。
我们来写一个例子来看一下,下面是一个日志操作类:
class Logger{
//首先,需要一个私有的静态变量来存储产生的对象实例
private static $instance;
//业务变量,保存日志写入路径
private $logDir;
//构造方法,注意必须也是私有的,不允许被外部实例化(即在外部被new)
private function __construct(){
//调试输出,测试对象被new的次数
echo "new Logger instance \r\n";
$logDir = sys_get_temp_dir(). DIRECTORY_SEPARATOR . "logs";
if(!is_dir($logDir) || !file_exists($logDir)){
@mkdir($logDir);
}
$this->logDir = $logDir;
}
//类唯一实例的全局访问点,用于判断并返回对象实例,供外部调用
public static function getInstance(){
if(is_null(self::$instance)){
$class = __CLASS__; //获取本对象的类型,也可以用new self()方式
self::$instance = new $class();
}
return self::$instance;
}
//重载__clone()方法,不允许对象对克隆
public function __clone(){
throw new Exception("Singleton Class Can Not Be Cloned");
}
//具体的业务方法,实际可以有很多方法
public function logError($message){
$logFile = $this->logDir . DIRECTORY_SEPARATOR . "error.log";
error_log($message, 3, $logFile);
}
}
//日志调用
$logger = Logger::getInstance();
$logger->logError("An error occured");
$logger->logError("Another error occured");
//或者这样调用
Logger::getInstance()->logError("Still have error");
Logger::getInstance()->logError("I should fix it");
这就是一个单例模式的一个简单例子。
重点注意:单例滥用,单例模式相对来说比较好理解和实现,因此一旦认识到单例模式的好处,很可能什么类都想写成单例,因此在使用次模式之前一定要考虑之前博主说的3种情况,看是否真的有必要使用。