设计模式学习笔记-单例(Singleton)跟多例(Multiton)

设计模式学习笔记--单例(Singleton)和多例(Multiton)


写在模式学习之前


       什么是设计模式:在我们进行程序设计时,逐渐形成了一些典型问题和问题的解决方案,这就是软件模式;每一个模式描述了一个在我们程序设计中经常发生的问题,以及该问题的解决方案;当我们碰到模式所描述的问题,就可以直接用相应的解决方法去解决这个问题,这就是设计模式。

       设计模式就是抽象出来的东西,它不是学出来的,是用出来的;或许你根本不知道任何模式,不考虑任何模式,却写着最优秀的代码,即使以“模式专家”的角度来看,都是最佳的设计,不得不说是“最佳的模式实践”,这是因为你积累了很多的实践经验,知道“在什么场合代码应该怎么写”,这本身就是设计模式。

       有人说:“水平没到,学也白学,水平到了,无师自通”。诚然,模式背熟,依然可能写不出好代码,更别说设计出好框架;OOP理解及实践经验到达一定水平,同时也意味着总结了很多好的设计经验,但"无师自通",却也未必尽然,或者可以说,恰恰是在水平和经验的基础上,到了该系统的学习一下“模式”的时候了,学习一下专家总结的结果,印证一下自己的不足,对于提高水平还是很有帮助的。

       本系列的设计模式学习笔记,实际是对于《Java与模式》这本书的学习记录。


单例(Singleton)模式


(1)一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类。
(2)单例类的一个最重要的特点是类的构造方法是私有的,从而避免了外部利用构造方法直接创建多个实例。


饿汉式单例类


public class EagerSingleton
{
	private static final EagerSingleton instance = new EagerSingleton();
	private EagerSingleton() {}
	//静态工厂方法法
	public static EagerSingleton getInstance() {return instance;}
}


懒汉式单例类


public class LazySingleton
{
	private static LazySingleton instance = null;
	private LazySingleton() {}
	//静态工厂方法法
	public static synchronized LazySingleton getInstance()
	{
		if(instance == null)
		{
			instance = new LazySingleton();
		}
		return instance;
	}
}

双重检查成例的研究


有些人为了克服懒汉式单例类中,synchronized(这是为了避免多线程场景下返回多个实例)带来的性能损失,提出了双重检查成例,代码如下:

public class DoubleCheckedSingleton
{
	private static DoubleCheckedSingleton instance = null;
	private DoubleCheckedSingleton() {}
	//静态工厂方法法
	public static DoubleCheckedSingleton getInstance()
	{
		if(instance == null)
		{
			//此处同一时刻可能多个线程到达
			synchronized(DoubleCheckedSingleton.class)	
			{
				//此处同一时刻只能一个线程到达
				if(instance == null)
				{
					instance = new DoubleCheckedSingleton();
				}
			}
		}
		return instance;
	}
}
这个代码在Java中不能工作,因为在Java编译器中,DoubleCheckedSingleton类的初始化和instance变量赋值的顺序不可预料,有可能导致空指针引用异常导致程序崩溃。


更多了解


(1)还有一种不常用的“登记式单例类”;

(2)JDK中的单例类:java.lang.Runtime、java.awt.Tookit、javax.swing.TimerQueue等;

(3)单例模式和静态类:虽然一个全部由静态方法组成的类,完全可以实现单例模式的全部功能,但是只有极为通用的功能才可以被设计为静态,其余的场景,对于“一个类只有一个实例”的情况,则应该使用单例。静态类是没有实例化的,系统启动时就被分配在方法区内存上,这是个类结构、静态属性方法、常量存储的地方,别称“非堆”、“永久代”;单例是在new时,分配在堆内存上。


多例(Multiton)模式


作为对象的创建模式,多例模式中的多例类可以有多个实例,而且多例类必须自己创建、管理自己的实例,并向外界提供自己的实例。


多例模式的特点


所谓的多例模式(Multiton Pattern),实际上就是单例模式的自然推广。作为对象的创建模式,多例模式或多例类有如下的特点:

(1)多例类可有多个实例

(2)多例类必须自己创建、管理自己的实例,并向外界提供自己的实例。

(3)根据是否有实例上限分为:有上限多例类和无上限多例类。


一个多语言支持的多例模式代码例子

import java.util.*;
class LingualResource
{
	private String languages = "en";
	private String region = "US";
	private String localeCode = "en_US";
	private static final String FILE_NAME = "res";
	private static Map<String,LingualResource> instances = new HashMap<String,LingualResource>();
	private Locale locale = null;
	private ResourceBundle resourceBundle = null;
	private LingualResource lingualResource;

	private LingualResource(String language,String region)
	{
		this.localeCode = language;
		this.region = region;
		localeCode = makeLocaleCode(language,region);
		locale = new Locale(language,region);
		resourceBundle = ResourceBundle.getBundle(FILE_NAME,locale);
		instances.put(localeCode,this);
	}
	private LingualResource() {}

	public synchronized static LingualResource getInstance(String language,String region)
	{
		if(instances.get(makeLocaleCode(language,region)) != null)
		{
			return instances.get(makeLocaleCode(language,region));
		} else {
			return new LingualResource(language,region);
		}
	}
	public String getLocaleString(String code)
	{
		return resourceBundle.getString(code);
	}
	private static String makeLocaleCode(String language,String region)
	{
		return language + "_" + region;
	}
}

class LingualResourceTest
{
	public static void main(String[] args)
	{
		LingualResource lr = LingualResource.getInstance("en","US");
		String usDollar = lr.getLocaleString("USD");
		System.out.println("USD=" + usDollar);
	}
}


定义一个属性文件:res_en_US.properties,内如:USD=US Dollar,即可看到演示效果。


一个根据语言代码和地区代码格式化数字的多例模式例子

import java.text.*;
import java.util.*;
class NumberFormatTest
{
	public static void displayNumber(Double d,Locale l)
	{
		NumberFormat nf;
		String dOut;
		nf = NumberFormat.getNumberInstance(l);
		dOut = nf.format(d);
		System.out.println(dOut + " " + l.toString());
	}
	public static void main(String[] args)
	{
		displayNumber(1234567.89,new Locale("en","US"));
		displayNumber(1234567.89,new Locale("de","DE"));
		displayNumber(1234567.89,new Locale("fr","FR"));
		displayNumber(1234567.89,new Locale("zh","CN"));
	}
}

类似的,可以同样方式格式化货币(getCurrencyInstance)、格式化百分比(getPercentInstance)等。


创建模式(Creational Pattern)小结


创建模式(Creational Pattern)一共有七种,分布是:简单工厂模式、工厂方法模式、抽象工厂模式、建造模式、原型模式、单例模式、多例模式。

设计模式学习笔记-单例(Singleton)跟多例(Multiton)

本篇介绍了其中的2种:单例和多例。