美文网首页牛叉的demo将来跳槽用
高效Java第一条考虑用静态工厂代替构造函数

高效Java第一条考虑用静态工厂代替构造函数

作者: 每天学点编程 | 来源:发表于2017-07-18 01:02 被阅读0次

    获得类的实例:
    1.提供一个公有的构造函数;
    2.提供一个公有的静态工厂方法,该方法只是一个返回类的实例的静态方法。


    静态工厂方法与设计模式中的工厂方法模式不同。

    提供静态工厂方法的优势——静态工厂方法与构造函数不同的第一大优势在于,它们有名称

    如果构造函数的参数本身没有确切地描述正被返回的对象,那么具有适当名称的静态工厂会更容易被使用,产生的客户端代码也更易于阅读。

    BigInteger(int,int,Random)返回的BigInteger可能为素数,如果用名为BigInteger.probablePrime的静态方法来表示,会更为清楚。
    一个类只能有一个带有指定签名的构造函数。
    提供两个构造函数,它们的参数列表只在参数类型的顺序上有所不同。这不是好主意。用户永远也记不住该用哪个构造函数,因此会经常调用错误的构造函数。并且,读调用这些构造函数的代码是有难度的。

    当一个类需要多个带有相同签名的构造函数时,可以用静态工厂方法代替构造函数,通过慎重地选择名称可以突出它们之间的区别。

    提供静态工厂方法的优势——静态工厂方法与构造函数不同的第二大优势在于,不必在每次调用它们的时候都创建一个新的对象

    可以避免创建不必要的重复对象。
    Booelan.valueOf(booelan)方法从不创建对象。
    类似于Flyweight模式。
    如果程序经常请求创建相同的对象,并且创建对象的代价很好,则这项技术可以极大地提升性能。

    实例受控的类:静态工厂方法能够为重复的调用返回相同的对象,这样有助于类总能严格控制在某个时刻那些实例应该存在。

    提供静态工厂方法的优势——静态工厂方法与构造函数不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象

    API可以返回对象,又不会使对象的类变成公有的。以这种方式隐藏实现类会使API变得非常简洁。
    这项技术适用于基于接口的框架,接口为静态工厂方法提供了自然返回类型。
    接口不能有静态方法,因此按照惯例,接口Type的静态工厂方法被放在一个名为Types的不可实例化的类中。

    Collections Framework API比导出32个独立公有类的实现方式要小得多,每种便利实现都对应一个类。这不仅是API数量上减少,也是概念意义上的减少。
    被返回的对象是由相关的接口精确指定的。
    使用这种静态工厂方法时,甚至要求客户端通过接口来引用被返回的对象,而不是通过它的实现类来引用被返回的对象,这是一种良好的习惯。

    公有的静态工厂方法所返回的对象的类不仅可以是非公有的,而且该类还可以随着每次调用而发生变化,这取决于静态工厂方法的参数值。只要是已声明的返回类型的子类型,都是允许的。为了提升软件的可维护性和性能,返回对象的类也可能随着发行版本的不同而不同。

    JDK5引入的java.util.EnumSet没有公有构造函数,只有静态工厂方法。它返回两种实现类之一,取决于底层枚举类型的大小:如果元素有64个或者更少,静态工厂方法就会返回一个RegularEnumSet实例,用单个long进行支持;如果枚举类型有65个或者更多元素,工厂就返回JumboEnumSet实例,用long数组进行支持。
    这两个实现类对客户来说是不可见的。如果RegularEnumSet不能再给小的枚举类型提供性能优势,就可能从未来的发行版本中被删除,这不会造成不良的影响。如果事实证明对性能有好处,在未来的发行版本中可以添加其他的EnumSet实现。客户永远不知道也不关心他们从工厂方法中得到的对象的类,他们只需要知道它是EnumSet的某个子类即可。

    静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不必存在。这种灵活的静态工厂方法构成了服务提供者框架的基础(JDBC).

    提供静态工厂方法的优势——静态工厂方法与构造函数不同的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。

    在调用参数化类的构造函数时,即使类型参数很明显,也必须指明。也就是说要接连两次提供类型参数:

    使用静态工厂方法,编译器可以推导出类型参数(类型推导)。

    JDK8竟然还会警告。

    静态工厂方法的主要缺点在于:类如果不含公有的或者受保护的构造函数,就不能被子类化。

    对于公有的静态工厂所返回的非公有类,也是如此。
    鼓励程序员使用复合,而不是继承。

    静态工厂方法的第二个缺点在于,它们与其他的静态方法没有任何区别。

    在API文档中,它们没有像构造函数那样在API文档中明确地标识出来。
    对于提供了静态工厂方法而不是构造函数的类来说,要想查明如何实例化一个类,是非常困难的。

    静态工厂方法的惯用名称

    valueOf——该方法返回的实例与它的参数具有相同的值。实际上是类型转换方法。
    of——valueOf的简洁替代,在EnumSet中使用并流行起来。
    getInstance——返回的实例是通过方法的参数来描述的,但是不能够说与参数具有同样的值。对于单例来说,该方法没有参数,并返回唯一的实例。
    newInstance——与getInstance一样,但newInstance确保返回的每个实例都与所有其他实例不同。
    getType——在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。
    newType——在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。

    服务提供者框架概念

    服务提供者框架:多个服务提供者实现一个服务,系统为服务提供者的客户提供多个实现,并把他们从多个实现中解耦出来。

    服务提供者三个组件

    服务接口:提供者实现
    提供者注册API:系统用来注册实现,让客户访问它们
    服务访问API:客户端用来获取服务的实例

    服务访问API允许但是不要求客户端指定某个选择提供者的条件,如果没有指定,则返回默认的实现。服务访问API是灵活的静态工厂,它构成了服务提供者框架的基础。

    服务提供者可选组件

    服务提供者接口,这些提供者负责创建其服务实现的实例。如果没有服务提供者接口,实现就按照类名称注册,并通过反射方式进行实例化。

    服务提供者例子——JDBC

    Connection服务接口
    DriverManager.registerDriver是提供者注册API
    DriverManager.getConnection是服务访问API
    Driver是服务提供者接口

    编写实例受控的类的原因

    实例受控使得类可以确保它是一个单例或是不可实例化的。
    实例受控使得不可变类可以确保不会存在两个相等的实例,即当且仅当a==b的时候才有a.equals(b)true。如果类保证了这一点,它的客户端就可以使用==操作符来代替equals(Object)方法,这样可以提升性能。

    总结

    静态工厂通常更加适合,因此切忌第一反应就是提供公有的构造函数,而不优先考虑静态工厂。

    相关文章

      网友评论

        本文标题:高效Java第一条考虑用静态工厂代替构造函数

        本文链接:https://www.haomeiwen.com/subject/zlfckxtx.html