导论:上次在面试的时候,面试官问及设计模式中的单例模式,什么是单例模式。在我把单例模式原理及两种实现方法道出,他问了一个问题,什么情况使用单例模式。在熟悉理论,并没有在实际开发过程中使用,这个问题问死了我。闲暇之余,重新的将这种简单且重要的软件设计模式总结。
单例模式:单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。即一个类只有一个对象实例。
单例模式的要点有三个:一是某个类只能有一个实例;二是它必须自行创建这个实例;三是它必须自行向整个系统提供这个实例。
单例模式的优点:
1.在单例模式中,活动的单例只有一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就 防止其它对象对自己的实例化,确保所有的对象都访问一个实例
2.单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
3.提供了对唯一实例的受控访问。
4.由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。
5.允许可变数目的实例。
6.避免对共享资源的多重占用。
单例模式的缺点:
1.不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。
2.由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
3.单例类的职责过重,在一定程度上违背了“单一职责原则”。
4.滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
单例模式的实现:
一.懒汉模式
对于懒汉版的单例模式,需要注意的线程安全的问题,在考略到多线程的问题,我们需要的就是加锁。如果不加锁,当多个线程同时对这个类进行实例时,这个类就同时完成多份实例出来的对象。在加锁的情况下,需要考略效率问题,但完成一次实例之后,再次获取时,不需要加锁,我们需要的是双重判断。我们在防止拷贝及赋值等处理时,只申明不定义,同时还需要的是防止在类外进行定义,要设置为私有。
class Lock
{
public:
Lock(mutex& m)
:_mtx(m)
{
_mtx.lock();
}
~Lock()
{
_mtx.unlock();
}
protected:
Lock(const Lock&);
Lock& operator=(const Lock&);
private:
mutex& _mtx;
};
class Singleton
{
public:
static Singleton* Get()
{
if (Self == NULL) //提高效率
{
Lock lock(_mtx); //线程安全
if (Self == NULL)
{
Self = new Singleton;
}
}
return Self;
}
private:
Singleton()
{}
//防拷贝
Singleton(const Singleton& );
Singleton& operator=(const Singleton&);
static Singleton* Self;
static mutex _mtx;
};
Singleton* Singleton::Self = NULL;
mutex Singleton::_mtx;
二.饿汉模式
饿汉式单例类在类被加载时就将自己实例化,它的优点在于无须考虑多线程访问问题,可以确保实例的唯一性;从调用速度和反应时间角度来讲,由于单例对象一开始就得以创建,因此要优于懒汉式单例。但是无论系统在运行时是否需要使用该单例对象,由于在类加载时该对象就需要创建,因此从资源利用效率角度来讲,饿汉式单例不及懒汉式单例,而且在系统加载时由于需要创建饿汉式单例对象,加载时间可能会比较长。
class Singleton
{
static Singleton* Get()
{
assert(Self);
return Self;
}
//static Singleton& Get2()
//{
// static Singleton s;
// return s;
//}
protected:
Singleton()
{}
Singleton(const Singleton&);
Singleton& operator=(const Singleton&);
static Singleton* Self;
};
Singleton* Singleton::Self = new Singleton;
单例模式的应用
一些资源管理器常常设计成单例模式。
在计算机系统中,需要管理的资源包括软件外部资源,譬如每台计算机可以有若干个打印机,但只能有一个Printer Spooler, 以避免两个打印作业同时输出到打印机中。每台计算机可以有若干传真卡,但是只应该有一个软件负责管理传真卡,以避免出现两份传真作业同时传到传真卡中的情况。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
需要管理的资源包括软件内部资源,譬如,大多数的软件都有一个(甚至多个)属性(properties)文件存放系统配置。这样的系统应当由一个对象来管理一个属性文件。
需要管理的软件内部资源也包括譬如负责记录网站来访人数的部件,记录软件系统内部事件、出错信息的部件,或是对系统的表现进行检查的部件等。这些部件都必须集中管理,不可整出多头。
这些资源管理器构件必须只有一个实例,这是其一;它们必须自行初始化,这是其二;允许整个系统访问自己这是其三。因此,它们都满足单例模式的条件,是单例模式的应用。