每一个软件开发人员都十分清楚, 当软件构建得越来越复杂时, 可维护性就成了一个很突出的问题。 如何在构造软件系统的过程中始终保持可控制的可维护性呢?
一、 整体组织
首先要从整体组织层面进行规划,基本方法是分层和模块化。
比如, 一般 Java Web 应用系统会划分为 Controllers - Services - (Dao , Network) - Model - Constants - Utils - Result 等层面和模块。 Controllers 是控制层, 负责资源映射、参数传递和结果返回; Services 是服务层, 负责具体的业务逻辑实现; Dao 是数据访问层, 负责与数据库交互; Network 是网络层, 负责从外部系统获取服务, 通常是 HTTP 调用, Model 定义应用系统中的数据对象, Contants 管理应用系统中的所有常量及参数KEY, Utils 管理应用系统中的实用工具类, Result 对应用系统中的结果进行管理。
进一步地, 还可根据各种业务模块进行细分。如果应用系统依赖于某些基础设施, 比如并发, 那么, 可以将这些基础设施单独组织起来, 提供简单而高层的接口。 最终的组织结构可能是这样的:
Project
Constants // 常量管理
Controller // 控制器定义
ecs , slb // 每种业务均有一个包
ControllerImp // 控制器实现
ecs , slb
Service // 服务定义
ecs , slb
ServiceImpl // 服务实现
ecs , slb
Dao // 数据访问定义
ecs , slb
DaoImpl // 数据访问实现
ecs , slb
Model // 数据传输实体定义
ecs , slb
Invoker // 外部调用接口定义
HttpInvoker // http 调用抽象类
EcsInvoker // Ecs 相关的调用实现类
SlbInvoker // Slb 相关的调用实现类
Utils // 实用工具类
Result // 结果接口定义
InvokeResult // 外部系统的结果表示
WebFrontResult // 返回前端的结果表示
ResultModel // 结果中涉及到的实体定义
MultiThreading // 多线程设施模块
callable // 多线程的任务定义
ecs, slb
二、 逻辑分割
提高应用系统可维护性的第二步就是分割系统, 将系统中的业务逻辑尽可能分割为相互独立、容易理解的微小工作单元, 即小方法/小函数。要坚持不懈地迭代这一步, 才能保证在项目开发过程中, 始终保证应用系统的可维护性在可控的范围内。具体方法有:
1. 资源映射: 将不同请求映射到不同的逻辑单元, 这通常可以实现不同请求的逻辑处理是相互独立的; 这些逻辑处理单元尽可能不要涉及到全局资源的共享写访问;
2. 将冗长的逻辑分割为若干段紧密相联的短逻辑, 每段短逻辑只完成一件事情;
3. 将复杂的逻辑分割为多层, 每个层面完成自己的事情, 比如一个完整的Web请求处理: ApacheHttpServer 负责请求的负载均衡; Web Server 负责请求的转发和回调过滤器、监听器进行预处理; Spring 负责 Controller/Service/Dao 单例的构造、初始化和注入应用, 以及请求的映射 ; Controller 负责获取请求的参数,传入 Service , 并获取处理结果 ; Service 负责具体业务逻辑的实现,可能依赖于 Dao 提供的数据库服务, 或者 Network 提供的 API 调用服务, 并将结果进行后处理, 传回给 Controller ; Dao 提供数据访问服务, Network 提供外部系统调用服务, 将获取的结果注入到 Model 的实例集中,交付给 Service ; Utils 在其中做一些打杂的事情。
将复杂逻辑分割为多层, 可能在某种程度上增加系统理解和维护的复杂性, 但有利于分工和团队协作。
三、 细粒度措施
细粒度措施通常是一些基本的方法和要求, 可以借鉴一些常见编程风格, 比如 K&R, Java 编程规范。
1. 适当的空格, 比如 if (condition) { } ; callFunction(arg1, arg2, ..., argN) ;
2. 适当的空行, 紧密相联的逻辑放在一起, 不紧密相联的逻辑用空行分开;
3. 一致的缩进, 通常为 4 个空格;
4. 一致的命名规范, 比如, 对于集合A, 一致使用下划线, 对于集合B, 一致使用驼峰式;
5. 必要的注释, 对于一些依赖于前提的做法, 要做出注释, 比如, // 目前系统仅支持挂载一个ISO ;
6. 必要的引用, 比如, HashMap 的实现基于哈希表, 详见 XXX ;
7. 遵循习惯用法, 比如,大部分情况使用 for (String elem: elementList) {},少量情况用 for (int i=0; i < len; i++){}
8. 制定一些约束做法, 比如, 一个函数或方法最好不超过 100 行;
四、 技术决策
技术决策也常常影响到可维护性。 比如, 你需要动态访问多个数据库, 并且随着业务的发展, 还可能添加更多的数据库配置。 如果采用静态的方式, 在应用启动时读取所有数据库配置, 那么, 当新添数据库配置时, 就不得不重启和发布应用; 而如果采用动态的方式, 在应用启动时读取数据库配置, 并且在新添数据库时发布事件来触发重新读取数据库配置, 就不需要重启和发布应用了, 这无疑是降低了维护成本;
此外, 在大型互联网公司, 一个系统通常不是孤立的, 它处于一个更大的系统架构中, 常常存在多种决策。 是采用 直接访问业务数据库 , 还是通过 API 的方式获取 ? 是采用 mysql , 还是 OTS, ODPS ? 是采用本地服务, 还是云服务? 很多技术决策都会影响应用系统的可维护性。 这些都涉及较大的方面。
即使是小的决策, 也会影响可维护性。 比如, 目前, 系统只支持挂载同类型的磁盘, 于是你在代码里写死了; 日后, 若要支持挂载不同类型的磁盘, 就可能要修改很多代码。 这就意味着: 对变化敏感。 需要评估: 哪些是变化频繁的,需要寻求更灵活的实现,便于将来扩展? 哪些是稳定的, 可以暂时保持不变? 现有的编程实现依赖于哪些隐含的前提条件? 当这些前提条件变化时, 需要多大的代价来应对这些变化 ?
五、 一致处理
需要提取出应用系统中的技术点并进行一致性处理。 比如, 应用中需要动态访问多个数据库, 需要访问多个外部系统提供的服务, 需要支持并发查询, 参数名、枚举常量放在哪里, 如何处理 JSON 或 XML 的结果, 前端的整体框架, 前端页面或窗口之间的传值, 加载和展示数据模式, 映射的统一管理(比如 0 表示管理员,1 表示普通用户), 前端与后台的交互通信模式, 等。 为所有技术点确立一个良好的解决模式, 将大大简化后续的开发和维护工作。
六、 对维护敏感
你还需要对维护工作敏感。 究竟是哪些变更和问题导致你需要进行维护? 为什么这些变更或问题会导致你的应用系统需要随之变更? 是不是原有方案不够彻底, 不够灵活? 还是大环境发生根本变化, 无可避免? 对这些问题进行归纳和总结, 管理这些事项, 可以提高维护效率。
我所遇到的情况主要有如下几种:
1. 原来的方案不够灵活, 静态配置和读取数据库, 当新添数据库配置时, 不得不重新启动应用和发布;
2. 数据库域名的IP地址发生变更, 而 JVM 具有 DNS 缓存作用, 导致不得不重新发布;
3. 数据订正工作;
4. 以不正确权限启动应用, 运行一段时间后崩溃, 或者监控报警;
5. 应用所在的物理机器出问题了;
6. 应用所在的网络环境、权限系统发生变更;
7. 编程实现的不仔细和不彻底导致缺陷或重要错误,需要修复;
8. 应用依赖的外部服务进行了变更发布;
9. 新功能的添加;
- 从电商秒杀与抢购谈Web系统大规模并发
从电商秒杀与抢购谈Web系统大规模并发 http://www.iamlintao.com/4242.html 一.大规模并发带来的挑战 在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程 ...
- 【架构】浅谈web网站架构演变过程
浅谈web网站架构演变过程 前言 我们以javaweb为例,来搭建一个简单的电商系统,看看这个系统可以如何一步步演变. 该系统具备的功能: 用户模块:用户注册和管理 商品模块:商品展示和管 ...
- 【转发】构建高可伸缩性的WEB交互式系统(上)
原文转自:http://kb.cnblogs.com/page/503460/ 可伸缩性是一种对软件系统处理能力的设计指标,高可伸缩性代表一种弹性,在系统扩展过程中,能够保证旺盛的生命力,通过很少的改 ...
- 浅谈web应用的负载均衡、集群、高可用(HA)解决方案(转)
1.熟悉几个组件 1.1.apache —— 它是Apache软件基金会的一个开放源代码的跨平台的网页服务器,属于老牌的web服务器了,支持基于Ip或者域名的虚拟主机,支持代理服务器,支持安 ...
- [原创]浅谈Web UI自动化测试
[原创]浅谈Web UI自动化测试 Web UI自动化测试相信大家都不陌生,今天来谈谈这个,我最早接触自动化测试时大约是在2004年,2006年当时在腾讯财付通算是开始正式接触自动化测试,之所以是正式 ...
- OS.js – 开源的 Web OS 系统,赶快来体验
OS.js 是一个开源的 Web OS 系统,可以在浏览器中运行,提供了窗口管理器,应用程序API,用户界面开发套件和抽象的文件系统等.可以部署在 Node 或者 PHP 环境中运行.OS.js is ...
- 【转发】构建高可伸缩性的WEB交互式系统(下)
原文转自:http://kb.cnblogs.com/page/504518/ 本文是<构建高可伸缩性的WEB交互式系统>系列文章的第三篇,以网易的NEJ框架为例,对模块的可伸缩性进行分析 ...
- 【转发】构建高可伸缩性的WEB交互式系统(中)
原文转自:http://kb.cnblogs.com/page/503953/ 在<构建高可伸缩性的WEB交互式系统>的第一篇,我们介绍了Web交互式系统中平台的可伸缩性.本文将描述模块的 ...
- Walle 瓦力 web部署系统
Walle 一个web部署系统工具,可能也是个持续发布工具,配置简单.功能完善.界面流畅.开箱即用! 安装步骤: 1. git clone 首先配置成功(去百度找答案) 打开git bash命令窗口执 ...
随机推荐
- Swift - 系统声音服务的使用(播放声音,提醒,震动)
1,系统声音服务介绍: 系统声音服务提供了一个Api,用于播放不超过30秒的声音.它支持的文件格式有限,具体的说只有CAF.AIF和使用PCM或IMA/ADPCM数据的WAV文件. 但此函数没有提供操 ...
- 单元测试工具 SmokeTest
.NET 程序集单元测试工具 SmokeTest Smoke Test(冒烟测试),也称Regression Test(回归测试),是对软件的安装和基本功能的测试.一般地我们使用脚本来实现Smoke ...
- Net Core-Razor
几行代码解决Razor中的嵌套if语句 MVC开发中,经常会遇到在razor中插入简单的逻辑判断. @if (clientManager.IsAdmin) { if (!Model.Topic.Top ...
- 关于如何解决谷歌Chrome浏览器空白页的问题
谷歌Chrome浏览器突然不打开任何网页,无论是任何站点(如http://www.baidu.com), 还是Chrome浏览器的设置页面(chrome://settings/), 扩展页面 ( ch ...
- RPC 的概念模型与实现解析(转)
今天分布式应用.云计算.微服务大行其道,作为其技术基石之一的 RPC 你了解多少?一篇 RPC 的技术总结文章,数了下 5k+ 字,略长,可能也不适合休闲的碎片化时间阅读,可以先收藏抽空再细读:) 全 ...
- MVP模式在Android开发中的应用
一.MVP介绍 随着UI创建技术的功能日益增强,UI层也履行着越来越多的职责.为了更好地细分视图(View)与模型(Model)的功能,让View专注于处理数据的可视化以及与用户的交互.同一 ...
- SVN的项目管理
基于SVN的项目管理——集中与分散 我们在此处不讨论 GIT 比 SVN 好多少,也不讨论 Maven 和 Gradle 哪个好用,基于现有的开发环境,大多数公司还是采用 SVN + Maven ...
- navicat for mysql 如何将表ID排序重1开始?
如果是自增字段,删除数据,他是根据原来的继续往后排的 1.你可以删除这个字段,重新建立个自增字段就可以了 2.好像也可以重新设置排序起始 alter table table_name AUTO_INC ...
- 文件比较,文件夹比较-- vimdiff,beyond compare, compare suite, WinMerge,Kdiff3
文件比较,文件夹比较-- vimdiff,beyond compare, compare suite, WinMerge,Kdiff3 有一个项目的源码包需要比较,400M以上,这就要找个好的工具了 ...
- SQLite中如何用api操作BLOB类型的字段
在实际的编程开发当中我们经常要处理一些大容量二进制数据的存储,如图片或者音乐等等.对于这些二进制数据(blob字段)我们不能像处理普通的文本那样简单的插入或者查询,为此SQLite提供了一组函数来处理 ...