http://blog.csdn.net/lovelion/article/details/17517213
我做的这部分笔记,鉴于自身水平有限,我只是对上面博客内做进一步提炼和记录,方便自己查阅。同时,更多的是对设计模式的框架进行学习,大部分细节都将略去,让自己侧重对每个设计模式框架的理解。
我应该理解和掌握的:
1)能够画出这个设计模式的架构框图;
2)能够根据架构框图写出对应的伪代码;
3)这个模式的应用场景,主要优缺点。
1.单例模式
(1)定义
单例模式:保证一个类只有一个实例,而且自行实例化并提供一个访问它的全局访问点。这个类称为单例类。
单例模式结构图中只包含一个单例角色:
参与者:Singleton
在单例内部实现只生成一个实例,定义一个GetInstance()操作,允许客户访问它的唯一实例。防止在外部对其实例化,将其构造函数设计为私有。在单例内部定义一个Singleton类型的静态对象,作为外部共享的唯一实例。
看图写代码:
class Singleton
{
public:
static Singleton* getInstance(); //定义一个静态获取对象的操作
private:
Singleton(){} //私有化构造函数,是外部不能实例化这个单例类
private:
static Singleton* _instance; //定义静态对象
};
//单例类的实现
Singleton* Singleton::_instance = 0; //初始化
Singleton* Singleton::getInstance() //使用lazy惰性初始化,懒汉模式,直到第一次被访问时才创建
{
if(_instance == 0)
{
_instance = new Singleton();
}
return _instance;
}
以上单例模式,大部分情况下都可以正常使用,若要进一步研究,可以参考以下博文:
http://blog.csdn.net/hackbuteer1/article/details/7460019
上面博文,是为了解决_instance指向的空间释放问题,我们直接取出解决方案的代码,详情请参考以上博文:
class Singleton
{
public:
static Singleton& getInstance()//定义一个静态获取对象的操作
{
/*
程序在结束的时候,系统会自动析构所有的全局变量。
事实上,系统也会析构所有的类的静态成员变量,就像这些静态成员也是全局变量一样。
利用这个特征,我们可以在单例类中定义一个这样的静态成员变量,
而它的唯一工作就是在析构函数中删除单例类的实例。
*/
static Singleton _instance;
return _instance;
}
void prin(){cout<<"singleton";}//test
private:
Singleton(){} //私有化构造函数,是外部不能实例化这个单例类
Singleton(const Singleton &);//声明并不实现,禁止类拷贝和类赋值
Singleton & operator = (const Singleton &);//声明并不实现,禁止类拷贝和类赋值
};
//测试
int main()
{
Singleton& p = Singleton::getInstance();//获取单例对象
p.prin(); //调用成员函数
return 0;
}
下面我们再来看看jave实现单例模式中的饿汉模式和懒汉模式
1)饿汉式单例类
结构图如下:
在定义静态变量的时候就实例化单例类,因此类加载的时候已经创建了单例类,代码如下:
class EagerSingleton{
private static final EargerSingleton _instance = new EargerSingleton(); //定义的时候就已经实例化
private EargerSingleton(){}
public static EargerSingleton getInstance(){
return _instance;
}
}
2)懒汉式单例类
懒汉式单例类是在第一次加载类对象的时候,才去实例化。为了避免多线程问题,我们需要对getInstance进行加锁,防止创建多个实例。下面是双重检查锁定:
看图出代码:
class LazySingleton{
private volatile static LazySingleton instance = null;
private LazySingleton(){}
public static LazySingleton getInstance(){
//第一重判断
if(instance == null){
//锁定代码块
synchronized(LazySingleton.class){
//第二重判断
if(instance == null){
instance = new LazySingleton();
}
}
}
return instance;
}
}
3)饿汉与懒汉的比较
饿汉式单例类在类被加载时就将自己实例化,它的优点在于无须考虑多线程访问问题,可以确保实例的唯一性。
懒汉式单例类在第一次使用时创建,无须一直占用系统资源,实现了延迟加载,但是必须处理好多个线程同时访问的问题,特别是当单例类作为资源控制器,在实例化时必然涉及资源初始化,而资源初始化很有可能耗费大量时间,这意味着出现多线程同时首次引用此类的机率变得较大,需要通过双重检查锁定等机制进行控制,这将导致系统性能受到一定影响。
4)更好的方法
结合两者的特点,在单例类内部增加一个静态内部类,让他持有单例类的对象。这被称为IoDH,代码如下:
class Singleton{
private Singleton(){
}
private static class HolerClass{
private final static Singleton instance = new Singleton();
}
public static Singleton getInstance(){
return HolerClass.instance;
}
}
由于静态单例对象没有作为Singleton的成员变量直接实例化,因此类加载时不会实例化Singleton,第一次调用getInstance()时将加载内部类HolderClass,在该内部类中定义了一个static类型的变量instance,此时会首先初始化这个成员变量,由Java虚拟机来保证其线程安全性,确保该成员变量只能初始化一次。由于getInstance()方法没有任何线程锁定,因此其性能不会造成任何影响。
通过使用IoDH,我们既可以实现延迟加载,又可以保证线程安全,不影响系统性能,不失为一种最好的Java语言单例模式实现方式(其缺点是与编程语言本身的特性相关,很多面向对象语言不支持IoDH)。
(2)总结1)单例模式提供了对唯一实例的受控访问。
2)系统内存中只存在一个对象,因此可以节约系统资源。
3)单例没有抽象层,扩展困难;
4)职责过重,即充当工厂角色,又充当产品角色。
(3)使用场景
1)系统只需要一个实例对象。
2)客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他路径访问该实例。