我的重构经历:编写代码生成器
概述
背景
多年前,我开发了一个基于C#的Windows程序——代码生成器,并在此后十多年间持续优化。该程序能够根据数据库表结构生成代码,并将结果显示在文本框中。最初是从同事那里接手的一个简单项目,经过不断扩展和重构,最终实现了通过数据库自动生成具备完整增删改查功能的网站。
数据库结构读取的SQL语句
- MySQL
- 获取表的列信息:
SELECT Table_Name AS TableName, Column_Name AS ColumnName, Data_Type, Column_Default, Is_Nullable, Column_Comment, CHARACTER_MAXIMUM_LENGTH, COLUMN_KEY, EXTRA, NUMERIC_PRECISION, NUMERIC_SCALE FROM COLUMNS WHERE TABLE_SCHEMA='{0}'
- 获取外键信息:
SELECT Constraint_Name AS FKName, Table_Name AS TableName, Column_Name AS ColumnName, Referenced_Column_Name AS RColumnName, Referenced_table_Name AS ParentTableName FROM Key_Column_USAGE WHERE Constraint_schema='{0}' AND Referenced_table_Name IS NOT NULL
- 获取表名称信息:
SELECT table_Name AS TableName, table_Comment AS script, 'U' AS tableType FROM tables WHERE TABLE_SCHEMA='{0}' UNION SELECT table_Name AS TableName, table_Name AS script, 'V' AS tableType FROM views WHERE TABLE_SCHEMA='{0}'
效果
- 支持MySQL、SQL Server、Oracle等多种数据库。
- 自动生成具备增删改查功能的网站,无需额外修改即可运行。
- 在MyBatis流行之前,已实现类似功能的框架。
编写的过程也是重构的过程
从缩减代码开始
代码生成器最初通过拼接字符串实现,代码量大且重复较多。我的重构从缩减代码开始,将相似代码抽离成通用函数,符合《程序员修炼之道》中的DRY原则(Don’t Repeat Yourself)。
将数据与展示分离
随着代码量的增加,我意识到代码与数据库结构有天然关联,于是将数据与展示分离,将通用语句封装到数据类中,形成函数。这一改动大幅缩减了代码量,并提升了逻辑清晰度。
代码生成器的思考
最初的代码生成器仅生成简单的业务对象和逻辑代码。随着改进,逐渐增加了数据交互、SQL语句和前台页面代码的生成功能。
代码生成器与框架
在编写过程中,我发现需要引入框架来进一步缩减代码量。于是,我开发了一个C#数据库访问框架,将通用功能抽象到父类中,仅生成子类代码。
重构与读书
在重构过程中,我通过阅读《C# Primer Plus》等书籍,不断学习新知识并应用到代码生成器中。这种边学边实践的方式让我对面向对象编程有了更深的理解。
重构与组件
随着前台页面代码生成的引入,代码量大幅增加。为了减少重复代码,我开始编写和扩展ASP.NET组件,例如扩展表格组件以支持排序、翻页等功能。
重构与通用功能(小的框架)
为了简化前台页面的数据验证逻辑,我扩展了页面控件并编写了一个JavaScript小框架,统一处理数据验证。
重构与面向对象
在重构过程中,我曾过度使用继承和多态,导致代码复杂化。后来意识到错误,调整了设计思路,避免不必要的继承。
使用模板
随着代码生成器中重复代码的增加,我引入了模板技术,使用模板语言(如vtemplate)进一步简化代码生成逻辑。
重构与新技术
在编写代码生成器的过程中,我不断学习新技术,例如定制特性和模板语言。某些设计与后来的MyBatis框架不谋而合。
重构与开源项目
开源项目DotNetNuke对我的代码生成器开发有很大帮助。我借鉴了其中的CBO类,并学习了策略模式等设计模式。
重构与功能扩展
随着代码生成器的不断重构,功能也逐渐扩展,支持更多场景和需求。
重构与框架演进
配套的框架从最初的三层架构逐步演进,最终将功能拆分为独立组件,提升了灵活性和可维护性。
其他开发语言实现
为了加深对新语言的理解,我用Python重新实现了代码生成器的核心部分,并用Java实现了框架组件。
思考
重构的理解
重构是整理代码的过程,旨在提升代码的可读性、可维护性和性能。通过重构,我逐渐理解了设计模式的本质,并意识到模式并非固定不变,而是根据需求自然产生的。
我的重构步骤
- 注释检查:确保注释清晰、充分且与代码一致。
- 命名检查:确保类、函数和变量命名准确且风格一致。
- 方法复杂度检查:拆分过长或过于复杂的方法。
- 相似类检查:抽象出基类和公共函数,减少重复代码。
- 类成员可见性检查:合理设置成员的访问权限。
- 类关系检查:进一步抽象类之间的关系,提升代码结构。
- 代码评审:请他人阅读代码并提供改进建议。
重构与单元测试
重构应从设计接口时开始。通过编写测试用例,可以发现接口设计中的问题,例如功能不单一或依赖外部条件。
重构的风险
重构可能带来风险,例如代码无法编译或功能失效。因此,建议使用版本控制工具,并频繁进行编译和测试。
重构的原则和方法
- DRY原则:避免重复代码。
- 正交性:确保代码模块之间的独立性。
- 隐藏秘密:封装实现细节,降低耦合度。
- 源代码即文档:通过清晰的代码结构和命名提升可读性。
- 《代码大全2》中的建议:强调代码的可读性和可维护性。