浅谈代码重构与优化

Posted miguel

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈代码重构与优化相关的知识,希望对你有一定的参考价值。

浅谈代码重构与优化

前几天看到有一篇不错的文章减少该死的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);
}

至此,我们就用策略模式对这段代码进行了优化,看上去是不是比之前的版本好多了呢?

以上是关于浅谈代码重构与优化的主要内容,如果未能解决你的问题,请参考以下文章

浅谈压缩感知(三十):压缩感知重构算法之L1最小二乘

浅谈重构

如何重构这个 Java 代码片段

PHP代码优化(重构)[关闭]

浅谈优化----nginx与php增大并发

敏捷和DevOps词汇表