简体   繁体   English

Delphi重构示例,涉及数据感知控件和可直接访问db表的数据模块

[英]Example of Delphi refactoring involving data aware controls and datamodules with direct access to db tables

I am trying to define the best way to refactor the project I am working on. 我正在尝试定义重构我正在进行的项目的最佳方法。

Due to lack of good design almost all project is made up of: 由于缺乏良好的设计,几乎所有项目都由以下部分组成:

1) forms containing business logic 1)包含业务逻辑的表单

2) huge datamodules (1 per form + some extra ones) 2)巨大的数据模块(每个表单1个+额外的一些)

3) some units that contain common code (libraries) 3)包含公共代码(库)的一些单元

There is no OOP (except for some small areas), code reuse it is at a minimum level. 没有OOP(除了一些小区域),代码重用它是最低级别的。

One problem is also that dataaware controls are used, so it was very simple to drop many datasets+datasources on the datamodules and link directly to the DB in an highly coupled manner. 一个问题是使用了数据感知控件,因此在数据模块上删除许多数据集+数据源并以高度耦合的方式直接链接到数据库非常简单。

Ideally i would like to extract classes, like TCustomer, TEmployee, to get advantage os encapsulation and to make it possible to create new UI in the future without duplicating all code. 理想情况下,我想提取类,如TCustomer,TEmployee,以获得os封装的优势,并且可以在将来创建新的UI而无需复制所有代码。

Anyway my question is: how do I can keep dealing with dataaware controls? 无论如何,我的问题是:如何继续处理数据控件? Should I implement a function that returns a dataset, and I link the dataawarecomponent.datasource to the function result? 我应该实现一个返回数据集的函数,并将dataawarecomponent.datasource链接到函数结果吗?

function TCustomer.LoadByID(aCustomerID: integer): TDataset

?

You are bound to the architecture your application was designed around. 您将受到应用程序设计的体系结构的约束。 Don't try to fight against it. 不要试图反对它。 Let the data aware controls do what they are good at, data synchronization. 让数据感知控件做他们擅长的事情,数据同步。 If your controls are already bound to their data sources using the dfm there shouldn't be a problem. 如果您的控件已使用dfm绑定到其数据源,则应该没有问题。

What you do need to refactor is any event handlers you have attached to your controls. 您需要重构的是您附加到控件的任何事件处理程序。 I suggest you take a look at the Supervising Controller pattern. 我建议你看看监督控制器模式。 I've found example implementations for: 我找到了以下示例实现:

While there are a few examples of UI architectural patterns in Delphi those that are geared toward desktop applications tend to be about the Passive View rather than Supervising Controller. 虽然在Delphi中有一些UI架构模式的例子,那些面向桌面应用程序的例子往往是被动视图而不是监督控制器。 So here is my take on it. 所以这是我的看法。

You'll want to start with defining at least one interface for each form in your application. 您将首先为应用程序中的每个表单定义至少一个接口。 I say at least one because some forms are complex and may need to be broken into multiple interfaces. 我说至少有一个因为某些形式很复杂,可能需要分解成多个接口。

IProductView = interface
end;

Then have your form implement it. 然后让你的表单实现它。

TProductForm = class(TForm, IProductView)
...
end;

Next you'll need a presenter/controller. 接下来你需要一个演示者/控制器。 This will handle everything except data synchronization. 这将处理除数据同步之外的所有内容

TProductPresenter = class
private
  FView: IProductView;
public
  constructor Create(AView:IProductView);
end;

Create an private field in your form class and create/free the presenter when the form is created/freed. 在表单类中创建一个私有字段,并在创建/释放表单时创建/释放演示者。 Whether you use the form's constructor/destructor or the onCreate/onDestroy events doesn't matter much. 无论您使用表单的构造函数/析构函数还是onCreate / onDestroy事件都无关紧要。

TProductForm = class(TForm, IProductView)
private
  FPresenter: TProductPresenter;
public
  constructor Create;
...
end;

implementation
TProductForm.Create
begin
  FPresenter := TProductPresenter.Create(self);
end;

Now when you need the form or one of its controls to respond to an event delegate responsibility to the presenter. 现在,当您需要表单或其中一个控件来响应事件委托给演示者的责任时。 Lets assume you need to check that the product name uses proper capitalization. 让我们假设您需要检查产品名称是否使用了适当的大小写。

TProductForm.NameDBEditChange(Sender: TObject);
begin
  FPresenter.ValidateName;
end;

Rather than pass the control or its text property as an argument you expose the data as a property on the interface... 而不是将控件或其text属性作为参数传递,而是将数据公开为接口上的属性...

IProductView = interface
  function GetName:string;
  procedure SetName(Value: string);
  property Name: string read GetName write SetName;

...and implement GetName and SetName on the form. ...并在表单上实现GetNameSetName

TProductForm.GetName: string;
begin
  Result := NameDBEdit.Text;
end;

TProductForm.SetName(Value: string);
begin
  NameDBEdit.Text := Value;
end;

Its important to expose the data in the simplest form possible. 以最简单的形式公开数据非常重要。 You don't want the presenter depending on the product name being stored in a TDBEdit. 您不希望演示者取决于存储在TDBEdit中的产品名称。 The presenter should only see what you explicitly allow it to see through the interface. 演示者应该只看到您明确允许它通过界面查看的内容。 The main benefit of this is you can modify the form as much as you want(or replace it entirely) and as long as it adheres to the interface no changes will need to be made to the presenter. 这样做的主要好处是您可以根据需要修改表单(或完全替换它),只要它符合界面,就不需要对演示者进行任何更改。

Now that all your business logic has been moved to your presenter it will resemble a god class. 现在您的所有业务逻辑都已移至您的演示者,它将类似于神级。 Your next step will be to refactor that logic into appropriate classes broken up by responsibility. 您的下一步将是将该逻辑重构为由责任分解的适当类。 When you reach this point you're in a much better position to attempt an architectural redesign (if your still considering it). 当你达到这一点时,你可以更好地尝试进行架构重新设计(如果你还在考虑它)。

"Wow! That looks like a lot of work!" “哇!看起来很多工作!” you might say. 你可能会说。 You'd be right (but then you knew it would be a lot of work before you got started). 你是对的(但是在你开始之前你知道这将是很多工作)。 It doesn't have to be done all at once. 它不必一次完成。 None of these steps is changing the behavior of the logic just where it takes place. 这些步骤都没有改变逻辑行为的位置。

Advantages 好处

  • UI is now easy to modify UI现在很容易修改
  • Business logic can more easily be tested in isolation 业务逻辑可以更容易地单独测试
  • Can be implemented incrementally 可以逐步实施

Disadvantages 缺点

  • It is more work at first though this is eventually offset by more maintainable code later on. 一开始它的工作量更大,但最终会被更易于维护的代码所抵消。
  • Not suitable for all applications. 不适合所有应用。 For small projects the additional infrastructure may not be worth the effort. 对于小型项目,额外的基础设施可能不值得。

Other references 其他参考

If there's no good design and no real OOP in the code then considering it's complexity you should first start by creating a design describing it's current functionality. 如果代码中没有好的设计并且没有真正的OOP,那么考虑到它的复杂性,首先应该首先创建一个描述它当前功能的设计。 Yes, that means you'll be busy writing a lot of documentation at first. 是的,这意味着你最初会忙着写很多文档。 But it allows you to split up the whole project into logical/functional parts which you could use to focus on once this documentation is finished. 但它允许您将整个项目拆分为逻辑/功能部分,您可以在完成此文档后将其用于关注。 You could then refactor each part separately, or possibly rewrite those parts even. 然后,您可以单独重构每个部分,或者甚至可以重写这些部分。
Projects this complex aren't always practical to refactor. 这个复杂的项目并不总是可行的重构。 You should return to the original design (thus create it since you have none) and then look at your code and consider what is faster: refactoring or rewriting... 您应该返回原始设计(因此创建它,因为您没有),然后查看您的代码并考虑更快的内容:重构或重写...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM