这是我参与更文挑战的第15天,活动详情查看: 更文挑战
一、前言
在之前的设计模式中,我们完整的学完了设计模式中的创建型模式,这种模式是专注于对象的创建方式。想回顾的可以看:
为了对这种创建型模式的扩展,今天开始,我们来讨论下对象是如何创建和销毁的这个话题。
几个疑问:
- 1、何时以及如何创建对象?
- 2、何时以及如何避免创建对象?
- 3、如何确保这些对象能够适时销毁掉?
- 4、对象销毁前需要做哪些清理工作?
二、用静态工厂方法代替构造器
什么是构造器?对于类而言,都有一个默认的公有的构造方法,我们可以调用它生成一个新的类,这就是我们常说的new 一个对象,也就是构造器。
什么是静态工厂方法? 这里的静态工厂方法与我们在设计模式中讲的工厂方法不同,但也类似,它是通过一个静态的方法返回类的实例,Boolean类型就是这是典型的静态工厂方法运用:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
复制代码
也就是用静态工厂方法代替构造器,这样做有什么好处呢?
2.1 优势
2.1.1 静态工厂方法有方法名
我们知道Java的构造器中往往会传入多个参数构造成不同的对象。也就是重载构造方法,如果参数类型、数目又比较相似,就很容易出错,如果没有参考文档,往往不知所措。
2.1.2 不必每次调用的时候都创建一个对象
对于不可变类,可以提前创建好实例,或者将实例缓存起来,进行重复利用,就可以避免创建不必要的对象,例如单例模式的创建,一次加载,后续调用即可。
2.1.3 可与返回原返回类型的子类
这样在选择返回对象的类时就更加灵活,也应对着设计模式的基本原则—-里氏替换原则。即子类应该能替代父类。
灵活的应用就是调用API返回对象。
在java.util.Collections 中,它的内部有很多Collections的非共有实现类(包括不可修改的集合,同步集合),这些类都可与通过静态工厂方法导出,返回的也是它的子类。好处就是减少了API的数量
public class Collections {
// Suppresses default constructor, ensuring non-instantiability.
private Collections() {
}
static class UnmodifiableCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 1820017752578914078L;
final Collection<? extends E> c;
UnmodifiableCollection(Collection<? extends E> c) {
if (c==null)
throw new NullPointerException();
this.c = c;
}
}
复制代码
简单举的例子:
public class Animal{
publict static Animal getInstance(){
return new Dog()
}
}
public class Dog extends Animal(){
}
复制代码
调用父类可以生成返回子类。
扩展:
java8 开始,接口中允许有静态方法
java9 开始,接口中允许有私有静态方法
2.1.4 创建参数型对象的时候,代码更加简洁
在源码里面,静态模式的参数值也是会随着版本而发生变化的。例如EnumSet 中它的具体实现就依据着底层枚举类型的带线啊哦,如果它的元素小于等于64,就发RegalarEnumSet实例,如果大于64,则返回JumboEnumSet方法,对于用户来说是不知道的。
当然,这是源码里的一个例子,实际里面好像还没遇到,可以在一个对象需要多个固定调用参数的时候,就用一个方法来封装它,调用这个方法即可,就不需要传入很多参数。当然,这种要求就比较苛刻了。
2.1.5 方法返回对象的类可以不存在
就是使用静态工厂方法,我们只需传入参数,就可以获得一个不存在的类的实例。
回顾下JDBC驱动:
Class.forName(driverClass);
//加载MySql驱动
Class.forName("com.mysql.jdbc.Driver");
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mydb", "root", "root");
复制代码
由Drivermanager.getConnection() 返回具体的实现类对象
2.2 劣势
2.2.1 类如果不含公有的或者受保护的构造器,就不能被子类化。
继承一个类的时候,那么这个父类的构造方法就不能私有化。
2.2.2 与其他的静态方法实际上没有任何区别
一般很难方法哪里使用了静态工厂方法,所以感觉不到它与其他静态方法的区别。因此,可以从一些惯用命名来找出静态工厂方法的使用地方:
静态工厂方法给方法起了更多有意义的名字,让代码阅读和编写更加清晰,常见的命名方式如下:
from——- 类型转换方法,它只有单个参数,例如Data d=Date.from(instance)
valueOf——不太严格的讲,该方法返回的实例与它的参数具有相同的值。这样的静态工厂方法实际上是类型转换方法。
of——valueOf的一种更为简洁的替代,在EnumSet中使用并流行起来。
getInstance——返回的实例是通过方法的参数来描述的,但是不能够说与参数具有同样的值。对Singleton来说,该方法没有参数,并返回唯一的实例。
create或newInstance——像getInstance一样,但newInstance能够确保返回的每个实例都与所有其他实例不同。
getType——像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。
newType——像newInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。
三、小结
总而言之,静态工厂方法的优点很多,在天天使用构造器方法的时候,可以不妨想想静态工厂方法。
当然,上面的问题在这篇文章种还是没有得到解决,接着后面的文章分析,我们再来对他们一探究竟。
参考资料:
《Effective Java (第3版)》