wad*_*des 11 delphi refactoring unit-testing datamodule
我正在寻找有关构建Delphi程序以实现可维护性的一些建议.虽然我第一次学习使用Turbo Pascal进行编程,但是经过几十年的大部分C/C++后我才开始使用Delphi编程,所以我对基本语言并不感到不舒服.在我之前使用C++和C#的经历中,我通过使用cxxtest和NUnit成为了TDD转换器.
我继承了这个程序,我现在负责维护.它主要由表单和几个数据模块组成.应用程序业务逻辑和数据访问主要分散在表单中,而数据模块大多只是全局ADO对象生存的地方.数据库访问通常通过引用TADOQuery或TADOCommand的全局实例,将SQL文本格式化为对象的相关属性,并调用其Open或Execute方法来完成.
我试图将业务逻辑纳入一定程度的封装,可以进行单元测试.我已经看到了这个答案,就形式抽象逻辑而言,这是完全合理的.我想知道数据访问的最佳实践是什么.我的想法是数据模块应该公开一种特定于应用程序的迷你API(可能包含所有虚拟方法),以便可以用模拟对象替换它们进行测试.另一个答案的链接显示了一些让我相信自己走上正轨的例子,但我仍然有兴趣看到一些关于数据模块的最佳实践文档.我通过Google找到的大多数页面都提供了相同类型的例子,说明了你可以在设计时做的所有很酷的事情,将数据绑定控件连接到查询等等,我对此并不感兴趣.在这一刻.
就个人而言,我不是TDataModule的粉丝.它很少鼓励良好的OO设计原则.如果它所使用的只是一个方便的数据库组件容器,这将是一件事,但它往往成为业务逻辑的倾销场,在域层中会更好.当这种情况发生时,它会变成神级和依赖磁铁.
除此之外,至少Delphi 2会导致表单的数据感知控件丢失其数据源(如果这些数据源位于窗体之前未打开的单元中),那么这个错误(或者它的一个特性)会继续存在.
我的建议
如果你不熟悉它,这种技术被称为域驱动设计.它当然不是唯一的解决方案,但它是一个好的解决方案 基本前提是UI,业务逻辑和数据库以不同的速率和不同的原因发生变化.因此,将业务逻辑作为问题域的模型,并将其与UI和数据库分开.
这是如何使我的代码更可测试的?
通过将业务逻辑移动到自己的层,您可以在不受UI或数据库干扰的情况下对其进行测试.这并不意味着您的代码本身就是可测试的,因为您将其放在自己的层中.使遗留代码可测试是一项艰巨的任务.大多数遗留代码紧密耦合,因此您将花费大量时间将其分成具有明确定义职责的类.
这是德尔福风格吗?
这取决于你的观点.传统上,大多数Delphi应用程序是通过串联开发UI和数据库来创建的.在窗体设计器上删除一些db感知控件.添加/更新包含字段的表以存储控件的数据.使用事件处理程序,使用大量的业务逻辑.中提琴!你刚刚烘焙了一个应用程序 对于非常小的应用程序,这是一个很好的节省时间.但是,不要自欺欺人,小应用程序往往变成大型应用程序,这种设计成为不可持续的维护噩梦.
这真的不是语言的错.您可以从数百个VB,C#和Java商店中找到相同的快速/脏/短视设计.这些类型的应用程序是新手开发人员的结果,他们不了解更好(和经验丰富的开发人员应该更好地了解),这样一个IDE可以很容易地完成工作并且很快就能完成工作.
Delphi社区中的那些(与其他社区一样)长期以来一直倡导更好的设计技术.
War*_* P 7
我认为您需要(事实上,大多数delphi数据库开发人员将需要)Mock数据集(查询,表等)组件,您可以使用它,并在模块初始化时替换它们,用于当前的ADO数据集对象到此模拟数据集,用于测试目的.而不是强迫接口进入你的设计,这是提供替代能力的一种方式,考虑通过Liskov替换原则,你应该能够(在测试夹具设置时),注入你的数据模块,模拟集 - 要使用的数据集,只需在测试执行时将一些ADO数据集替换为其他功能等效的实体(模拟数据集或文件支持的表数据集).
也许您甚至可以从数据模块中完全删除数据集,并将它们在运行时(在主应用程序中)连接到正确的ADO数据集对象,并在单元测试中附加模拟数据集.
由于您没有编写ADO数据集,因此无需对其进行单元测试.但是,模拟这样的数据集可能很困难.
我建议你考虑使用JvCsvDataSet或ClientDataSet作为fixture(模拟)数据集的基础.然后,您可以使用这些来确保将所有数据库平台依赖项(编写远程过程或数据库SQL的内容)抽象到其他类中,这将再次模拟.这样的努力可能不仅需要使您的业务逻辑单元可测试,它还可能是在您的业务逻辑中成为多数据库平台友好的一步.
假设您有一个名为CustomerQuery的ADOQuery,将您放到数据模块上的对象重命名为CustomerQueryImpl,并将其添加到您的数据模块类声明中:
private
FCustomerQuery:TADOQuery;
published
property CustomerQuery:TADOQuery read FCustomerQuery write FCustomerQuery;
Run Code Online (Sandbox Code Playgroud)
然后在create事件的数据模块中,将属性连接到对象:
FCustomerQuery := CustomerQueryImpl
Run Code Online (Sandbox Code Playgroud)
现在您可以编写单元测试,它将在运行时"挂钩"并用自己的测试夹具(模拟对象)替换CustomerQuery.
| 归档时间: |
|
| 查看次数: |
2403 次 |
| 最近记录: |