设计模式之静态工厂方法

工厂模式非常实用,但是为每一个类创建一个工厂方法方法类会引起工厂类的泛滥,此时,我们可以使用静态工厂方法来避免--在每个类里实现一个静态的工厂方法,就不需要额外的工厂类了。静态工厂方法在"Effective Java" 一书中有详细的介绍,我们也经常使用它们。例如,在Java 5版本里,为创建基本类型Integer、Long、Boolean对象都提供了静态工厂方法,以Integer类为例,它的静态工厂方法如下所示。

  1. public static Integer valueOf(int i) {  
  2.     if(i >= -128 && i <= IntegerCache.high)  
  3.         return IntegerCache.cache[i + 128];  
  4.     else  
  5.         return new Integer(i);  
  6. }  

代码注解

java.lang.Integer.IntegerCache.high 默认值是127,可以通过设置VM的启动参数值来设定(大于127才有意义)。如果在[-128, IntegerCache.high]之间,则返回IntegerCache类缓存的Integer对象,否则创建一个新的Integer对象。

这里需要注意的是,由于Integer是不可变(immutable)对象,所以这些缓存对象不会引起计算上的错误。

静态工厂方法的优缺点

优点

可以为静态工厂选择合适的命名,提高程序的可读性。一段优秀的代码具有可读性,并不一定需要冗长的注释,有时候根据类名、方法名、变量名的良好命名更容易使读者读懂程序。

静态工厂和工厂模式一样,可以封装复杂的初始化过程,实现实例的缓存。

还可以根据不同的输入返回不同实现类/具体类对象

缺点

一般为了强迫使用工厂方法,不直接使用构造方法来构造实例,我们会强迫类只含有私有构造方法,这样,会导致此类不能被子类化。

如果添加了一个新的该类的子类(该类有非私有的构造方法),此静态工厂方法可能需要重写,以加入该子类的实例化过程,导致扩展性不强。

静态方法没有面向对象的特征,比如继承、动态多态等,不可被覆写(Overwritten)。

一篇文章:

创建类的实例的最常见的方式是用new语句调用类的构造方法。在这种情况下,程序可以创建类的任意多个实例,每执行一条new语句,都会导致Java虚拟机的堆区中产生一个新的对象。假如类需要进一步封装创建自身实例的细节,并且控制自身实例的数目,那么可以提供静态工厂方法。

例如Class实例是Java虚拟机在加载一个类时自动创建的,程序无法用new语句创建java.lang.Class类的实例,因为Class类没有提供public类型的构造方法。为了使程序能获得代表某个类的Class实例,在Class类中提供了静态工厂方法forName(String name),它的使用方式如下:

Class c=Class.forName("Sample"); //返回代表Sample类的实例

静态工厂方法与用new语句调用的构造方法相比,有以下区别。

(1)构造方法的名字必须与类名相同。这一特性的优点是符合Java语言的规范,缺点是类的所有重载的构造方法的名字都相同,不能从名字上区分每个重载方法,容易引起混淆。

静态工厂方法的方法名可以是任意的,这一特性的优点是可以提高程序代码的可读性,在方法名中能体现与实例有关的信息。例如例程11-5的Gender类有两个静态工厂方法:getFemale()和getMale()。

例程11-5 Gender.java

public class Gender{

private String description;

private static final Gender female=new Gender("女");

private static final Gender male=new Gender("男");


private Gender(String description){this.description=description;}


public static Gender getFemale(){

return female;

}


public static Gender getMale(){

return male;

}

public String getDescription(){return description;}

}

这一特性的缺点是与其他的静态方法没有明显的区别,使用户难以识别类中到底哪些静态方法专门负责返回类的实例。为了减少这一缺点带来的负面影响,可以在为静态工厂方法命名时尽量遵守约定俗成的规范,当然这不是必需的。目前比较流行的规范是把静态工厂方法命名为valueOf或者getInstance。

l valueOf:该方法返回的实例与它的参数具有同样的值,例如:

Integer a=Integer.valueOf(100); //返回取值为100的Integer对象

从上面代码可以看出,valueOf()方法能执行类型转换操作,在本例中,把int类型的基本数据转换为Integer对象。

(public static Integer valueOf(int i)
Returns an Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values. This method will always cache values in the range -128 to 127, inclusive, and may cache other values outside of this range.
Parameters:
i - an int value.
Returns:
an Integer instance representing i.



l getInstance:返回的实例与参数匹配,例如:

//返回符合中国标准的日历

Calendar cal=Calendar.getInstance(Locale.CHINA);

(2)每次执行new语句时,都会创建一个新的对象。而静态工厂方法每次被调用的时候,是否会创建一个新的对象完全取决于方法的实现。

(3)new语句只能创建当前类的实例,而静态工厂方法可以返回当前类的子类的实例,这一特性可以在创建松耦合的系统接口时发挥作用,参见本章11.3.5节(松耦合的系统接口)。

静态工厂方法最主要的特点是:每次被调用的时候,不一定要创建一个新的对象。利用这一特点,静态工厂方法可用来创建以下类的实例。

l 单例类:只有惟一的实例的类。

l 枚举类:实例的数量有限的类。

l 具有实例缓存的类:能把已经创建的实例暂且存放在缓存中的类。

l 具有实例缓存的不可变类:不可变类的实例一旦创建,其属性值就不会被改变。

在下面几节,将结合具体的例子,介绍静态工厂方法的用途。

11.3.1 单例(singleton)类
单例类是指仅有一个实例的类。在系统中具有惟一性的组件可作为单例类,这种类的实例通常会占用较多的内存,或者实例的初始化过程比较冗长,因此随意创建这些类的实例会影响系统的性能。


Tips

熟悉Struts和Hibernate软件的读者会发现,Struts框架的ActionServlet类就是单例类,此外,Hibernate的SessionFactory和Configuration类也是单例类。

 

例程11-6的GlobalConfig类就是个单例类,它用来存放软件系统的配置信息。这些配置信息本来存放在配置文件中,在GlobalConfig类的构造方法中会从配置文件中读取配置信息,并把它存放在properties属性中。

例程11-6 GlobalConfig.java

import java.io.InputStream;

import java.io.FileInputStream;

import java.io.IOException;

import java.util.Properties;


public class GlobalConfig {

private static final GlobalConfig INSTANCE=new GlobalConfig();

private Properties properties = new Properies();

private GlobalConfig(){

try{

//加载配置信息

InputStream in=getClass().getResourceAsStream("myapp.properties");

properties.load(in);

in.close();

}catch(IOException e){throw new RuntimeException("加载配置信息失败");}

}

public static GlobalConfig getInstance(){ //静态工厂方法

return INSTANCE;

}

public Properties getProperties() {

return properties;

}

}

实现单例类有两种方式:

(1)把构造方法定义为private类型,提供public static final类型的静态变量,该变量引用类的惟一的实例,例如:

public class GlobalConfig {

public static final GlobalConfig INSTANCE =new GlobalConfig();

private GlobalConfig() {…}





这种方式的优点是实现起来比较简捷,而且类的成员声明清楚地表明该类是单例类。

(2)把构造方法定义为private类型,提供public static类型的静态工厂方法,例如:

public class GlobalConfig {

private static final GlobalConfig INSTANCE =new GlobalConfig();

private GlobalConfig() {…

}

public static GlobalConfig getInstance(){return INSTANCE;}



}

这种方式的优点是可以更灵活地决定如何创建类的实例,在不改变GlobalConfig类的接口的前提下,可以修改静态工厂方法getInstance()的实现方式,比如把单例类改为针对每个线程分配一个实例,参见例程11-7。

例程11-7 GlobalConfig.java

package uselocal;

public class GlobalConfig {

private static final ThreadLocal<GlobalConfig> threadConfig=

new ThreadLocal<GlobalConfig>();

private Properties properties = null;

private GlobalConfig(){…}


public static GlobalConfig getInstance(){

GlobalConfig config=threadConfig.get();

if(config==null){

config=new GlobalConfig();

threadConfig.set(config);

}

return config;

}

public Properties getProperties() {return properties; }

}

以上程序用到了ThreadLocal类,关于它的用法参见第13章的13.14节(ThreadLocal类)。

转自:http://bbs.csdn.net/topics/120039455

原文地址:https://www.cnblogs.com/youxin/p/3190674.html