考虑用静态工厂方法代替构造器

      在Java的程序开发时,类与类之间的关系及其复杂,有依赖,关联,聚合,组合等等。而在处理某些关系时 —— 比如组合关系,我们往往在一个类中通过构造器(即new语句)创建一个实例,再通过实例调用相关的方法来对它进行操作。但是我们发现这样存在着很多局限,下面我们将具体谈一谈遇到类似情况是否有更好的解决方案。


      首先我们需要了解一下静态工厂方法和构造器的概念。


      静态工厂方法:它主要是在类的内部提供一个获取自身实例的静态的方法。例如下面的程序片段就是静态工厂方法的应用。


public class Provider{
	
	//私有构造器(这里用private主要防止通过构造器被实例化)
	private Provider(){
		System.out.println("This is a Provider!");
	}

	//静态工厂方法
	public static Provider getInstance(){
		return new Provider();
	}

}

      构造器:构建器最大的用处就是在创建对象时执行初始化。当创建一个对象时,系统会为这个对象的实例进行默认的初始化或者执行我们自己定义的构造器。它与静态工厂方法的主要区别在于将构造器以公共接口暴露给使用它的用户,而这势必会带来一个问题——在含有复杂参数列表的构造器中,如果用户不仔细阅读参数列表,往往不知所云。下面便是上述类对应的一个构造器实现。


public class Provider(){

	public Provider(){
		System.out.println("This is a Provider!");
	}
	
}

      了解了构造器和静态工厂方法之后,某些区别已经显而易见,可还有些区别需要我们在更复杂的应用中使用它们并发现各自的优缺点。那么问题来了,何时应用静态工厂方法,何时可以应用构造器呢?


      如题,我们优先考虑用静态工厂方法代替构造器。因为这样做会带来下面几点好处(也许在功能简单的应用中并不能体现这样做的优势,但是养成更加清晰和健壮的编程习惯无疑是不错的选择)。


      (1)静态工厂方法往往带有容易让用户理解的名字。在使用的时候我们可以不用纠结这个方法是做什么的,而构造方法却只能和类具有相同的名字(这点不多做说明,在理解概念的时候所写程序片段很能够说明这个问题)。


      在此我想补充一些惯用的静态工厂方法名称(大家都知道编程中总是会存在着很多不是规范的规范,不强制你一定这样,但是如果你这样做,代码的可读性会上升一个档次)。

      ①valueof —— 类型转换方法(返回的实例是通过参数来描述的)。

      ②of —— valueof的一种简洁的替代。

      ③getInstance —— 返回需要的类的实例(即通过参数列表来决定返回的实例,当然也可以没有参数)。

      ④newInstance —— 与getInstance不同的是,它返回的实例每次都是新的。

      ⑤getType —— 在不同的类中的时候使用,和getInstance作用相同。

      ⑥newType —— 在不同的类中的时候使用,与newInstance作用相同。


      (2)构造器在每次调用的时候均产生一个新的对象,而静态工厂方法则不然。


public class Provider{

	private static final Provider SingleProvider = new Provider();

	//私有构造器(这里用private主要防止通过构造器被实例化)
	private Provider(){
		System.out.println("This is a Provider!");
	}

	//静态工厂方法
	public static Provider getInstance(){
		return SingleProvider;
	}

}

      上述代码是对Provider的改进(实际上这是一个单例模式),我们有时候需要在不同的时间节点获取一个对象的实例,却又不希望每次都产生一个新的实例来耗费资源,这个时候静态工厂方法无疑是不错的选择。


      (3)静态工厂方法可以返回原返回对象的任何子类型的对象。通俗来讲,就是应用静态工厂方法创建实例的时候,可以直接创建该类的子类的实例。下面用程序片段进行说明。


interface Provider{
	public void method();
}

class ProviderA implements Provider{
	public void method(){
		//do something...
	}
}

public ProviderB implements Provider{
	public void method(){
		//do something...
	}
}

public class Providers{
	//原返回对象为Provider,现在分别为其子对象
	public static Provider getInstance(String str){
		if (str.equals("ProviderA")) {
			new ProviderA();
		}else if(str.equals("ProviderB")){
			new ProviderB();
		}
	}
}

      实际上,服务提供者框架很好地应用了这一点特性,下面进行说明。


      其实在上面的Providers类中编写静态方法时,我们发现,也许我们在写该静态方法时,返回对象所属的类根本不存在。在此需要说明,服务提供者框架就是在该理论基础上实现的。


//服务接口
public interface Service{

	//一系列描述服务内容的方法

}

//服务提供者接口
public interface Provider{

	//提供服务
	Service newService();

}

//注册和创建的非实例化类
public class Services {

	//私有构造器,防止通过构造器被实例化
    private Services(){

    }

    //用Map存放服务提供者和它的名字
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>(); Provider>();
    public static final String DEFAULT_PROVIDER_NAME = "<def>";

    //注册提供者
    public static void registerDefaultProvider(Provider provider) {
        registerProvider(DEFAULT_PROVIDER_NAME, provider);
    }
    //注册:将name和proviser put进Map
    public static void registerProvider(String name, Provider provider) {
        providers.put(name, provider);
    }

    //创建实例静态方法
    public static Service newInstance() {
        return newInstance(DEFAULT_PROVIDER_NAME);
    }

    public static Service newInstance(String name) {
        Provider provider = providers.get(name);
        if (provider == null)
            throw new IllegalArgumentException(
                    "No provider registered with name: " + name);
        return provider.newService();
    }
}

      在使用服务提供者框架的时候,首先需要注册(即调用registerDefaultProvider方法或者registerProvider方法),再进行实例化(即调用的的newInstance方法)。


    注:服务提供者框架在很多地方应用广泛,比如JDBC中,Connection就是它的服务接口,DriverManager.registerDriver是提供者注册API,DriverManager.getConnection就就是服务访问API,Driver是服务提供者接口。


      (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>();
}

      当然,计算机编程中不存在着“完美”之说,永远只是相对地更好。那么静态工厂方法也存在着它的缺陷。例如,类中如果不含有公有的或者受保护的构造器,它就不能被子类化(即带着私有构造函数的类不能子类化)。还比如,它和其他的静态方法本质上不存在区别,也就是说,我们不能够直接识别出它是为静态工厂方法还是普通的静态方法(而构造器则在API中明确地标识出来了)。


    以上仅为个人心得,站在了巨人的肩膀上,如有错误,敬请批评指正。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值