SINGLETON(单例)—— 对象创建型模式
概述:保证一个类只有一个实例,并且提供一个访问它的全局访问点。
关键对象:Singleton
- 定义一个GetInstance操作,保证只有在第一次调用是才会生成Singleton对象,以后的调用返回的是唯一已经存在的实例;
- 为了防止在外部对Singleton类实例化,它的构造函数被设为private;
- 在Singleton类的内部定义了一个Singleton类型的静态对象,作为提供外部共享的唯一实例。
交互方式:只能用过Singleton的GetInstance操作访问它唯一的一个Singleton实例。
适用场景:
- 当类只能有一个实例而且客户可以从一个众所周知的访问点访问它。
- 当这个唯一实例应该是通过子类化可扩展的,并且客户应该无需要改代码就能使用一个扩展的实例。
结构图示:
优缺点:
- 对唯一实例的受控访问:因为Singleton类封装它的唯一实例,所以它可以严格的控制怎样以及何时访问它;
- 缩小名空间:Singleton模式是对全局变量的一种改进。它避免了那些存储唯一实例的变量污染名空间;
- 允许对操作和表示的精化:Singleton类可以有子类,而且用这个扩展类的实例来配置应用是很容易的。可以用所需要的类的实例在运行时刻配置应用;
- 允许可变数目的实例:这个模式使得你易于改变你的想法,并允许Singleton类的多个实例。此外,你可以用相同的方法来控制应用所使用的实例的数目。只有允许访问Singleton实例的操作需要改变;
- 比类操作更灵活:另一种封装单件功能的方式是使用类操作。但这两种语言技术都难以改变设计以允许一个类有多个实例。此外,C++中的静态成员函数不会虚函数,因此子类不能多态的重定义它们。
- 单例模式中没有抽象层,因此单例类的扩展有困难;
- 单例类的职责过重,在一定程度上违背了单一职责的原则。因为单例类即提供了业务方法,又提供了创建对象的方法,将对象的创建和对象本身的功能耦合在一起。
- 很多高级面向对象编程语言如C#和Java等都提供了垃圾回收机制,如果实例化的共享对象长时间不被利用,系统则会认为它是垃圾,于是会自动销毁并回收资源,下次利用时又得重新实例化,这将导致共享的单例对象状态的丢失。
简单示例:
file: Config.h
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <string>
class Config
{
public:
~Config();
static Config *GetInstance();
bool Load(std::string path="./config.json");
void Writejsonfile();
std::string Json();
protected:
template <typename PrettyWriter>
void serialize(PrettyWriter &writer) const;
public:
std::string m_path;
std::string m_version;
int m_width;
int m_height;
private:
Config();
static Config *_instance;
};
#endif
file: Config.cpp
#include <fstream>
#include <rapidjson/document.h>
#include <rapidjson/prettywriter.h>
#include <iostream>
#include "config.h"
using namespace std;
using namespace rapidjson;
Config *Config::_instance = 0;
Config *Config::GetInstance() {
if(!_instance) {
_instance = new Config();
}
return _instance;
}
Config::Config() {
load();
}
Config::~Config() {
if(_instance) {
delete _instance;
_instance = NULL;
}
}
void Config::writejsonfile() {
std::string jsondata;
long size;
jsondata = json();
ofstream file(m_path, ios::out|ios::binary|ios::trunc);
size = jsondata.size();
file.write(jsondata.c_str(),size);
file.close();
}
std::string readjsonfile(const char* jsonfile) {
std::ifstream fin;
std::string jsondata = " ";
fin.open(jsonfile,std::ifstream::in);
if (!fin.is_open()) {
cout << "--- read json file error." << endl;
}
std::string line;
while(getline(fin,line)) {
jsondata.append(line + "\n");
}
return jsondata;
}
bool Config::load(std::string path) {
std::string json = readjsonfile((char *)path.c_str());
m_path = path;
Document doc;
doc.Parse(json.c_str());
if (doc.IsObject()) {
if (doc.HasMember("version")) {
if (doc["version"].IsString()) {
version = doc["version"].GetString();
}
}
if (doc.HasMember("width")) {
if(doc["width"].IsUint()) {
width = doc["width"].GetUint();
}
}
if (doc.HasMember("height")) {
if (doc["height"].IsUint()) {
height = doc["height"].GetUint();
}
}
} else {
cout << "---There is nonstandard jsondata." << endl;
}
return true;
}
template <typename PrettyWriter>
void Config::serialize(PrettyWriter &writer) const {
writer.StartObject();
writer.Key("version");
writer.String(m_version.c_str());
writer.String("width");
writer.Uint(m_width);
writer.String("height");
writer.Uint(m_height);
writer.EndObject();
}
std::string Config::json() {
StringBuffer sb;
PrettyWriter<StringBuffer> writer(sb);
serialize(writer);
return sb.GetString();
}