获得类的实例:
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)
方法,这样可以提升性能。
总结
静态工厂通常更加适合,因此切忌第一反应就是提供公有的构造函数,而不优先考虑静态工厂。
网友评论