你被绑定到你的应用程序设计的架构。不要试图与之抗争。让数据感知控件做他们擅长的事情,即数据同步。如果您的控件已经使用 dfm 绑定到其数据源,则应该没有问题。
您需要重构的是您附加到控件的任何事件处理程序。我建议你看看监督控制器模式。我找到了以下示例实现:
虽然在 Delphi 中有一些 UI 架构模式的例子,但那些面向桌面应用程序的往往是关于被动视图而不是监督控制器。所以这是我的看法。
您首先要为应用程序中的每个表单定义至少一个界面。我说至少一个,因为有些形式很复杂,可能需要分解成多个接口。
IProductView = interface
end;
然后让你的表单实现它。
TProductForm = class(TForm, IProductView)
...
end;
接下来,您需要一个演示者/控制器。这将处理除数据同步之外的所有内容。
TProductPresenter = class
private
FView: IProductView;
public
constructor Create(AView:IProductView);
end;
在表单类中创建一个私有字段,并在创建/释放表单时创建/释放演示者。无论您使用表单的构造函数/析构函数还是 onCreate/onDestroy 事件都无关紧要。
TProductForm = class(TForm, IProductView)
private
FPresenter: TProductPresenter;
public
constructor Create;
...
end;
implementation
TProductForm.Create
begin
FPresenter := TProductPresenter.Create(self);
end;
现在,当您需要表单或其控件之一来响应事件时,将责任委托给演示者。假设您需要检查产品名称是否使用了正确的大小写。
TProductForm.NameDBEditChange(Sender: TObject);
begin
FPresenter.ValidateName;
end;
与其将控件或其文本属性作为参数传递,不如将数据作为接口上的属性公开......
IProductView = interface
function GetName:string;
procedure SetName(Value: string);
property Name: string read GetName write SetName;
...并在表格上实施GetName
和。SetName
TProductForm.GetName: string;
begin
Result := NameDBEdit.Text;
end;
TProductForm.SetName(Value: string);
begin
NameDBEdit.Text := Value;
end;
以尽可能简单的形式公开数据很重要。您不希望演示者依赖于存储在 TDBEdit 中的产品名称。演示者应该只看到您明确允许它通过界面看到的内容。这样做的主要好处是您可以根据需要修改表单(或完全替换它),只要它符合界面,就不需要对演示者进行任何更改。
现在您的所有业务逻辑都已移至您的演示者,它将类似于一个上帝类。您的下一步将是将该逻辑重构为按职责分解的适当类。当您达到这一点时,您将处于一个更好的位置来尝试重新设计架构(如果您仍在考虑它)。
“哇!看起来工作量很大!” 你可能会说。你是对的(但你知道在开始之前会做很多工作)。它不必一次完成。这些步骤都没有改变逻辑发生的地方的行为。
好处
- UI现在很容易修改
- 业务逻辑可以更容易地单独测试
- 可以增量实施
缺点
- 一开始需要做更多的工作,尽管这最终会被更多可维护的代码所抵消。
- 不适合所有应用。对于小型项目,额外的基础设施可能不值得付出努力。
其他参考