【设计模式学习笔记四】【创建型模式】【单例模式(Singleton)】

本文是我学习刘伟技术博客的笔记,博客链接如下:

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)客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他路径访问该实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值