多例模式是相对单例模式而言的。单例模式有且仅有一个实例,但是多例模式,顾问思义:允许存在有限个实例。 什么叫“有限个实例”? 就是说:有多少实例,我们是知道的,并不是不可以预知的, 如果一个类的构造函数是public 的,那么在任意地方都可以通过调用构造函数来创建实例,那么这样的实例是我们不能预知的。这是有上限多例模式,但是多例模式还有一种无上限多例模式。因此,多例模式有以下特点:
首先,我们来看一个多例模式的例子。先来一个场景,相信大家都知道打麻将吧。每盘开局的时候,都会掷骰子,那么骰子只有2粒(大家没见过还有3粒的吧),把这句抽象一下,就是:骰子的个数是确定的,是有限个。 那么这个就符合我们多例模式的运用场景了。
package com.pattern.multilingual; import java.util.Date; import java.util.Random; public class Die { /** * 创建2个实例 */ private static Die die1 = new Die(); private static Die die2 = new Die(); /** * 私有构造子 */ private Die() { } /** * 工厂方法 * @param whichOne * @return */ public static Die getInstance(int whichOne) { if (whichOne == 1) { return die1; } return die2; } /** * 掷骰子 * @return */ public synchronized int dice(){ Date d = new Date(); Random r = new Random(d.getTime()); int value = r.nextInt(); value = Math.abs(value)%6+1; return value; } }
测试类:
package com.pattern.multilingual; public class Client { private static Die die1,die2; public static void main(String[] args) throws InterruptedException{ die1 = Die.getInstance(1); die2 = Die.getInstance(2); System.out.println(die1.dice()); Thread.sleep(2000); System.out.println(die2.dice()); } }
无上限多例模式:事先并不知道实例的具体个数,因为实例个数无法预知,因为通常的做法是用集合来存储实例。下面来看一段国际化的例子:
package com.pattern.multilingual; import java.util.HashMap; import java.util.Locale; import java.util.ResourceBundle; public class LingualResource { private String localeCode = "en_US"; private static final String FILE_NAME = "test"; private static HashMap<String,LingualResource> instances = new HashMap<String,LingualResource>(19); private Locale locale = null; private ResourceBundle bundle = null; /** * 构建LocaleCode * @param language * @param region * @return */ private static String makeLocaleCode(String language,String region){ return new StringBuilder(language).append("_").append(region.toUpperCase()).toString(); } /** * 构造子 * @param language * @param region */ private LingualResource(String language,String region){ localeCode = makeLocaleCode(language,region); this.locale = new Locale(language,region); bundle =ResourceBundle.getBundle(FILE_NAME,locale); instances.put(localeCode, this); } private LingualResource(){} /** * 工厂方法 * @param language * @param region * @return */ public synchronized static LingualResource getInstance(String language,String region){ if(instances.get(makeLocaleCode(language,region)) != null){ return instances.get(makeLocaleCode(language,region)); } return new LingualResource(language,region); } /** * 国际化方法 * @param code * @return */ public String getLocaleString(String code){ return this.bundle.getString(code); } }
测试类:
package com.pattern.multilingual; public class ResourceTest { public static void main(String[] args){ LingualResource ling = LingualResource.getInstance("en", "US"); String usName = ling.getLocaleString("name"); LingualResource lingCN = LingualResource.getInstance("zh","CN"); String cnName = lingCN.getLocaleString("name"); System.out.println("usName:" + usName); System.out.println("cnName:" + cnName); } } 资源文件:test_en_US.properties name=john age=20 test_zh_CN.protites name=\u7EA6\u7FF0 age=20
指印结果如下:
usName:john cnName:约翰
其实在我们的开发中,其实很少这样写,通常像这种情况,可能在我们需要国际化的地方直接 new()的方式创建实例,相比之下采用这种方式,可以减少内存开支,省去了反复创建对象和回收对象的麻烦。
ps:在《JAVA与模式》一书中260页,这个国际化的例子,有错误,比如他的工厂方法是错误,而且存在大量的多余变量,部分问题考虑的很是不周到,有兴趣的朋友可以自己翻书对照一下,我的代码与书的代码。
同时,发现在本书中存在大量的错误,不知道是笔者手误,还是印刷出版社的印刷错误,希望大家在看此书时,抱着审视的态度。“尽信书则不如无书”,不过此书的例子仍然非常棒,这仍然是一本不错的书籍。
ps:多例模式,这个多例包括1个实例,因此多例模式是单例模式的推广,单例模式是多例模式的特例,另外给大家科普点小知识,为什么国际化(Internationalization)简称i18N? 那是因为I和n之间有18个字母,挺逗的命名。