Java编程语言爱好者

代码易测性之异步代码重构

2023-03-14  本文已影响0人  李不言被占用了

这个重构技巧的核心就是不要在Runnable#run方法里写业务逻辑,将业务逻辑抽成一个单独的方法,测试起来更方便。

重构前:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.apache.commons.lang3.StringUtils;

public class Service {
    private BlockingQueue<String> queue = new ArrayBlockingQueue<>(128);
    
    public void start() {
        new Thread(new EventRunnable()).start();
    }

    public void doService() {
        String event = null;
        xxx;
        xxxx;
        xxxx;
        queue.offer(event);
    }

    private class EventRunnable implements Runnable {

        @Override
        public void run() {
            try {
                String event = queue.take();
                if (StringUtils.isNotBlank(event)) {
                    xxx;
                    xxxx;
                    xxx;
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这段代码很好理解,Service#doService处理后将event放到队列中,然后由消费线程获取并处理EventRunnable#run

这段代码最大的问题就是可测性差,重构一下:

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import org.apache.commons.lang3.StringUtils;

public class Service {
    private BlockingQueue<String> queue = new ArrayBlockingQueue<>(128);

    public void start() {
        new Thread(new EventRunnable()).start();
    }

    public void doService() {
        String event = null;
        xxx;
        xxxx;
        xxxx;
        enQueue(event);
    }

    private void enQueue(String event) {
        queue.offer(event);
    }

    private class EventRunnable implements Runnable {

        @Override
        public void run() {
            try {
                String event = queue.take();
                processEvent(event);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void processEvent(String event) {
        if (StringUtils.isNotBlank(event)) {
            xxx;
            xxxx;
            xxx;
        }
    }
}

抽出enQueueprocessEvent方法。这样只要测试processEvent就能知道逻辑是否正确,不涉及多线程。
更多的,还可以通过Mock把异步代码变成同步调用。

public void mock() {
    new MockUp<Service> () {
       @Mock
       public void start() {
           // 不用启动异步线程
       }
       
       @Mock
       public void enQueue(Invocation invocation, String event) {
           // 直接变成同步调用,串联起逻辑
           Service instance = invocation.getInvokedInstance();
           instance.processEvent(event);
       } 
    };
}

以上代码以JMockit为例,用Mockito也差不多。

上一篇 下一篇

猜你喜欢

热点阅读