• 第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这样的静态工厂方法来实例化该对象。

  • 相关阅读:
    Java读写锁(ReentrantReadWriteLock)学习
    水平拆分和垂直拆分理解(未完)
    MySQL 主从复制
    sharding-JDBC 实现读写分离
    Linux查看程序端口占用情况
    sharding-jdbc 实现分表
    MySQL explain
    MySQL的七种join
    MySQL建立高性能索引策略
    Nginx企业级优化
  • 原文地址:https://www.cnblogs.com/13jhzeng/p/5601942.html
Copyright © 2020-2023  润新知