类可以提供公共的静态工厂方法,也就是返回实例的公共静态方法。
public static Person getNewPerson(String name, int age, String skill) {
if (StringUtils.isBlank(skill)){
return new Person(name, age);
}
return new Person(name, age, skill);
}
以上是使用静态工厂方法来返回一个Person的实例对象,我们可以先不管方法体内的判断逻辑,只看返回值为一个Person对象。
该静态工厂方法的功能和构造器类似,可以直接调用方法返回与构造器相同的结果。但是在实际代码开发中,静态工厂方法和构造器的使用场景也是有较大的区分的。相信读到这个地方的同学会觉得,如果都一样功能,那为什么还要构造函数,或者会问静态工厂方法的存在意义呢?当我们研究一个技术的应用场景的时候会先分析该技术的优势和劣势。
优势:
1. 我们继续从上面的代码示例看出来,我们有两个return,根据skill是否为空来决定调用哪一种构造器,实际在开发过程中,我们可能会根据业务定义多个构造函数。当定义过多的时候,创建对象的同学就会挨着一个个的去研究每个构造函数的参数来区分,实际构造的对象具有哪些属性。其实我们可以直接通过静态工厂方法的名字区分,这样就更清晰明确。如下:
/**
* 静态工厂方法返回新人类的对象,新人类具有新skill技能
**/
public static Person getNewPerson(String name, int age, String skill) {
if (StringUtils.isBlank(skill)){
return new Person(name, age);
}
return new Person(name, age, skill);
}
这样,在调用该代码的同学就会十分清楚,可以通过该静态工厂方法来创建具有新skill的新人类。
2. 可以通过写静态工厂方法来统一返回一个对象,也就是不必每次调用静态工厂方法的时候每次都创建一个新的对象。
private static Table table;
public static Table getTableInstance() {
if (table == null){
return new Table();
}
return table;
}
我们先忽略上段代码的线程安全的问题,该段代码主要表达了,当任意的调用者调用缓存表静态工厂方法的时候,无论何时都返回同一个 table 对象。这样就可以在全局使用该 table 对象缓存数据。
当然该段方法也可以通过返回相同的对象,避免创建不必要的重复对象。如果创建该对象的代价或者资源占用较高,这不失为一个不错的好办法。
3. 通过该静态方法可以返回任何子类型的对象,且对调用者可以直接屏蔽实例化对象的类型。如下:
定义一个接口,该接口主要定义了发消息和收消息的两个方法。
import java.util.List;
/**
* 定义通道的行为
*/
public interface PassAgeWay {
public int send(List<String> message);
public List<String> pull();
}
实现一个通过Email来发消息和收消息的类:
import java.util.List;
public class EmailWay implements PassAgeWay{
public int send(List<String> message) {
// TODO 邮件写文字信息
return 0;
}
public List<String> pull() {
// TODO 邮件收邮件信息
return null;
}
}
实现一个通过Phone来发消息和收消息的类:
import java.util.List;
public class PhoneWay implements PassAgeWay{
public int send(List<String> message) {
// TODO 拨号 发语音
return 0;
}
public List<String> pull() {
// TODO 听筒听语音
return null;
}
}
实现一个静态工厂类,主要通过参数来决定使用不同的实现类来创建通信对象:
/**
* 根据不同的要求来选择不同的通信底层,0 --- 慢的方法,1 --- 快的方法
* @param type
* @return
*/
public static PassAgeWay callMesage(int type) {
if (type == 0) {
return new EmailWay();
} else if (type == 1){
return new PhoneWay();
} else {
throw new ClassCastException(type + " not an type");
}
}
这样,调用者可以在业务代码中,通过为callMesage传递不同参数来决定,底层使用的是哪种实现方式,当然调用者并不关心同时也看不到,究竟是使用的具体哪种类来实例化的对象,他只需要知道传递1传输消息就是非常快。调用如下:
public static void main(String[] args) {
List<String> message = new ArrayList<String>();
message.add("起床了没?");
message.add("吃饭了没?");
// 通过传递0来使静态工厂方法创建较慢的底层通信对象 --- EmailWay
PassAgeWay slow = CallWay.callMesage(0);
slow.send(message);
List<String> slowPullMesage = slow.pull();
// 通过传递1来使静态工厂方法创建更快的底层通信对象 --- PhoneWay
PassAgeWay fast = CallWay.callMesage(1);
fast.send(message);
List<String> fastPullMesage = fast.pull();
}
4. 静态工厂方法返回对象的类,可以是在定义静态工厂方法的时候不存在的。
例如:我们在使用JDBC的时候,每次都会传入需要调用的jdbc实现类。通过getConnection方法来判断传入的url对应使用数据库的类型,创建对应不同的连接对象实现。这样就可以和不同种类的数据库进行交互。
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test";
String username = "root";
String password = "123456";
Connection conn = null;
//2.获取与数据库的链接
conn = DriverManager.getConnection(url, username, password);
说了那么多优点,接下来我们也来说说,静态工厂方法的缺点,如下:
1. 当类不能被继承的话,就不能使用上述多态的优势;
2. 作为静态工厂方法,很难被调用者发现,调用者一般都会去查看构造函数是否满足创建对象的要求。我们建议使用良好的命名规范来约定,例如:使用instance、getInstance或者newInstance等约定的命名规范来编写,这样调用者在查看doc文件的时候可以快速的找到各种创建实例对象的方法。
原文:https://www.cnblogs.com/cara-zhang/p/15094774.html