Effective Java —— 优先考虑依赖注入来引用资源
本文参考
本篇文章参考自《Effective Java》第三版第五条"Prefer dependency injection to hardwiring resources"
Static utility classes and singletons are inappropriate for classes whose behavior is parameterized by an underlying resource
静态工具类模式和单例模式都不适合应用到需要将底层资源作为自身字段进行参数化的类,这个引用的资源可以是一个接口,他对应有很多的实现,不同的实现应对不同的场景
静态工具类模式往往不需要进行实例化,而且有一个无参的private构造方法来强化noninstantiability的特性,他需要引用的资源在类加载时就已经被构建,尽管我们可以再设计一个静态方法来修改引用的资源,但并不是灵活和方便测试的方式
虽然单例模式存在一个实例,但是这个实例仅允许被初始化一次,所以引用的资源也只能被参数化一次,难以按照不同场景下的需求进行改变
do not use a singleton or static utility class to implement a class that depends on one or more underlying resources whose behavior affects that of the class, and do not have the class create these resources directly.
pass the resource into the constructor when creating a new instance
要解决上述问题,便要求能够用不同的资源构建不同的实例,我们可以通过构造方法来实现不同资源的参数化,这就是依赖注入的其中一种解决方案(同样可以应用到前述的静态工厂方法和构建者模式),这与Spring中的"构造注入"类似,它也能够保证不同的客户端共享同一份引用的资源,即"preserves immutability"
pass the resources, or factories to create them( refer to the fields using underlying resources ), into the constructor (or static factory or builder). This practice, known as dependency injection, will greatly enhance the flexibility, reusability, and testability of a class.
a variant of the pattern is to pass a resource factory to the constructor
原文提供了一种使用Supplier<T>接口的Factory Method Pattern(工厂方法模式)
有关Supplier的使用,参考这篇文章:https://mkyong.com/java8/java-8-supplier-examples/
以马赛克为例,工厂方法模式的示例代码如下,顺便复习一下builder构建者模式
public class Tile {
private String name;
private int price;
private String origin;
public static class Builder {
private String name;
private int price;
private String origin;
public Builder name(String name) {
this.name = name;
return this;
}
public Builder price(int price) {
this.price = price;
return this;
}
public Builder origin(String origin) {
this.origin = origin;
return this;
}
public Tile build() {
return new Tile(this);
}
}
private Tile(Builder builder) {
name = builder.name;
price = builder.price;
origin = builder.origin;
}
public String getName() { return name; }
public int getPrice() { return price; }
public String getOrigin() { return origin; }
}
public class Mosaic {
private Tile tile;
private String style;
private Mosaic(String style) {
this.style = style;
}
public static Mosaic create(Supplier<? extends Tile> tileFactory) {
Tile tile = tileFactory.get();
if (tile.getOrigin().equals("American")) {
return new Mosaic("USA-STYLE");
} else {
return new Mosaic("GENERAL_STYLE");
}
}
public Tile getTile() { return tile; }
public String getStyle() { return style; }
}
public class SupplierExample {
public static void main(String[] args) {
Mosaic product = Mosaic.create(() -> new Tile.Builder().origin("American").build());
System.out.println(product.getStyle());
}
}