這個(gè)重構(gòu)技巧的核心就是不要在Runnable#run
方法里寫(xiě)業(yè)務(wù)邏輯,將業(yè)務(wù)邏輯抽成一個(gè)單獨(dú)的方法徐裸,測(cè)試起來(lái)更方便遣鼓。
重構(gòu)前:
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
放到隊(duì)列中重贺,然后由消費(fèi)線(xiàn)程獲取并處理EventRunnable#run
骑祟。
這段代碼最大的問(wèn)題就是可測(cè)性差,重構(gòu)一下:
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;
}
}
}
抽出enQueue
和processEvent
方法气笙。這樣只要測(cè)試processEvent
就能知道邏輯是否正確次企,不涉及多線(xiàn)程。
更多的潜圃,還可以通過(guò)Mock
把異步代碼變成同步調(diào)用抒巢。
public void mock() {
new MockUp<Service> () {
@Mock
public void start() {
// 不用啟動(dòng)異步線(xiàn)程
}
@Mock
public void enQueue(Invocation invocation, String event) {
// 直接變成同步調(diào)用,串聯(lián)起邏輯
Service instance = invocation.getInvokedInstance();
instance.processEvent(event);
}
};
}
以上代碼以
JMockit
為例秉犹,用Mockito
也差不多。