1、考虑用静态工厂方法替代构造函数
例子:
Integer.valueOf(“1”)、Boolean.valueOf(“true”)等。
优势:
- 可读性高(方法名)
- 性能(不一定创建对象)
- 灵活性高
下面针对三个优势进行一些解读。
可读性高
new Point(x,y)和Point.at(x,y)、Point.origin()。构造函数只能看出两个参数,不知其意,后者更易理解。
性能
在某些情况下,可以事先进行实例化一些对象,调用时直接调用即可,不需要进行改变。比如,Boolean。
public final class Boolean implements Serializable, Comparable<Boolean> { // 预先设置两个对象 public static final Boolean TRUE = new Boolean(true); public static final Boolean FALSE = new Boolean(false); public Boolean(boolean var1) { this.value = var1; } public Boolean(String var1) { this(parseBoolean(var1)); } // 工厂方法 public static Boolean valueOf(boolean var0) { return var0?TRUE:FALSE; // 返回预先设置的对象,而不是创建对象 } // 工厂方法 public static Boolean valueOf(String var0) { return parseBoolean(var0)?TRUE:FALSE; } // ... other code }
灵活性高
可根据具体情况,返回子类。相当于更强大的工厂。直接从父类获取到子类。尤其适用于工具类(提供各种API)。例子:Collections。
public class Collections { // 私有,典型工厂 private Collections() { } public static final List EMPTY_LIST = new EmptyList<>(); // 工厂方法 public static final <T> List<T> emptyList() { return (List<T>) EMPTY_LIST; } private static class EmptyList<E> extends AbstractList<E> implements RandomAccess, Serializable { // code } // 工厂方法 public static <E> List<E> checkedList(List<E> list, Class<E> type) { // 根据具体情况,获取相应子类 return (list instanceof RandomAccess ? new CheckedRandomAccessList<>(list, type) : new CheckedList<>(list, type)); } // 子类1 static class CheckedRandomAccessList<E> extends CheckedList<E> implements RandomAccess { CheckedRandomAccessList(List<E> list, Class<E> type) { super(list, type); } public List<E> subList(int fromIndex, int toIndex) { return new CheckedRandomAccessList<>( list.subList(fromIndex, toIndex), type); } } // 子类2 static class CheckedList<E> extends CheckedCollection<E> implements List<E> { // code } }
2、多个构造函数时,考虑使用构造器
尤其在进行Android开发时,会碰到这种情况。通常是一个对象,具有多个成员变量可能需要初始化,常规方法,需要提供大量构造函数。例如:
// 非Android中的AlertDialog,便于说明问题,举个例子 public class AlertDialog { private int width; private int height; private String title; private String confirmText; private String denyText; private AlertDialog(){} public AlertDialog(int width, int height){ // 空白的警告框 AlertDialog(width,height,null); } // 带标题的警告框 public AlertDialog(int width, int height, String title){ // 带标题的警告框 AlertDialog(width, height, title, "确定"); } // 带标题的警告框,有确定按钮 public AlertDialog(int width, int height, String title, String confirm){ AlertDialog(width, height, title, confirm, null); } // 带标题的警告框,有确定按钮,取消按钮 public AlertDialog(int width, int height, String title, String confirm, String denyText){ // set every thing. } }
有多种样式的警告框,为了调用方便,必须提供多个构造函数。否则用户在调用时,只能使用完整构造函数,容易犯错且无法进行阅读。极不灵活。如果采用另外一种方式,则可以解决,但会花费很多经历处理并发的情况:
// 非Android中的AlertDialog,便于说明问题,举个例子 public class AlertDialog { private int width; private int height; private String title; private String confirmText; private String denyText; public AlertDialog(){}// 空白的构造函数 public void setWidth(int width){ this.width = width; } // 其他set方法 }
调用时,通过调用各个参数的set方法进行设置。问题来了:
- 并发
- 无法进行参数校验。例如,只创建了对象,设置了标题,却没有尺寸,相当于创建了一个没有尺寸的警告框。
在Android中,大量的控件都使用了构造器Builder。
// 非Android中的AlertDialog,便于说明问题,举个例子 public class AlertDialog { private int width; private int height; private String title; private String confirmText; private String denyText; // private private AlertDialog(){} // Builder中使用 protected AlertDialog(Builder b){ width = b.width; height = b.height; // ..... if(width==0||height==0) throws new Exception("size must be set"); } // 构造器 public static class Builder { private int width; private int height; private String title; private String confirmText; private String denyText; // 注意:返回的Builder。 public Builder setTitle(String title) { this.title = title; return this; } // 其他set... public AlertDialog build(){ return AlertDialog(this); } } }
于是,可以根据相应需求,进行相应设置,并在AlertDialog真正构造时,进行参数校验。就像这样:
new AlertDialog.Builder().setTitle("提示").build();
上述例子,会成功抛出异常。