在平时Android开发中,可能会出现某个类有很多个重载方法。
这个问题很多人在维护项目的时候可能会经常遇到,比如需求变更,刚开始只有一个构造方法,传入一个参数的。如下所示:
public class Demo{ private int a; public Demo(int a){ this.a=a; //do something... } }
后来需求变更,传入两个参数比较符合业务逻辑,如下图所示:
public class Demo{ private int a; private int b; public Demo(int a){ //do something... this(a,0); } public Demo(int a,int b){ this.a=a; this.b=b; //do something... } }
再到后来随着业务逻辑的扩展,可能会有3个构造方法。可能每个构造方法里面的逻辑也有所不一样。如下所示:
public class Demo{ private int a; private int b; private int c; public Demo(int a){ //do something... this(a,0); } public Demo(int a,int b){ //do something... this(a,b,0); } public Demo(int a,int b,int c){ this.a=a; this.b=b; this.c=c; //do something... } }
甚至有4个构造方法的:
public class Demo{ private int a; private int b; private int c; private int d; public Demo(int a){ //do something... this(a,0); } public Demo(int a,int b){ //do something... this(a,b,0); } public Demo(int a,int b,int c){ //do something... this(a,b,c,0); } public Demo(int a,int b,int c,int d){ this.a=a; this.b=b; this.c=c; this.d=d; //do something... } }
随着业务的发展,方法越来越多,越来越不好维护,重载方法之间的逻辑也不大相同。那么遇到这种问题应该怎么最好的优化呢?我这里给出了 以下几种方案 供大家选择:
一、可以模仿Android源码,使用 @Deprecated
注解标记一下方法过时,建议在哪个版本中使用哪个方法,这样也起一个标记的作用。
【优点】暂时解决了方法维护的问题,开发人员不再为过时方法维护了,而且旧版本也可以使用相应方法,对老版本兼容性比较好。
【缺点】所有的方法都在,还是有那么多冗余代码,还是没从根源上解决问题。
示例如下:
public class Demo{ private int a; private int b; private int c; private int d; public Demo(int a){ //do something... this(a,0); } //比如在app的v1.0.0版本中在这个构造方法中标记为过时,后续版本中不使用该方法 @Deprecated {@link Demo(int, int, int)} public Demo(int a,int b){ //do something... this(a,b,0); } public Demo(int a,int b,int c){ //do something... this(a,b,c,0); } public Demo(int a,int b,int c,int d){ this.a=a; this.b=b; this.c=c; this.d=d; //do something... } }
二、根据面向对象的思想,把参数实例化成一个实体类,然后构造里面引入这个实体类,想要哪些属性,通过getter和setter来访问成员变量。
【优点】解决了代码冗余问题。
【缺点】针对不同版本设置的代码不一样,代码量还是很大的。
示例代码如下:
//封装一个实体类public class DataBean{ private int dataA; private int dataB; private int dataC; private int dataD; public int getDataA() { return dataA; } public void setDataA(int dataA) { this.dataA = dataA; } public int getDataB() { return dataB; } public void setDataB(int dataB) { this.dataB = dataB; } public int getDataC() { return dataC; } public void setDataC(int dataC) { this.dataC = dataC; } public int getDataD() { return dataD; } public void setDataD(int dataD) { this.dataD = dataD; } //篇幅有限,toString,equals和hasCode方法省去不写了}
然后我加了一个接口,处理版本号码的问题,所有的版本号码都可以写在这个接口里面,都是int类型的,实质相当于枚举,因为枚举比较耗性能,所以就用接口替代了。(为什么写接口,写接口方便扩展,性能好。)
/** * 处理版本号的接口 */public interface IVersionCode { public int VERSION_ONE = 1; public int VERSION_TWO = 2; public int VERSION_THREE = 3; public int VERSION_FOUR = 4; }
然后原代码里面只需要传入这个DataBean
实体类就可以了,同时实现了IVersionCode接口,可以直接使用里面的常量。
再看看原来那个类的变化:
public class Demo implements IVersionCode{ private DataBean dataBean; //通过一个LinkedList有序列表去保存版本号 private List<Integer> mLinkedList = new LinkedList<>(); public Demo(DataBean bean){ this.dataBean = bean; } /** * 设置版本信息 * 传入一个版本号,根据对应的版本号设置对应的方法 * */ public void setVersion(int versionName){ switch(versionName){ //假如是版本VERSION_ONE,调用的是一个参数 case VERSION_ONE: //避免重复添加 if(!mLinkedList.contains(VERSION_ONE)){ mLinkedList.add(VERSION_ONE); } dataBean.setDataA(1); //do something... break; case VERSION_TWO: if(!mLinkedList.contains(VERSION_TWO)){ mLinkedList.add(VERSION_TWO); } dataBean.setDataA(1); dataBean.setDataB(2); //do something... break; case VERSION_THREE: if(!mLinkedList.contains(VERSION_THREE)){ mLinkedList.add(VERSION_THREE); } dataBean.setDataA(1); dataBean.setDataB(2); dataBean.setDataC(3); //do something... break; case VERSION_FOUR: if(!mLinkedList.contains(VERSION_FOUR)){ mLinkedList.add(VERSION_FOUR); } dataBean.setDataA(1); dataBean.setDataB(2); dataBean.setDataC(3); dataBean.setDataD(4); //do something... break; default: break; } } /** * 根据获取的版本信息得到实体类对象 * 调用get方法就可以拿到实体类里面的具体方法 * @return */ public DataBean getVersion(int versionName){ for(int i =0; i<mLinkedList.size(); i++){ if(mLinkedList.get(i) == VERSION_ONE){ dataBean.getDataA(); }else if(mLinkedList.get(i) == VERSION_TWO){ dataBean.getDataA(); dataBean.getDataB(); }else if(mLinkedList.get(i) == VERSION_THREE){ dataBean.getDataA(); dataBean.getDataB(); dataBean.getDataC(); }else if(mLinkedList.get(i) == VERSION_FOUR){ dataBean.getDataA(); dataBean.getDataB(); dataBean.getDataC(); dataBean.getDataD(); } } return dataBean; } }
那么有没有一种更好的解决方案呢?我觉得目前能够想出来的解决方案就是下面这种了:
三、使用建筑者模式,把Demo这个类的构建对象的操作转移到内部类里面去执行,对外隐藏对象创建的细节。
【优点】这种对象的构建方式不但解决了代码可读性的问题,并大幅减少了构造参数,构建过程保证了一定的一致性。
【缺点】建造者模式的产品的组件基本相同,如果产品的差异性较大,建造者模式就不适用了。
示例代码如下:
public class Demo{ private int dataA; private int dataB; private int dataC; private int dataD; private Demo(Builder builder) { this.dataA = builder.dataA; this.dataB = builder.dataB; this.dataC = builder.dataC; this.dataD = builder.dataD; } public void setA(int a){ this.dataA = a; } public void setB(int b){ this.dataB = b; } public void setC(int c){ this.dataC = c; } public void setD(int d){ this.dataD = d; } public void getA(int a){ return dataA; } public void getB(int b){ return dataB; } public void getC(int c){ return dataC; } public void getD(int d){ return dataD; }
public static class Builder { private int dataA; private int dataB; private int dataC; private int dataD; public Demo build() { return new Demo(this); } public Builder getDataA(int a) { this.dataA = a; return this; } public Builder getDataB(int b) { this.dataB = b; return this; } public Builder getDataC(int c) { this.dataC = c; return this; } public Builder getDataD(int d) { this.dataD = d; return this; } } }
四、使用接口回调来处理
实体类稍微改了一下:
public class DataBean{ public static final String TAG = DataBean.class.getSimpleName(); private int dataA; private int dataB; private int dataC; private int dataD; /** * 是否已经设置了A属性,解决不同版本何止不同的参数问题 */ private boolean isDataASetted; /** * 是否已经设置了B属性,解决不同版本何止不同的参数问题 */ private boolean isDataBSetted; /** * 是否已经设置了C属性,解决不同版本何止不同的参数问题 */ private boolean isDataCSetted; /** * 是否已经设置了D属性,解决不同版本何止不同的参数问题 */ private boolean isDataDSetted;
public int getDataA() { Log.d(TAG, "getDataA: " + dataA); return dataA; } public void setDataA(int dataA) { this.dataA = dataA; } public int getDataB() { Log.d(TAG, "getDataB: " + dataB); return dataB; } public void setDataB(int dataB) { this.dataB = dataB; } public int getDataC() { Log.d(TAG, "getDataC: " + dataC); return dataC; } public void setDataC(int dataC) { this.dataC = dataC; } public int getDataD() { Log.d(TAG, "getDataD: " + dataD); return dataD; } public void setDataD(int dataD) { this.dataD = dataD; } public boolean isDataASetted() { return isDataASetted; } public void setDataASetted(boolean dataASetted) { isDataASetted = dataASetted; } public boolean isDataBSetted() { return isDataBSetted; } public void setDataBSetted(boolean dataBSetted) { isDataBSetted = dataBSetted; } public boolean isDataCSetted() { return isDataCSetted; } public void setDataCSetted(boolean dataCSetted) { isDataCSetted = dataCSetted; } public boolean isDataDSetted() { return isDataDSetted; } public void setDataDSetted(boolean dataDSetted) { isDataDSetted = dataDSetted; } @Override public String toString() { return "DataBean{" + "dataA=" + dataA + ", dataB=" + dataB + ", dataC=" + dataC + ", dataD=" + dataD + ", isDataASetted=" + isDataASetted + ", isDataBSetted=" + isDataBSetted + ", isDataCSetted=" + isDataCSetted + ", isDataDSetted=" + isDataDSetted + '}'; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; DataBean dataBean = (DataBean) o; if (dataA != dataBean.dataA) return false; if (dataB != dataBean.dataB) return false; if (dataC != dataBean.dataC) return false; if (dataD != dataBean.dataD) return false; if (isDataASetted != dataBean.isDataASetted) return false; if (isDataBSetted != dataBean.isDataBSetted) return false; if (isDataCSetted != dataBean.isDataCSetted) return false; return isDataDSetted == dataBean.isDataDSetted; } @Override public int hashCode() { int result = dataA; result = 31 * result + dataB; result = 31 * result + dataC; result = 31 * result + dataD; result = 31 * result + (isDataASetted ? 1 : 0); result = 31 * result + (isDataBSetted ? 1 : 0); result = 31 * result + (isDataCSetted ? 1 : 0); result = 31 * result + (isDataDSetted ? 1 : 0); return result; } }
原来的那个类改造如下:
public class Demo implements IVersionCode{ private DataBean dataBean; private OnVersionChoosedListener mOnVersionChoosedListener; public Demo(DataBean bean){ this.dataBean = bean; }
/** * 设置版本名 */ public void setVersion(int versionName){ switch(versionName){ case VERSION_ONE: dataBean.setDataA(1); dataBean.setDataASetted(true); dataBean.setDataBSetted(false); dataBean.setDataCSetted(false); dataBean.setDataDSetted(false); mOnVersionChoosedListener.setVersion(versionName,dataBean); //do something... break; case VERSION_TWO: dataBean.setDataA(1); dataBean.setDataB(2); //表示在这个版本只有A B这两个方法是有效的 dataBean.setDataASetted(true); dataBean.setDataBSetted(true); dataBean.setDataCSetted(false); dataBean.setDataDSetted(false); mOnVersionChoosedListener.setVersion(versionName,dataBean); //do something... break; case VERSION_THREE: dataBean.setDataA(1); dataBean.setDataB(2); dataBean.setDataC(3); dataBean.setDataASetted(true); dataBean.setDataBSetted(true); dataBean.setDataCSetted(true); dataBean.setDataDSetted(false); mOnVersionChoosedListener.setVersion(versionName,dataBean); //do something... break; case VERSION_FOUR: dataBean.setDataA(1); dataBean.setDataB(2); dataBean.setDataC(3); dataBean.setDataD(4); dataBean.setDataASetted(true); dataBean.setDataBSetted(true); dataBean.setDataCSetted(true); dataBean.setDataDSetted(true); mOnVersionChoosedListener.setVersion(versionName,dataBean); //do something... break; default: break; } } public DataBean getVersion(){ if(dataBean.isDataASetted()){ dataBean.getDataA(); } if(dataBean.isDataBSetted()){ dataBean.getDataB(); } if(dataBean.isDataCSetted()){ dataBean.getDataC(); } if(dataBean.isDataDSetted()){ dataBean.getDataD(); } return dataBean; } public int getVersionName(){ return mOnVersionChoosedListener.getVersionName(); }
interface OnVersionChoosedListener{ /** * 设置版本 * @param version * @param dataBean */ void setVersion(int version, DataBean dataBean); /** * 获取版本号 * @return */ int getVersionName(); /** * 获取对应的实体类对象 方便下一步的操作 * @return */ DataBean getVersion(); } public void setOnVersionChoosedListener(@NonNull OnVersionChoosedListener onVersionChoosedListener) { mOnVersionChoosedListener = onVersionChoosedListener; } }
五、使用观察者模式处理
。。。待完善。。。
基本就这些了,如果你还有更好的解决方式,欢迎提出疑问,留言或者私信我都可以。