プロジェクト内のif-elseネストを減らす方法

44583 ワード

プロジェクト内のif-elseネストを減らす方法


一般的にif-elseネストは3層を超えないことをお勧めします
元のコード
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、方法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);
}

この共有機能が自分のアプリにある機能で、サードパーティ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、方法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();
    }
}