尝试理解 Delphi 接口

8

我正在阅读Nick Hodges的书籍“Coding in Delphi”,并尝试理解接口的使用。在一个单元中,我放置了一个简单的接口:

unit INameInterface;

interface

type
  IName = interface
  ['{CE5E1B61-6F44-472B-AE9E-54FF1CAE0D70}']
    function FirstName: string;
    function LastName: string;
  end;

implementation

end.

在另一个单元中,我已经按照书本示例实现了这个接口:

unit INameImplementation;

interface

uses
  INameInterface;

type
  TPerson = class(TInterfacedObject, IName)
    protected
      function FirstName: string;
      function LastName: string;
  end;

implementation

{ TPerson }

function TPerson.FirstName: string;
begin
  Result := 'Fred';
end;

function TPerson.LastName: string;
begin
  Result := 'Flinstone';
end;

end.

目前我已经创建了一个简单的VCL表单应用程序,以便使用我创建的对象。表单代码如下:

unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  Vcl.StdCtrls, INameImplementation;

type
  TfrmMain = class(TForm)
    lblFirtName: TLabel;
    lblLastName: TLabel;
    txtFirstName: TStaticText;
    txtLastName: TStaticText;
    btnGetName: TButton;
    procedure btnGetNameClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    Person: TPerson;
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  txtFirstName.Caption := '';
  txtLastName.Caption := '';
end;

procedure TfrmMain.btnGetNameClick(Sender: TObject);
begin
  txtFirstName.Caption := ...
end;

end.

我的问题是:我该如何使用接口?这两个函数被声明为受保护的,那么我该如何从表单中访问它们?我需要将它们定义为公共的吗,还是应该使用INameInterface接口单元?我对接口感到非常困惑!!!
Eros

1
接口使用的一个很好的例子是......假设您有一个包含对象的DLL,您希望从调用进程中使用该对象。您无法跨DLL边界传递对象,但可以传递接口(只要其成员也是安全的,例如WideString而不是String)。然后,在DLL内部实例化对象,并将该对象的接口引用从DLL传递回调用应用程序 - 然后调用应用程序可以以与直接调用对象相同的方式与该接口交互。 - Jerry Dodge
@Jerry 这更多是二进制互操作的实现问题。我认为接口是一个更深层次的概念。 - David Heffernan
1个回答

9

基本上,除了你已经展示出理解的内容之外,还有三件事情需要你了解。

1. 如何调用接口方法

如果你有一个接口的引用,那么你可以像调用类引用一样调用方法:

var
  Name: IName;
....
Writeln(Name.FirstName);
Writeln(Name.LastName);

2. 如何获取接口引用

通常你需要实例化一个实现所需接口的类来获取接口引用:

var
  Name: IName;
....
Name := TPerson.Create;
// now you can use Name as before

有其他获取接口引用的方法,但现在让我们暂且不谈。

3. 如何传递接口

您可能不希望每次使用接口时都创建一个新对象。因此,您可以让其他方将要使用的接口传递给您。例如,接口可以作为方法参数传递:

procedure Foo(Name: IName);
begin
  // use Name as before
end;

您可以通过函数调用和属性等方式获得接口引用。

这两个函数被声明为protected,我怎样才能从表单中访问它们?

好吧,它们在实现对象中被声明为protected。但是您不会通过实现对象来访问它们。您将通过接口来访问它们,这意味着实现对象中的可见性对于接口的视角来说并不相关。

表单单元引用了INameImplementation,这是创建实现接口的对象所需的。您还需要使用INameInterface,以便您的代码可以看到接口本身。

这个例子还不是很强大,因为您仍然可以看到实现对象的类型。但想象一下,如果您看不到它们,并且只能看到一个返回IName的函数,那么当您达到这个点时,接口就可以发挥其潜力。


非常感谢,David。 还有一个问题:我需要在主单元的uses子句中包含INameInterface和INameImplementation吗?这是这种对象的正常用法吗? - Eros
关于接口的内容远不止这些,但我尽量坚持基础知识。我相信尼克的书会讲得更多。 - David Heffernan
@Eros:所有接口使用者应该只引用接口单元。依赖注入是这种编码风格的天然选择。 - whosrdaddy
我认为我们的 OP 还没有准备好进行那种程度的脑力体操。 - Warren P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接