第1条:考虑用静态工厂方法代替构造器

Posted 没有梦想的小灰灰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第1条:考虑用静态工厂方法代替构造器相关的知识,希望对你有一定的参考价值。

为了获得一个类的实例,有两种办法1.类提供一个公有的构造器 2.类提供一个公有的静态工厂方法。

 

静态工厂方法的优势:

1.有名称。

慎重地选择方法名称能突出多个构造器的区别,例如使用BigInteger(int, int, Random)构造器,返回的BigInteger可能为素数,如果用

BigInteger.probalePrime(int, Random)静态工厂方法,显得更为清楚。

 

2.不必在每次调用的时候都创建一个新的对象。

Boolean类的代码中有public static final Boolean TRUE = new Boolean(true)  这样一个TURE常量,它在类加载的时候就把这个常量初始化,在valueOf方法中每次返回

的都是这个常量。

public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}

 

3.可以返回原返回类型的任何子类型的对象。(多态)

 java.util.EnumSet没有公有构造器,只有静态工厂方法,返回两种实现类之一,具体取决于底层枚举类型的大小,如果元素有64个或更少,返回一个RegualrEumSet实例,用单个long支持,如果多于64个,则返回JumboEnumSet实例,用long数组进行支持。

public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
        Enum<?>[] universe = getUniverse(elementType);
        if (universe == null)
            throw new ClassCastException(elementType + " not an enum");

        if (universe.length <= 64)
            return new RegularEnumSet<>(elementType, universe);
        else
            return new JumboEnumSet<>(elementType, universe);
}

//RegularEnumSet用单个long支持
private long elements = 0L;
//JumboEnumSet用long数组支持
private long elements[];

RegualrEumSet和JumboEnumSet这两个实现类对客户来说不可见,客户只关心的是它是某个EnumSet的某个子类,如果RegularEnumSet不能给小的枚举类型提供性能优势,就有可能从未来的发行版中将它删除,不会对客户造成任何影响,如果在未来的发行版中要添加第三第四个甚至更多EnumSet实现,对客户也没有任何影响。

 

一个服务提供者的框架:

//服务接口,由服务提供者实现
public interface Service {
    //服务方法...
}

//服务提供者接口,由服务提供者实现
public interface Provider {
    Service newService();
}

//一个服务注册和获得服务的不可实例化类
public class Services {
    private Services() {};
    
    //服务提供者名-服务提供者的映射
    private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
    
    public static final String DEFAULT_PROVIDER_NAME = "<def>";
    
    //服务提供者注册API,系统用来注册实现
    public static void registerDefaultProvider(Provider p) {
        registerProvider(DEFAULT_PROVIDER_NAME, p);
    }
    public static void registerProvider(String name, Provider p) {
        providers.put(name, p);
    }
    
    //服务访问API,客户用来获取服务实例
    public static Service newInstance() {
        return newInstance(DEFAULT_PROVIDER_NAME);
    }
    public static Service newInstance(String name) {
        Provider p = providers.get(name);
        if(p == null)
            throw new IllegalArgumentException("No provider registered with name: " + name);
        return p.newService();
    }
    
}

结合JDBC的数据库连接服务理解:Connection是服务接口,DriverManager.registerDriver是提供者注册API,DriverManager.getConnection是服务访问API,Driver就是服务提供者接口。

这样的实现使得Services类(在JDBC中,即是DriverManager类)的代码几乎不需要修改了(就算是修改也不会把框架改了,只会优化,对外的接口是不会变的),现在来了一个新的数据库厂商,该厂商只需要提供实现了Provider接口的服务提供者,实现Service接口的服务,就可以了,不会对客户造成任何影响,因为客户根本不知道多了一个新的数据库。

 

4.在创建参数化类型实例的时候,使代码变得更加简洁。

Map<String, List<String>> m = new HashMap<String, List<String>>()

调用参数化类型时,需要两次提供类型参数<String, List<String>>

假如HashMap提供了这样一个静态工厂:

public static <K, V> HashMap<K, V> newInstance() {
    return new HashMap<K, V>();
}

就能使用

Map<String, List<String>> m = HashMap.newInstance();

代替原来的冗长代码。

 

静态工厂方法的主要缺点:

1.类的构造器如果是私有的,就不能被子类化。

2.它们与其他静态方法实际上没有任何区别。

就像上面提到的EnumSet类,它没有提供公有的构造器,所以无法这样实例化EnumSet s = new EnumSet(),在Eclipse会提示你这个类不没通过构造器实例化,除此之外没有任何有用的消息了,它不会告诉你使用noneof、allof这样的静态工厂方法来实例化该对象。

以上是关于第1条:考虑用静态工厂方法代替构造器的主要内容,如果未能解决你的问题,请参考以下文章

第1条:考虑用静态工厂方法代替构造器

读书笔记 - Effective Java01. 考虑用静态工厂方法代替构造器

第一条:考虑用静态工厂方法代替构造器

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

用静态工厂方法代替构造器遇到多个构造器参数时要考虑用构建器

Effective Java大厂实战之考虑以静态工厂方法代替构造方法