在平时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;
}
}
五、使用观察者模式处理
。。。待完善。。。
基本就这些了,如果你还有更好的解决方式,欢迎提出疑问,留言或者私信我都可以。
喜欢的话就打赏,点个赞 吧!