如何减少项目中if-else嵌套
一般建议if-else嵌套不超过三层
原始代码
Bean类
private class ShareItem{
int type;
String title;
String content;
String imagePath;
String link;
}
public interface ShareListener{
int STATE_SUCC=0;
int STATE_FAIL=1;
void onCallback(int atate,String msg);
}
定义分享接口
public void share(ShareItem item,ShareListener listener){
if(item!=null){
if(item.type==TYPE_LINK){
if(!TextUtils.isEmpty(item.link)&&!TextUtils.isEmpty(item.title)){
doShareLink(item.link,item.title,item.content,listener);
}else{
if(listener!=null){
listener.onCallback(ShareListener.STATE_FAIL,"分享信息不完整");
}
}
}else if(item.type==TYPE_IMAGE){
if(!TextUtils.isEmpty(item.imagePath)){
doShareImage(item.imagePath,listener);
}else{
if(listener!=null){
listener.onCallback(ShareListener.STATE_FAIL,"分享信息不完整");
}
}
}else if(item.type==TYPE_TEXT){
if(!TextUtils.isEmpty(item.content)){
doShareText(item.content,listener);
}else{
if(listener!=null){
listener.onCallback(ShareListener.STATE_FAIL,"分享信息不完整");
}
}
}else if(item.type==TYPE_IMAGE_TEXT){
if(!TextUtils.isEmpty(item.content)&&!TextUtils.isEmpty(item.imagePath)){
doShareImageAndText(item.imagePath,item.content,listener);
}else{
if(listener!=null){
listener.onCallback(ShareListener.STATE_FAIL,"分享信息不完整");
}
}
}else{
if(listener!=null){
listener.onCallback(ShareListener.STATE_FAIL,"不支持的分享类型");
}
}
}else{
if(listener!=null){
listener.onCallback(ShareListener.STATE_FAIL,"ShareItem不能为null");
}
}
}
目前来看思路清晰,但share方法分支15条,意味着每次回看代码都的考虑15种情况,并把15中情况全部测试,如果增加效其他功能又要增加多条分支,还要读代码改代码。我们的脑力不该花费在无止境的分支语句里。分析代码上面代码分支原因:null判断、业务判断、状态判断
多次判断是否为null时可以接口分层
1、方法一:接口分层
所谓接口分层指:把接口分为内部和外部接口,所有空值判断放在外部接口完成,只处理一次;而内部接口传入的变量有内部接口保证不为空,从而减少null
public void share(ShareItem item,ShareListener listener){
if(item==null){
if(listener!=null){
listener.onCallback(ShareListener.STATE_FAIL,"ShareItem不能为null");
}
return;
}
if(listener==null){
listener=new shareListener(){
@Override
public void onCallback(int state,String msg){
Log.i("DEBUG","ShareListener is null");
}
};
}
shareImpl(item,listener);
}
private void shareImpl(ShareItem item,ShareListener listener){
if(item.type==TYPE_LIKE){
if(!TextUtils.isEmpty(item.link)&&!TextUtils.isEmpty(item.title)){
doShareLink(item.link,item.title,item.content,listener);
}else{
listener.onCallback(ShareListener.STATE_FAIL,"分享信息不完整");
}
}else if(item.type==TYPE_IMAGE){
if(!TextUtils.isEmpty(item.imagePath)){
doShareImage(item.imagePath,listener);
}else{
listener.onCallback(ShareListener.STATE_FAIL,"分享信息不完整");
}
}else if(item.type==TYPE_TEXT){
if(!TextUtils.isEmpty(item.content)){
doShareText(item.content,listener);
}else{
listener.onCallback(ShareListener.STATE_FAIL,"分享信息不完整");
}
}else if(item.type==TYPE_IMAGE_TEXT){
if(!TextUtils.isEmpty(item.content)&&!TextUtils.isEmpty(item.imagePath)){
doShareImageAndText(item.imagePath,item.content,listener);
}else{
listener.onCallback(ShareListener.STATE_FAIL,"分享信息不完整");
}
}else{
listener.onCallback(ShareListener.STATE_FAIL,"不支持的分享类型");
}
}
上面代码分为外部接口share和内部接口shareImpl,ShareItem和ShareListener的判断都放在share里完成,那么shareImpl就减少了if-else嵌套,这样嵌套就不超过3层
但shareImpl了还是包含分享类型的判断,即业务判断,分享的类型随时可能改变或添加,我们用多态解决,多态不但能应付业务改变的情况,也可以减少if-else嵌套
2、方法二:利用多态
利用多态,每种业务单独处理,在接口不再做任何业务判断。把shareItem抽象出来,作为基础类,然后针对每种业务各自实现其子类
public abstract class ShareItem {
int type;
public ShareItem(int type) {
this.type = type;
}
public abstract void doShare(ShareListener listener);
}
public class Link extends ShareItem {
String title;
String content;
String link;
public Link(String link, String title, String content) {
super(TYPE_LIKE);
this.link = !TextUtils.isEmpty(link) ? link : "default";
this.title = !TextUtils.isEmpty(title) ? title : "default";
this.content = !TextUtils.isEmpty(content) ? content : "default";
}
@Override
public void doShare(ShareListener listener) {
}
}
public class Image extends ShareItem {
String imagePath;
public Image(String imagePath) {
super(TYPE_IMAGE);
this.imagePath = !TextUtils.isEmpty(imagePath) ? imagePath : "default";
}
@Override
public void doShare(ShareListener listener) {
}
}
public class Text extends ShareItem {
String content;
public Text(String content) {
super(TYPE_TEXT);
this.content = !TextUtils.isEmpty(content) ? content : "default";
}
@Override
public void doShare(ShareListener listener) {
}
}
public class ImageText extends ShareItem {
String content;
String imagePath;
public ImageText(String imagePath, String content) {
super(TYPE_IMAGE_TEXT);
this.imagePath = !TextUtils.isEmpty(imagePath) ? imagePath : "default";
this.content = !TextUtils.isEmpty(content) ? content : "default";
}
@Override
public void doShare(ShareListener listener) {
}
}
注意:上面每个子类的构造方法还对每个字段做了空值处理,为空的话,赋值default,这样如果用户传了空值,在调试就会发现问题。
实现多态后,分享接口就简单多了:
public void share(ShareItem item,ShareListener listener){
if(item==null){
if(listener1!=null){
listener.onCallback(ShareListener.STATE_FAIL,"ShareItem不能为null");
}
return;
}
if(listener==null){
listener=new ShareListener(){
@Override
public void onCallback(int state,String msg){
Log.i("DEBUG","ShareListener is null");
}
};
}
shareImpl(item,listener);
}
private void shareImpl(ShareItem item,ShareListener listener){
item.doShare(listener);
}
如果这个分享功能是自己App里的功能,不是第三方SDK,到这里已经没有问题了。但如果时第三方SDK的功能的话,这样暴漏给用户的类就增加了很多,用户的接入成本很高,违背了迪米特原则。处理这种情况也简单,再次封装一层即可。把ShareItem的子类的访问权限降低,在暴漏给用户的主类里定义几个方法,在内部帮助用户创建具体的分享类型,这样用户就无需知道具体的类了:
public ShareItem createLinkShareItem(String link,String title,String content){
return new Link(link,title,content);
}
public ShareItem createImageShareItem(String ImagePath){
return new Image(ImagePath);
}
public ShareItem createTextShareItem(String content){
return new Text(content);
}
public ShareItem createImageTextShareItem(String ImagePath,String content){
return new ImageText(ImagePath,content);
}
或者有人会说,这样用户也需要额外来了解多几个方法,其实让用户多了解几个方法好过多了解几个类,而方法名一看就知道意图,成本还是挺小的,时可以接受的。
其实这种情况,更多人想到的是工厂模式,但工厂模式用户也需要额外了解多今几个type类型,而且工厂模式难免要引入分支,我们可以用Map消除分支。
3、方法三:使用Map代替分支语句
把所有分享类型预先缓存在Map里,那么就可以直接get获取具体类型,消除分支:
private Map<Integer,Class<? extends ShareItem>> map=new HashMap<>();
private void init(){
map.put(TYPE_LINK,Link.class);
map.put(TYPE_IMAGE,Image.class);
map.put(TYPE_TEXT,Text.class);
map.put(TYPE_IMAGE_LINK,ImageText.class);
}
public ShareItem creatShareItem(int type){
try{
Class<? extends ShareItem> shareItemClass = map.get(type);
return shareItemClass.newInstance();
}catch(Exception e){
return new DafaultShareItem();
}
}