在某种工作应用程序中,我看到了这个可怕的代码:
class SomeUglyClass extends Thread {
ArrayList<SomeData> someDataStructure = new ArrayList<SomeData>();
Handler mHandler = new Handler() {
// a lot
// writing to someDataStructure
}
public void run() {
int some_count, ...;
while(true) {
// a lot
// r/w access to someDataStructure
try {
Thread.sleep(1, 0);
} catch (Exception e) {
break;
}
}
} // end of run(), total 500 lines of code
} // end of SomeUglyClass, total 4K lines of code
也许您已经看到了此代码的问题。如果没有,它们是:
mHandler
附加到 UI 线程(因为它是由加载类的线程创建的,该线程是主线程)没有循环器(这是事实错误)
线程浪费 CPU 时间并耗尽电池电量
someDataStructure
不是线程安全的,但同步基本访问操作将无济于事;在无限循环中同步大型代码块可能会阻塞受保护的资源并使其对其他线程不可用;最后,它不仅someDataStructure
,整个类都基于只有一个线程可以运行其代码的假设。我不能只添加循环器,因为
run()
中的无限循环必须运行,而Looper.loop();
也是一个无限循环。一个线程不能运行两个无限循环。
尽管有这个史诗般的架构失败,但代码确实在做一些事情,它不能一次重写,它是 4K 行代码,而且通常我只能猜测代码真正做了什么。
我需要重构它。它应该是保留功能的一系列小步骤。
如何重构这个了不起的代码?
你应该尝试分离关注点:首先尝试将整个类分成许多最小的类,每个类只负责做/处理一件事。
您可能具有数据访问(读/写数据)、服务(独立业务逻辑)和 UI 的内容。您可以使用事件总线在对象之间解耦(考虑 otto),也可以使用依赖注入(考虑 Dagger)。
这个分离过程将帮助您了解每段代码在做什么以及不同部分之间的依赖关系,从而使编写单元/集成测试变得更加容易。
添加大量测试,使用版本控制,然后根据需要尽可能慢地工作。
第一步是更改:
public void run() {
int some_count, ...;
while(true) {
// a lot
// r/w access to someDataStructure
try {
Thread.sleep(1, 0);
} catch (Exception e) {
break;
}
}
}
自:
@Override
public void run() {
Looper.prepare();
mHandler = new MyHandler();
mHandler.post(run_step);
Looper.loop();
}
Runnable run_step = new Runnable() {
int some_count, ...;
@Override
public void run()
{
//while(true) {
// a lot
// r/w access to someDataStructure
mIntoThreadHandler.postDelayed(this, 1);
//}
}
}
这样可以保留功能,但仍会浪费 CPU 时间。紧急错误已修复,问题已关闭;我不能向我的管理层出售"必须重构以杀死可怕的代码",但我可以出售"如果我重构,这可以更快地工作",因此打开了一个新的单独问题。呸!
PS没有机会出售"大量测试"。