一个真实的项目:
这是一个真实的而向全球消费者的华尔街金融网站项目的一部分,系统根据用户选择的语言将网站的静态文字和动态文字全部转换为用户所选择的语言.这实际就是多国语的实现.
单例模式的精神是允许有限个实例,并不是仅允许一人实例,这种允许有限多个实例并向整个JVM提供自己实例的类叫做多例类,这种模式叫做多例模式.现在就需要用多例模式来实现资源对象,需要构造出能提供有限个实例,每个实例有各不相同的属性(即Locale代码)的代码.
一:什么是多例模式
比如每一台麻将都要两个骰子,这里就以这个为例说明多例模式的结构:
package cai.milenfan.basic.test;
import java.util.Date;
import java.util.Random;
public class Die {
private static Die die1 = new Die();
private static Die die2 = new Die();
private Die(){}
public static Die getInstance(int whichOne){
if(whichOne==1){
return die1;
}else{
return die2;
}
}
//掷骰子,返回一个在1-6之间的随机数
public synchronized int dice(){
Date d = new Date();
Random r = new Random(d.getTime());
int value = r.nextInt();
value = Math.abs(value);
value = value%6;
value += 1;
return value;
}
}
在多例类Die中,使用了饿汉方式创建了两个Die实例,根据静态工厂方法的参数,工厂方法返回两个实例中的一个。多例类是单例类的推广,而单例类是多例类的特殊情况.如同单例类可以分成有状态和没有状态两种一样,多例类也可以分成这两种状态。注:如果一个系统是建立在诸如EJB和RMI等分散技术之上的,那么多例类会出现数个实例,在这种情况下除非提供有效的协调机制,不然最好不要用有状态和可变的单例类.
二:下面就用多例模式来完成多国语
先来看下什么是Locale代码,一个Locale代码由语言代码和地区代码组合而成,如:
语言代码地区代码Locale代码说明
en US en_US美国英语
zh CH zh_CH简体汉语
fr FR fr_FR法国法语
Resource文件及其命名规范:
一个Resource文件是一个简单的文本文件,一个Resource文件的名字是由一个短文件名和文件扩展名properties组成的,而Resource文件的短文件名则是java程序在调用此文件时使用的文件名.一个Resource文件和一个普通的properties文件并无本质的区别,但java语言对两者的支持是区别的,java.util.Properties类不支持多语言,而java.util.ResourceBundle类则支持多语言。
接下来看如何使用Locale对象和ResourceBundle对象读取Resource文件:
Locale locale = new Locale("fr","FR");
ResourceBundle res = ResourceBundle.getBundle("shortname",locale);
在上面的例子里,res对象会加载一个名为shortname_fr_FR.properties的Resource文件.
现在有两个Resource文件:
为美国英语准备的Resource文件res_en_US.properties的内容如下:
USD=US Dollar
JPY=Japanese Yen
为简体中文准备的Resource文件res_zh_CH.properties的内容如下:
USD=美元
JPY=日元
下面来看如何用LingualResource来实现多国语:
package cai.milenfan.basic.test;
import java.util.HashMap;
import java.util.Locale;
import java.util.ResourceBundle;
public class LingualResource {
private String language="en";
private String region="US";
private String localeCode="en_US";
private static final String FILE_NAME="res";
private static HashMap instances = new HashMap(19);
private Locale locale = null;
private ResourceBundle resourceBundle = null;
private LingualResource lnkLingualResource;
private 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(makeLocaleCode(language,region),resourceBundle);
}
//返回一个Locale代码
private static String makeLocaleCode(String language,String region){
return language + "_" + region;
}
public synchronized static LingualResource getInstance(String language,String region){
if(instances.containsKey(makeLocaleCode(language,region))){
return (LingualResource)instances.get(makeLocaleCode(language,region));
}else{
return new LingualResource(language,region);
}
}
public String getLocaleString(String key){
return resourceBundle.getString(key);
}
}
客户端测试程序如下:
package cai.milenfan.basic.test;
public class LongualResourceTester {
public static void main(String[] args){
//LingualResource ling = LingualResource.getInstance("en","US");
LingualResource ling = LingualResource.getInstance("zh","CN");
String usDollar = ling.getLocaleString("USD");
System.out.println("USD=" + usDollar);
}
}
为什么国际化又叫做i18n?
这是一个有趣的问题,因为国际的英文单词是Internationalization,第一个字母i和最后一个字母n之间有18个字母,因此取名为i18n.