減少復雜的if-else嵌套


前幾天看到有一篇不錯的文章減少該死的if-else嵌套,覺得寫得很不錯,整理了一下后准備在團隊內部簡單分享一下。

寫在前面

大家在接手項目的時候,應該有遇到過下面這種結構的代碼

if (true) {
    .....
    if (true) {
        if (true) {
            ....
            if (true) {
                if (true) {
                    ......
                }
            }
            // do something
        }
       // do something
       if (true) {
           .........
       }
    }
    // do something
}

看到這些代碼,第一反應是腦殼痛,N多的if-else已經將這段代碼的邏輯變得十分復雜,代碼的可讀性和可維護性也會變得極差。下面舉個實際案例來介紹如何優化這種代碼。

實際案例

業務需求:提供一個服務,可以讓用戶分享鏈接、圖片、文本和圖文,並將分享結果回調給用戶。

初看這個業務很需求很簡單,先寫一個簡單的demo

public class ShareItem {

    public static final int TYPE_LINK = 0;
    public static final int TYPE_IMAGE = 1;
    public static final int TYPE_TEXT = 2;
    public static final int TYPE_IMAGE_TEXT = 3;

    public int type;
    public String title;
    public String content;
    public String imagePath;
    public String link;
}
public void share (ShareItem item, ShareListener listener) {
    if (item != null) {
        if (item.type == TYPE_LINK) {
            // 分享鏈接
            if (!StringUtils.isEmpty(item.link) && !StringUtils.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 (!StringUtils.isEmpty(item.imagePath)) {
                doShareImage(item.imagePath, listener);
            } else {
                if (listener != null) {
                    listener.onCallback(ShareListener.STATE_FAIL, "分享信息不完整");
                }
            }
        } else if (item.type == TYPE_TEXT) {
            // 分享文本
            if (!StringUtils.isEmpty(item.content)) {
                doShareText(item.content, listener);
            } else {
                if (listener != null) {
                    listener.onCallback(ShareListener.STATE_FAIL, "分享信息不完整");
                }
            }
        } else if (item.type == TYPE_IMAGE_TEXT) {
            // 分享圖文
            if (!StringUtils.isEmpty(item.imagePath) && !StringUtils.isEmpty(item.content)) {
                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");
        }
    }
}

上面這段代碼,寫完后好像沒啥問題,至少思路是清晰的,但是如果經過幾輪需求的摧殘,這個代碼可能就會變得很爆炸。比如說,新增了好幾個分享方式,那if-else就會一直往下疊,更不用說如果產品來了一些定制化的需求,if-else里面就會再嵌if-else,最終極可能會得和最上面的代碼例子一樣了。

分層、衛語句

分層

接口分為外部和內部接口,所有空值判斷放在外部接口完成,只處理一次;而內部接口傳入的變量由外部接口保證不為空,從而減少空值判斷。

Controller(簡單校驗) -> Service(核心業務處理) -> Data

Controller -> InternalService(簡單校驗) -> Service(核心業務處理) -> Data

衛語句

將異常流提前返回,好處是可以將異常流和正常流的代碼分開,讓開發人員將精力更聚焦在正常的業務流程上面,而且這種方式也能有效地較少if—else的嵌套層數。

if (a != null) {
    // 正常流
} else {
    // 異常流
}
if (a == null) {
    // 異常流
    return;
}

// 正常流

改造后的代碼

public boolean share(ShareItem item, ShareListener listener) {
    // 前置校驗
    if (listener == null) {
        log.error("listener 不能為空!");
        return false;
    }
    if (item == null) {
        log.error("shareItem 不能為空!");
        return false;
    }

    // 核心業務處理
    shareProcess(item, listener);
    return true;
}

public void shareProcess(ShareItem item, ShareListener listener) {
    if (item.type == TYPE_LINK) {
        // 分享鏈接
        if (!StringUtils.isEmpty(item.link) && !StringUtils.isEmpty(item.title)) {
            doShareLink(item.link, item.title, item.content, listener);
        } else {
            listener.onCallback(ShareListener.STATE_FAIL, "分享信息不完整");
        }
    } else if (item.type == TYPE_IMAGE) {
        // 分享圖片
        if (!StringUtils.isEmpty(item.imagePath)) {
            doShareImage(item.imagePath, listener);
        } else {
            listener.onCallback(ShareListener.STATE_FAIL, "分享信息不完整");
        }
    } else if (item.type == TYPE_TEXT) {
        // 分享文本
        if (!StringUtils.isEmpty(item.content)) {
            doShareText(item.content, listener);
        } else {
            listener.onCallback(ShareListener.STATE_FAIL, "分享信息不完整");
        }
    } else if (item.type == TYPE_IMAGE_TEXT) {
        // 分享圖文
        if (!StringUtils.isEmpty(item.imagePath) && !StringUtils.isEmpty(item.content)) {
            doShareImageAndText(item.imagePath, item.content, listener);
        } else {
            listener.onCallback(ShareListener.STATE_FAIL, "分享信息不完整");
        }
    } else {
        listener.onCallback(ShareListener.STATE_FAIL, "不支持的分享類型");
    }
}

多態

利用多態,每種業務單獨處理,在接口不再做任何業務判斷。把ShareItem抽象出來,作為基礎類,然后針對每種業務各自實現其子類:

public abstract class ShareItem {

    public static final int TYPE_LINK = 0;
    public static final int TYPE_IMAGE = 1;
    public static final int TYPE_TEXT = 2;
    public static final int TYPE_IMAGE_TEXT = 3;

    public int type;

    public ShareItem(int type) {
        this.type = type;
    }

    public abstract boolean validate();

    public abstract void doShare(ShareListener listener);
}
public class LinkShareItem extends ShareItem {

    private String title;
    private String content;
    private String link;

    public LinkShareItem(String title, String content, String link) {
        super(TYPE_LINK);
        this.title = title;
        this.content = content;
        this.link = link;
    }

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void doShare(ShareListener listener) {
        // do share
    }
}

public class ImageShareItem extends ShareItem {

    private String imagePath;

    public ImageShareItem(String imagePath) {
        super(ShareItem.TYPE_IMAGE);
        this.imagePath = imagePath;
    }

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void doShare(ShareListener listener) {
        // do share
    }
}

public class ImageTextShareItem extends ShareItem {

    private String content;
    private String imagePath;

    public ImageTextShareItem(String content, String imagePath) {
        super(ShareItem.TYPE_IMAGE_TEXT);
        this.content = content;
        this.imagePath = imagePath;
    }

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void doShare(ShareListener listener) {
        // do share
    }
}

public class TextShareItem extends ShareItem {

    private String content;

    public TextShareItem(String content) {
        super(ShareItem.TYPE_TEXT);
        this.content = content;
    }

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void doShare(ShareListener listener) {
        // do share
    }
}

將實現分離出來后,方法就變得很簡潔了,if-else基本都已經被消除,以后如果新增一種分享方式,就只需要編寫對應的實現方式就可以了。

public void share(ShareItem item, ShareListener listener) {
    // 前置校驗
    if (listener == null) {
        log.error("listener 不能為空!");
        return;
    }
    if (item == null) {
        log.error("shareItem 不能為空!");
        return;
    }

    // 核心業務處理
    shareProcess(item, listener);
}

public void shareProcess(ShareItem item, ShareListener listener) {
    boolean validateRet = item.validate();
    if (!validateRet) {
        listener.onCallback(ShareListener.STATE_FAIL, "分享信息不完整");
        return;
    }

    item.doShare(listener);
}

策略模式

上面的多態模式,是不是看上去已經很完美了?不過在我眼里其實不然,有一點不太合理的是,ShareItem作為POJO,里面不適應做過多的業務實現。目前貧血模式下,ShareItem應該作為一個BO,里面只放基礎的屬性值,而業務實現應該都放到各自的service里面。

這時候我們可以考慮使用策略模式對其進行優化。

將原ShareItem的業務邏輯代碼移除,僅保留基礎的屬性

public abstract class ShareItem {
    public static final int TYPE_LINK = 0;
    public static final int TYPE_IMAGE = 1;
    public static final int TYPE_TEXT = 2;
    public static final int TYPE_IMAGE_TEXT = 3;

    public int type;
}

public class ImageShareItem extends ShareItem {
    private String imagePath;
}

public class ImageTextShareItem extends ShareItem {
    private String content;
    private String imagePath;
}

public class LinkShareItem extends ShareItem {
    private String title;
    private String content;
    private String link;
}

public class TextShareItem extends ShareItem {
    private String content;
}

編寫策略類

public abstract class AbstractShareHandler {

    public abstract boolean validate();

    public abstract void doShare(ShareListener listener);

    public abstract int getShareType();
}

@Service
public class ImageShareHandle extends AbstractShareHandler {

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void doShare(ShareListener listener) {

    }

    @Override
    public int getShareType() {
        return ShareItem.TYPE_IMAGE;
    }
}

@Service
public class ImageTextShareHandler extends AbstractShareHandler {

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void doShare(ShareListener listener) {

    }

    @Override
    public int getShareType() {
        return ShareItem.TYPE_IMAGE_TEXT;
    }
}

@Service
public class LinkShareHandler extends AbstractShareHandler {

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void doShare(ShareListener listener) {

    }

    @Override
    public int getShareType() {
        return ShareItem.TYPE_LINK;
    }
}

@Service
public class TextShareHandler extends AbstractShareHandler {

    @Override
    public boolean validate() {
        return false;
    }

    @Override
    public void doShare(ShareListener listener) {

    }

    @Override
    public int getShareType() {
        return ShareItem.TYPE_TEXT;
    }
}

創建一個Manager類用來獲取和管理策略

@Service
public class ShareManager implements InitializingBean, ApplicationContextAware {

    /**
     * 策略集合
     */
    private Map<Integer, AbstractShareHandler> shareHandlerMap = new HashMap<>();

    private ApplicationContext applicationContext;

    /**
     * 獲取處理的策略
     * @param shareType
     * @return
     */
    public AbstractShareHandler getHandler(Integer shareType) {
        AbstractShareHandler shareHandler = shareHandlerMap.get(shareType);
        if (shareHandler == null) {
            throw new IllegalArgumentException("shareType handler not found, shareType=" + shareType);
        }
        return shareHandler;
    }

    @Override
    public void afterPropertiesSet() {
        // 自動注入策略
        Map<String, AbstractShareHandler> shareImplMap = applicationContext.getBeansOfType(AbstractShareHandler.class);
        for (AbstractShareHandler handler : shareImplMap.values()) {
            shareHandlerMap.put(handler.getShareType(), handler);
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

最后調整后的share方法就會變為下圖那樣

@Resource
private ShareManager shareManager;

public void share(ShareItem item, ShareListener listener) {
    // 前置校驗
    if (listener == null) {
        log.error("listener 不能為空!");
        return;
    }
    if (item == null) {
        log.error("shareItem 不能為空!");
        return;
    }

    // 核心業務處理
    shareProcess(item, listener);
}

private void shareProcess(ShareItem item, ShareListener listener) {
    AbstractShareHandler handler = shareManager.getHandler(item.getType());

    if (!handler.validate()) {
        listener.onCallback(ShareListener.STATE_FAIL, "分享信息不完整");
        return;
    }

    handler.doShare(listener);
}

至此,我們就用策略模式對這段代碼進行了優化,看上去是不是比之前的版本好多了呢?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM