[转]解耦:Delphi下IoC 模式的实现

解耦:Delphi下IoC 模式的实现

摘要:在Delphi下IoC模式的多种实现

Ioc英文为 Inversion of Control,即反转模式,这里有著名的好莱坞理论:你呆着别动,到时我会找你。Ioc模式是解决调用者和被调用者之间关系的模式,可以有效降低软件的耦合度,并适合团队开发,使用这种模式需要首先设计一个好的框架,也可以称之为IoC容器

(可能这样的说法在Java世界更Cool J)。其实Windows内部就存在这样的模式,称之为

Callback(回调),在Delphi 的源代码中也有很多这样的方式。看一个例子:

       可以打开下面两个文件

       $(Delphi)\Source\Vcl\DB.pas

       $(Delphi)\Source\Vcl\DBLogDlg.pas

       第二个文件是一个需要用户填写登录用户名和密码的对话框,显然这属于用户交互层,

我们不希望在DB.pas(数据逻辑层)中直接调用这个功能,Delphi是这样实现的:

       DB.pas #2282 有这样的声明:

  LoginDialogProc: function (const ADatabaseName: string; var AUserName, APassword: string): Boolean;

       LoginDialogProc 被定义为函数类型变量。

       在DBLogDlg.pas #132

initialization

  if not Assigned(LoginDialogProc) then

    LoginDialogProc := LoginDialog;

       也就是在DBLogDlg.pas单元初始化阶段给DB.Pas中的LoginDialogProc赋予实现函数。

我觉得这个例子已经足够把问题说得清楚了。

当然这并非实现IoC的唯一方法,对于不同的语言都有自己的特性,上面的方法可能比较古老的一种,通过函数指针来实现,其他的方法还有

    一:封装为接口

              我们看到在DB.pas 中声明了很多类似的函数变量,我们可以将其封装为一个接口

       IDBDlogInterface = Interface

  ['{06B5DEAC-0BB1-4658-91F6-E78004DF131D}']

  LoginDialogProc: function (const ADatabaseName: string; var AUserName, APassword: string): Boolean;

  LoginDialogExProc: function (const ADatabaseName: string; var AUserName, APassword: string; NameReadOnly: Boolean): Boolean;

  RemoteLoginDialogProc: function (var AUserName, APassword: string): Boolean;

end;

然后申明一个全局的接口类型变量

Var

       DBDialog : IDBDialogInterface ;

       然后在其他单元实现这个接口,并初始化时赋值到这个接口变量。

       二:注册机制的IoC

       这个例子稍微复杂一些,但是似乎更实用

       我们在一个窗口上放置了一个pageControl,我们知道一个pageControl上可以放置多个pageControl,而且这多个 pageControl功能相对独立,没有耦合。我们可以这样做:建立这个主窗体放置一个空的PageControl,然后声明一个 TInterfaceList变量用来存储PageControl的具体实现。每一个PageControl动态创建,使用注册的Frame来填充

       代码如下:

unit frmStartPage;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs,  StdCtrls, ImgList,Contnrs, ComCtrls, ExtCtrls;

Type

  //填充pageControl的Frame基类

  TPageFrame = class(TFrame)

  protected

    function Caption :String ; virtual; abstract;

  end;

  TPageFrameClass = class of TPageFrame ;

  TStartPage = class(TForm)

    PageControl1: TPageControl;

    procedure FormCreate(Sender: TObject);

  private

    { Private declarations }

  public

    { Public declarations }

  end;

var

  StartPage :TStartPage ;

  RegTabSheetClasses: TClassList;

  procedure RegisterStartPageTabSheet( AFrameClass :TPageFrameClass);

implementation

{$R *.dfm}

procedure TStartPage.FormCreate(Sender: TObject);

var

  i:integer;

  lTabSheet : TTabSheet;

  lFrame : TPageFrame;

begin

  for i:= 0 to RegTabSheetClasses.Count -1 do

  begin

    lTabSheet := TTabSheet.Create(PageControl1);

    lFrame := TPageFrameClass( RegTabSheetClasses[i]).Create(self) as TPageFrame;

    lTabSheet.Caption := lFrame.Caption ;

    lFrame.Align := alClient;

    lTabSheet.InsertControl(lFrame);

    lTabSheet.PageControl := PageControl1;

    lTabSheet.PageIndex := 0 ;

  end;

end;

procedure RegisterStartPageTabSheet( AFrameClass :TPageFrameClass);

begin

  RegTabSheetClasses.Add(AFrameClass);

end;

initialization

  RegTabSheetClasses := TClassList.Create ;

finalization

  RegTabSheetClasses.Free;

end.

unit Unit2;

interface

uses

  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

  Dialogs,frmStartPage, StdCtrls;

type

  TFrame2 = class(TPageFrame)

    Label1: TLabel;

  private

    { Private declarations }

  protected

    { Public declarations }

    function Caption :String ; override;

  public

  end;

implementation

{$R *.dfm}

{ TFrame2 }

function TFrame2.Caption: String;

begin

  result := 'Frame1';

end;

initialization

  RegisterStartPageTabSheet(TFrame2);

end.

原文地址:https://www.cnblogs.com/moon25/p/1600879.html