1 静态工厂方法 与 构造器 是什么?
假设我们有一个学生类Animal
-
public class Animal{
-
//your code
-
}
我们获取这个类的示例 最常用的方式就是使用这个类公有的构造器,每个类默认会有一个无参的构造器
-
public class AnimalTest {
-
-
public static void main(String [] args){
-
Animal animal = new Animal();
-
}
-
-
}
还有另外一种方法,也就是我们需要了解的静态工厂方法(static factorymethod),它是一个返回类实例的静态方法。那么我们就给Student类一个静态工厂方法
-
public class Animal{
-
private static Animal s = new Animal();
-
public static Animal getInstance(){
-
return animal;
-
}
-
}
-
public class AnimalTest {
-
-
public static void main(String [] args){
-
Animal animal = new Animal();
-
Animal animal2 = Animal.getInstance();
-
}
-
-
}
说明这里面的静态工厂方法并不是设计模式中的工厂模式
2 静态工厂方法 与 构造器 的对比
我们可以通过类的静态工厂方法来获得示例,而不是单独是构造器。
2.1 使用 静态工厂方法 的优点
2.1.1 静态工厂方法是有名称的
与构造器相比,静态工厂方法是有名称的。 如果构造器的参数不能够明确的描述返回的对象,我们使用有名称的静态工厂方法就是比较好的。我们可以通过名称的含义来理解这个静态工厂方法返回类的示例。
这种方法尤其在一种情况下很突出,当一个类只能有一个带有签名的构造器。我们都是通过修改两个构造器中的参数列表中的参数类型顺序。面对这样的API 使用起来是很麻烦的,由于静态工厂方法 是有名称的,所以不受这个限制。
2.1.2 不必每次调用都创建新的对象
使用静态的工厂方法,我们可以预先构造好类的实例,或者将写好的实例放入缓存中,进行重复的利用。
静态的工厂方法能够进行重复的调用而返回相同的对象,这可以帮助类控制那些时刻哪些实例应该存在。这种类型的类被称为 示例受控制的类(instance-controlled),写这样的类有几个原因:
确保这个类是单例模式(Singleton) 或者不可实例化的
可是使用 "==" 操作符来代替 equals(Object)方法,提高性能
-
public class AnimalTest {
-
public static void main(String [] args){
-
Animal animal = Animal.getInstance();
-
Animal animal1 = Animal.getInstance();
-
System.out.println(animal == animal1);
-
}
-
}
2.1.3 可以返回类子类的对象 *
这个特性是非常灵活的,我们先试图想象三个组件 服务接口(用于用户获得实例)、 提供者注册API(服务器注册的实例)、 服务访问API(用于用户访问,获取) .其中还有一个可选的组件 服务提供者接口(用于创建实例)。这个就是基本的服务提供者框架,它也可以变化为 适配器(Adapter) 模式
说明:如果有web开发经验的可以把这个当作一个用户拿不同的数据访问 action ,而action 返回不同的视图。而第四个组件我们可以想象成一个Spring 的 base 实例化 action 对象。
这里提供一个完整的代码,希望大家能够自己拿去运行一下、拓展一下。
-
/**
-
*服务访问API
-
*/
-
public interface IAnimal {
-
}
-
-
/**
-
*服务提供者接口
-
*/
-
public interface IProvider {
-
IAnimal ianimal();
-
}
-
-
public class Animal{
-
-
//构造方法私有化,客户端不能够使用构造器获得对象
-
private Animal(){}
-
-
//根据名称放入相应的实例
-
private static final Map<String,IProvider> providers= new ConcurrentHashMap<String,IProvider>();
-
public static final String DEFAULT_IPROVIDER_NAME = "<dfe>";
-
-
//用户注册实例的接口 提供者注册API
-
public static void registerDefaultProvider(IProvider p){
-
registerProvider(DEFAULT_IPROVIDER_NAME,p);
-
}
-
-
public static void registerProvider(String name,IProvider p){
-
providers.put(name, p);
-
}
-
-
//用户获得实例的接口 服务接口
-
public static IAnimal newInstance(){
-
return newInstance(DEFAULT_IPROVIDER_NAME);
-
}
-
-
public static IAnimal newInstance(String name){
-
IProvider p = providers.get(name);
-
if(p == null)
-
throw new IllegalArgumentException(
-
"没有实例注册这个名称:"+name);
-
return p.ianimal();
-
}
-
-
}
-
-
public class Provider implements IProvider{
-
-
private IAnimal iAnimal;
-
-
public Provider(IAnimal iAnimal){
-
this.iAnimal = iAnimal;
-
}
-
-
@Override
-
public IAnimal ianimal() {
-
return iAnimal;
-
}
-
-
}
-
-
public class Person implements IAnimal{
-
@Override
-
public String toString() {
-
return "Person 实例";
-
}
-
}
-
-
public class Dog implements IAnimal{
-
@Override
-
public String toString() {
-
return "Dog 实例";
-
}
-
}
-
-
public class AnimalTest {
-
public static void main(String [] args){
-
Animal.registerProvider("dog", new Provider(new Dog()));
-
-
Animal.registerProvider("person", new Provider(new Person()));
-
-
IAnimal dog = Animal.newInstance("dog");
-
IAnimal person = Animal.newInstance("person");
-
-
System.out.println(dog.toString());
-
System.out.println(person.toString());
-
}
-
}
-
2.1.4 创建参数化类型实例更加简洁
在创建参数化类型实例的时候,静态工厂方法 会让代码变得更加的简洁
在调用参数化的构造器的时,即时类型参数很明显,也必须指明。通常要接连提供两次类型参数
Map<String,List<String>> m = new HashMap<String,List<String>>();
这个类型的声明太过于冗长,假设HashMap 有一个这样的静态工厂方法:
-
public static <K, V> HashMap<K, V> newInstance(){
-
return new HashMap<K, V>;
-
}
这样我们就可以这样声明HashMap 了
Map<String,List<String>> m = HashMap.newInstance()
2.2 使用 静态工厂方法 的缺点
2.2.1 可能类不能实例化
类如果实现静态的工厂方法,而不含有公有的或者受保护的构造器,就不能够被实例化。这也鼓励我们多使用 复合(composition) 而不是继承
2.2.2 与其他的静态方法没有区别
静态工厂方法在API 文档中没有像构造器一样明确的表示出来,所以,对于静态工厂方法而不是构造器的类来说,想要看清楚是一件不简单的事情。在javadoc工具没有注意到静态工厂方法前,我们通过命名来区分:
valueOf -- 这个方法返回的实例与它的参数具有相同的值,这样的静态工厂方法实际上是用来做类型转换的。
of -- valueOf的简写,在EnumSet 开始使用流行
getInstance -- 返回的实例是通过方法的参数来描述的,但是不能够说与参数具有相同的值。对于Singleton来说这个方法没有参数,并返回唯一的实例
newInstance -- 像getInstance一样,但newInstance确保返回的每个实例都与其他实例不同。
getType -- 像getInstance一样,但是在工厂方法处于不同的类中的时候使用。type表示工厂方法所返回的对象类型
newType -- 像newInstance一样,但是在工厂方法处于不同的类中的时候使用。type表示工厂方法所返回的对象类型
3 总结
静态工厂方法和公有构造器是各有用处的,但是我们需要理解他们的长处。静态工厂方法通常更加的合适,希望我们以后第一时间不是考虑构造器,而是想想静态工厂方法。