使用静态工厂方法代替构造方法
静态工厂方法是一个静态方法,用来生成实例。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Dog{ private Dog(){
} private static class Inner(){ private static final Dog dog = new Dog(); } public static getInstance(){ return Inner.dog; } }
|
因为构造方法每次调用都需要新建一个对象,有些情况下不能满我们的要求。
而静态工厂方法生成对象有以下几个好处:
1. 名字更有意义。
- from —— 类型转换方法,它接受单个参数并返回此类型的相应实例,例如:Date d = Date.from(instant);
- of —— 聚合方法,接受多个参数并返回该类型的实例,并把他们合并在一起,例如:Set faceCards = EnumSet.of(JACK, QUEEN, KING);
- valueOf —— from 和 to 更为详细的替代 方式,例如:BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- instance 或 getinstance —— 返回一个由其参数 (如果有的话) 描述的实例,但不能说它具有相同的值,例如:StackWalker luke = StackWalker.getInstance(options);
- create 或 newInstance —— 与 instance 或 getInstance 类似,除此之外该方法保证每次调用返回一个新的实例,例如:Object newArray = Array.newInstance(classObject, arrayLen);
- getType —— 与 getInstance 类似,但是在工厂方法处于不同的类中的时候使用。getType 中的 Type 是工厂方法返回的对象类型,例如:FileStore fs = Files.getFileStore(path);
- newType —— 与 newInstance 类似,但是在工厂方法处于不同的类中的时候使用。newType中的 Type 是工厂方法返回的对象类型,例如:BufferedReader br = Files.newBufferedReader(path);
- type —— getType 和 newType 简洁的替代方式,例如:List litany = Collections.list(legacyLitany);
2. 可以实现实例数量的控制。
例如单例、不可实例化类的实现。
3. 返回的对象可以根据参数不同而不同。
4. 返回的实例可以没有对应的对象类型。
例如服务者提供框架,就是用到静态工厂方法。
服务接口,表示实现;提供者注册API,选择实现;服务访问API,客户端调用。服务提供者接口,描述生成服务接口实例的工厂对象。
依赖注入框架可以被看作强大的服务提供者。
# 当构造函数多时使用**builder**模式
构造函数名字都与类名一样,区分不同构造函数依靠参数的顺序与数量。
当构造参数很多时,实例的生成非常麻烦,往往不知道调用哪个构造函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| public class NutritionFacts{ private final int servingSize; private final int calories; private final int fat; public static class Builder{ private final int servingSize; private int calories; private int fat;
public Builder(int servingSize){ this.servingSize = servingSize; }
public Builder calories(int calories){ calories = calories; return this; }
public Builder fat(int fat){ fat = fat; return this; }
public NutritionFacts build(){ return new NutritionFacts(this); } }
private NutritionFacts(Builder builder){ this.servingSize = builder.servingSize; this.calories = builder.calories; this.fat = builder.fat; } }
NutritionFacts coca = NutritionFacts.Builder(10).calories(40).fat(0).build();
|
**使用场景:**
当设计类的构造方法或静态工厂的参数超过几个时,Builder 模式是一个不错的选择,特别是如果许多参数是可选的或相同类型的。
# 单例模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| public class Dog{ private static final Dog dog; private Dog(){
} public static getInstance(){ if(dog==null) dog = new Dog(); return dog; } }
public class Dog{ private static final Dog dog = new Dog(); private Dog(){
} public static getInstance(){ return dog; } }
public class Dog{ private Dog(){
} private static class Inner(){ private static final Dog dog = new Dog(); } public static getInstance(){ return Inner.dog; }
Object readResolve() throws ObjectStreamException{ return dog; } }
public enum Dog{ INSTANCE; public static getInstance(){ return Dog.INSTANCE; } }
|
# 使用私有构造方法非实例化
构造方法声明为私有方法,可以创建非实例化类。
该类存在的意义就是提供工具静态方法,例如 java.util.Arrays 工具类等。
# 简单依赖注入
通过构造方法把**类依赖**的**客户端资源**注入到类中,是依赖注入的一种方式。
1 2 3 4 5 6 7 8 9
| public class SpellChecker{ private final Chinese dictionary; public SpellChecker(Chinese dictionary){ this.dictionary = Object.requireNonNull(dictionary); }
public boolean isValid(String word){...}
}
|
可以通过依赖注入把同一子类的资源工厂当做参数。
工厂就是可以被重复调用生产实例的对象。
Java 8的函数式接口Supplier非常适合工厂。
1 2 3 4 5
| @FunctionalInterface interface Supplier<T>{ T get(); }
|
1
| Mosaic create(Supplier<T? extends Tile> tileFactroy){..}
|
避免创建不必要的对象
例如
1 2 3 4 5 6 7 8 9 10
| Integer it = 0; for(int i=0; i<1000000; i++){ it += i; }
String s = new String("moreClass");
|
原则:
尽量使用基本类型而不使用装箱的基本类型,即使自动装箱也要考虑。
消除过期的对象引用。
如果对象引用是隐式存在,如果不需要,则置为null。
例如编写stack类pop()方法:
1 2 3 4 5
| public <T> pop(){ <T> result = bucket[--size]; bucket[size] = null; return ; }
|
避免使用Finalizer和Cleaner机制!
参考: https://sjsdfg.github.io/effective-java-3rd-chinese/#/