重构以修复架构错误

时间:2015-01-26 09:16:30

标签: android multithreading android-looper

在一个工作类型的应用程序中,我看到了这个可怕的代码:

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

也许你已经看到了这段代码的问题。如果没有,这里是:

  1. mHandler附加到UI线程(因为它是由加载类的线程创建的,这是主线程)

  2. 没有活套(事实是 错误

  3. 线程浪费CPU时间并耗尽电池

  4. someDataStructure不是线程安全的,但同步基本访问操作无济于事;在无限循环中同步大块代码可能会阻止受保护的资源并使其对其他线程不可用;最后,不仅someDataStructure,整个类基于只有一个线程可以运行其代码的假设。

  5. 我不能只添加looper,因为run()中的无限循环必须运行,而Looper.loop();也是无限循环。一个线程无法运行两个无限循环。

  6. 尽管这个史诗般的体系结构失败了,但代码实际上是在做某些事情,它不能立刻重写,它是4K行代码,而且我常常只能猜出代码的真正含义。

    我需要重构它。它应该是一系列保留功能的小步骤。

    如何重构这个极好的代码?

3 个答案:

答案 0 :(得分:1)

你应该尝试separation of concerns:首先尝试将整个班级划分为许多最小的班级,每个班级负责完成/处理一件事。

您可能拥有数据访问(读/写数据),服务(独立业务逻辑)和UI。您可以使用事件总线在对象之间进行解耦(考虑otto)并且可能是依赖注入(考虑Dagger)。

这种分离过程将帮助您了解每段代码的作用以及不同部分之间的依赖关系,从而使编写单元/集成测试变得更加容易。

答案 1 :(得分:0)

添加大量测试,使用版本控制,然后根据需要缓慢工作。

答案 2 :(得分:0)

第一步是改变:

    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时间。紧急错误已得到修复,问题已经结束;我无法向我的管理层出售“必须重构以杀死怪物代码”,但我可以出售“如果我重构,这可以更快地工作”,所以一个新的单独问题已经被打开了。 UGH!

PS没有机会出售“大量测试”