陈禹樵,隋 浩
(中移互联网有限公司云产品事业部 广东 广州 510000)
截至2021年2月,全球约70.46%的手机和平板搭载了Android操作系统,在Google Play商店中,可以下载到的Android应用数量达到295万个。为了获得更多的用户,Android应用开发市场面临着激烈的竞争。许多企业和开发者为了快速实现功能,忽视了架构的设计,随着版本的迭代,功能添加和修改,应用越做越复杂,开发和维护的效率降低,应用质量也出现下滑。为了提升代码的质量,提高应用的运行效率,代码重构成为开发者面临的现实问题。本文从设计模式的角度出发,结合案例讨论其在代码重构过程中的应用。
重构是指在不改变软件可观察行为的前提下,对软件内部结构进行修改,使得软件更容易理解,更容易修改[1]。在软件开发的迭代过程中,基于以下因素,代码重构过程是必不可少的:(1)代码架构设计的不足;(2)软件功能的叠加和修改;(3)开发技术的发展和进步;(4)开发规范的执行和变化。人们不可能实现一份适配所有变化的系统,保证后续的需求都不偏离最初的设计。代码重构是纠正这些偏离,实现架构进步的一种手段。
代码重构的主要目的包括:(1)优化代码结构,降低软件结构的复杂性,使得代码结构清晰,阅读、维护和拓展简单;(2)提升软件运行的效率,通过重构简化复杂的逻辑判断、重复的执行过程、过时的方法架构,优化速度;(3)提高软件的健壮性,解决软件迭代过程中积累的潜在问题;(4)控制代码规范,统一不同人员的代码风格和习惯,适配行业和企业制定的规范等。
代码重构的主要方法有:(1)不良代码的识别与监测;(2)基于搜索的重构方法;(3)基于UML模型的重构方法;(4)基于代码异味的重构方法;(5)基于测试驱动的重构方法;(6)自动化重构工具[2]。
代码重构的时机选择:(1)添加新功能时一并重构;(2)修补错误时一并重构;(3)代码审查时进行重构;(4)代码设计完全错误时,应当重写;(5)重构工作应安排入主版本发布计划,有序实施。
设计模式是指经过众多的软件开发人员在长期的开发工作中,总结出的解决软件开发过程中面临的一般性问题的模板化的设计方法。这些方法通常为了更简单方便的改进或者复用以往成功的设计和体系机构,又能有效地处理需求的变更,某些模式可以减少类之间的耦合与依赖[3]。这些设计模式被软件开发人员在后续的代码设计过程中反复实践和验证。
Gamma等[4]于1995年出版的《设计模式:可复用面向对象软件的基础》一书中收录了23种设计模式。这些设计模式在 Java、C++、Object-C、Kotlin等多种面向对象的编程语言都得到了实现,成为不同语言共用的模板方式。近年来,在解决复杂的软件架构设计问题上,MVC、MVP、MVVM等设计模式在Android软件开发过程中,也得到了广泛的应用。
将设计模式思想应用于软件重构中,可以解决提升代码复用率,降低代码耦合度,使得代码结构更加清晰,进而提高代码的可维护性,提高软件健壮性、扩展性和规范化。
不同的设计模式面向解决不同场景下的设计问题。在软件重构过程中,应当根据问题选择最优的设计模式,保证重构效果。在Android应用开发和Android系统源码中,常用的设计模式约有7~8种,这些设计模式有着不同的变体,但其基本思想是一致的。熟悉这些常用的设计模式,可以解决重构过程中的大部分问题。下面将结合具体问题,讨论5种常用设计模式在代码重构工作中的应用。
单例模式适合用于内存常驻、信息配置、公用方法或者全局管理等职责单一的任务。例如:Android应用经常会使用到设备信息,作为运行设备的唯一标识,对于设备信息的获取和管理,在重构过程中可以构建DeviceInfo Manager单例对象,实现一次获取,多次使用的目的。Android应用开发常会用到多线程技术,对于子线程和线程池的管理,可以在重构过程中构建Common Executor单例对象,统一管理子线程数量,管控子线程的运行、同步和结束。需要注意的是,在单例对象实例化时,需要注意线程同步问题,可采用双重锁的方式,避免异步线程产生空指针异常。单例模式不利于扩展,对象常常需要常驻内存,因此也不可滥用。
观察者模式适用于消息通知和事件传递等场景,在Android源码和一些优秀的Android开源框架中,例如:ListView、RecyclerView、RxJava、OKHttp等,有着广泛的应用。近两年发展的ViewModel、LiveData等框架也是由观察者模式思想发展而来。在重构过程中,可以应用于以下场景,例如:Android应用常会用到推送功能,在与服务端交互的过程中,新消息的到达会导致数据和状态的改变。此类场景的重构,可以用Observable定义消息数据的更新与处理,将视图层定义为Observer,订阅数据处理类,在数据处理后,通过Observer通知视图的刷新,解耦数据与视图代码的耦合。
在开发过程中,经常会遇到这样的场景:产品经理要求在某些行为函数前加入功能、日志或者数据统计。在不改变现有设计的情况下,代理模式是一种友好的选择。代理模式可以控制对象的间接访问,修饰和操作对象的输入输出。例如:对于数据访问的权限判断重构场景中,可以隐藏数据访问类的visit方法,加入VisitorProxy类,在调用visit函数时,通过VisitorProxy类先调起权限判断功能,根据其返回结果判断是否继续调用visit方法。这样可以将权限判断和数据访问的代码有效解耦,降低代码混乱。
责任链模式的设计起源于工厂生产中的流水线思想,符合面向对象设计的单一职责原则。责任链模式的设计思路为:将一项任务分解成多个子任务步骤,对于子任务设计隔离开的接口和实现,再将子任务衔接成完整的任务流水。责任链模式在开源框架Glide、OKHttp、Gson中都得到了应用。例如:在邮箱应用中,新邮件数据的同步和显示,该过程包括:新邮件通知→同步邮件头→解析邮件头→解析邮件结构→存储邮件结构→刷新邮件列表→下载邮件正文→存储邮件正文→下载邮件附件→存储邮件附件,在重构过程中,可以选用责任链模式来实现。将以上过程抽象成Chain,每个阶段的任务实现Chain中的process方法,并指定下一阶段任务,依次传递,直到邮件同步和显示任务处理完毕。
工厂模式可以归为三类:简单工厂模式、工厂方法模式和抽象工厂模式。简单工厂模式通常以静态代码的形式,预先定义“工厂类”所能生成的“产品类”。工厂方法模式实现了对工厂角色的抽象,定义包含必须的工厂行为的接口类,在生产具体产品时,定义实现抽象工厂接口的具体工厂来实现,提高了对于产品生产行为的拓展性。抽象工厂模式,不仅工厂角色是抽象的,产品角色也是抽象的,适用于产品属性未产生本质变化的生产行为。例如:对于图像的处理工作的重构。图像处理包括色调、饱和度、亮度、大小、旋转等操作,这些行为可定义为抽象的Abstract Image Process Fatory,根据具体操作实现为Tone Process Fatory、Saturation Fatory、Brightness Fatory、Size Factory、Rotate Factory。其中Tone Process Fatory用于实例化具体的Abstract Tone Effect类。色调可能有暖色调、冷色调、怀旧效果等不同的需求,但其有着共同的抽象方法,可以定义继承Abstract Tone Effect实现具体的处理逻辑。
关于Android应用重构过程中设计模式的选择,应该充分考虑软件设计和开发过程中可能会发生的潜在变化因素,考虑实现设计模式对软件运行和架构产生的负担,衡量应用该设计模式所带来的价值与成本负担。其基本过程如下:(1)理解任务需求,保证产品需求和功能表象不发生本质变化;(2)梳理现有代码的基本框架,列出其需要重构的原因;(3)研究当前任务未来可能发生变化和添加的潜在需求;(4)匹配任务属性及潜在需求与设计模式的匹配度;(5)衡量使用设计模式修改代码的效率成本和时间成本;(6)安排分工,使用设计模式重构现有代码;(7)将重构后的代码使用在部分模块,通过测试比较其于重构前的运行差异;(8)完善重构代码的不足;(9)将重构后的代码全量应用,移除旧代码;(10)测试及上线。
随着Android应用日益激烈的竞争,代码重构成为了提高其软件质量、可维护性、可复用性、保证软件开发可持续发展的重要手段。本文从设计模式角度出发,探讨了设计模式与代码重构目的的一致性,以及其在Android代码重构方面的应用。代码重构和框架设计是彼此互补又相互联系的环节,在软件设计、开发及维护阶段,都有必要借助科学合理的设计模式,以提升代码的质量,进而加强应用的竞争力。