Item1:考虑用“静态工厂”,而不是“构造器”
先来看个静态工厂的典型例子
// translate a boolean primitive value into a Boolean object reference
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
要注意的是,与设计模式中的“工厂模式”不同!
好处
-
好处一:静态工厂有名字,而构造器没有
- 可读性强,方法名即功能
- 能区别入口参数差异(比如参数类型一样,只是顺序不一样,通过方法名字说明)
-
好处二:可以避免创建新的对象
- 比如上述例子,并未创建新对象
- 对象控制(比如单例模式,在方法外创建好需要的对象,方法里调就完了)
-
好处三:可以返回原返回类型的子类对象
- 灵活性增强
- API可以返回对象,同时可以使返回对象的类不是
public
的,更好的隐藏封装,API更简洁 - 让人们专注于API,而非类文档
- Java8已经支持接口里写公有的静态成员了,而Java9甚至支持私有静态方法
package food;
import food.Food;
/**
* 服务-提供者框架模型,一种服务(打印食物信息)多个提供者,通过FoodFactory静态工厂方法自动获取提供者
* 下文好处五中会提到这个模型
*/
public class FoodFactory {
private FoodFactory(){
super();
}
public static final String TYPE_APPLE = "Apple";
public static final String TYPE_BANANA = "Banana";
private static final String TYPE_DEFAULT = "Bread";
public static Food getFoodByType(String foodType){
Food food = null;
try {
// 通过反射机制拿到类,那也就无所谓它的访问权限了
food = (Food) Class.forName("food."+foodType).newInstance();//传说中的反射机制
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
return food;
}
public static Food getDefaultFood(){
return getFoodByType(TYPE_DEFAULT);
}
}
-
好处四:返回对象的类型随输入参数的不同而改变
- 比如我money少于10元,那我就return一个包子类;否则,我return一个汉堡类
- 当然你也可能传的是一个对象数组,我就去遍历这个数组。比如说我这个数组元素个数小于10,我就返回Regular类;否则,返回一个Special类。这都是用户看不到的细节
-
好处五:当包含方法的类被写入时,返回对象的类不需要存在
-
典型例子:JDBC(A service provider framework)
-
一个好的服务提供者的框架,应该包含:
- 服务接口 a service interface —— 实现服务
- 提供者注册API a provider registration API —— 提供者用它来注册实现
- 服务访问API a service access API —— 客户用它来获取服务对象【这个就是静态工厂的好处四】
-
// 注册/加载驱动 —— a provider registration API
Class.forName("com.mysql.cj.jdbc.Driver");
// 获取连接对象 —— a service access API
String url = "jdbc:mysql://localhost:3306/how2java?useUnicode=true&characterEncoding=UTF-8";
Connection connection = DriverManager.getConnection(url,"root","admin");
// 获取执行sql语句的表单对象 —— a service access API
String sql = "select * from account";
Statement statement = connection.createStatement();
// 执行sql —— a service interface
ResultSet resultSet = statement.executeQuery(sql);
System.out.println("result = " + result);
// Driver is the provider interface
坏处
-
坏处一:(仅提供静态工厂方法时)没有
public
或protected
的构造函数的类不能被继承- 其实也算是好处😄哈哈,为什么呢?因为这使我们多用策略模式中的组合,而非去继承
-
坏处二:程序员不好找到静态工厂方法,因为它在文档中并不显眼,需要我们自己注意
接下来就讲几个常见的静态工厂例子吧:
- from—类型转换方法,它接收单个参数并返回此类型的相应实例,例如:
Date d = Date.from(instant);
- of—一个接收多个参数并返回包含它们的此类型实例的聚合方法,例如:
Set faceCards = EnumSet.of(JACK, QUEEN, KING);
- valueOf—比 from 和 of 更为详尽的替代方法,例如:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- instance or getInstance—返回由其参数描述的实例(如果有),但不能说具体有相同的值,例如:
StackWalker luke = StackWalker.getInstance(options);
- create or newInstance—就像 instance 和 getInstance,但该方法保证每个调用都返回一个新的实例,例如:
Object newArray = Array.newInstance(classObject, arrayLen);
- getType—就像 getInstance,但在工厂方法位于不同类中使用。Type 是工厂方法返回的对象的类型,例如:
FileStore fs = Files.getFileStore(path);
- newType—就像 newInstance,但在工厂方法位于不用类中使用。Type 是工厂方法返回的对象的类型,例如:
BufferedReader br = Files.newBufferedReader(path);
- type—getType 和 newType 的简洁替代方法,例如:
List litany = Collections.list(legacyLitany);
总结
总之呢,静态工厂和公有构造器各有优劣,但以后在创建对象时,我们一定要优先考虑“静态工厂”!