2012-06-01 32 views
4

当我们在Delphi中设计一个类时,通常我们有私有字段(成员),私有setter和getter方法以及公共属性。从课外,只有公共财产才能访问这些数据;该类的用户甚至不知道存在getter方法。它是否违反了接口属性访问器的公共封装?

所以getter和setter方法封装的实例成员和财产封装了getter和setter方法。

然而,定义一个接口,当我们揭露那些方法:

ICounter = interface 
    // I wouldn't want to specify these 2 methods in the interface, but I'm forced to 
    function GetCount: Integer; 
    procedure SetCount(Value: Integer); 

    property Count: Integer read GetCount write SetCount; 
end; 

实施具体类:

TCounter = class(TInterfacedObject, ICounter) 
private 
    function GetCount: Integer; 
    procedure SetCount(Value: Integer); 
public 
    property Count: Integer read GetCount write SetCount; 
end 

使用它:

var 
    Counter: ICounter; 
begin 
    Counter := TCounter.Create; 
    Counter.Count := 0; // Ok, that's my public property 

    // The access should me made by the property, not by these methods 
    Counter.SetCount(Counter.GetCount + 1); 
end; 

如果属性封装getter/setter私有方法,是不是违规? getter和setter是具体类的内部,并且不会被暴露。

+1

这听起来像一个咆哮。你的问题是什么? –

+1

哪一点让你感到困惑?这对我来说很有意义。 –

+1

-1在您已经有三个答案后完全改变问题的性质。我反对你的发言,我的修改“改变了方向”。我的编辑把你的两个问题放在标题中,这样标题就成了一个恰当的问题。 *你的*是改变方向的编辑。 –

回答

8

方法是与界面交互的方式。接口上的属性是一个特定于Delphi的扩展;他们只是为基础方法提供语法糖。没有其他语言由于接口中的方法根据定义是公开的,它们不会被属性封装。你不是通过表明了属性,因为在接口中的方法支持透露任何实现细节,性能总是的方法支持,和方法总是公众。封装不能违反,如果它从来没有出现在第一位。

你举的例子具体类是一种误导。首先,在那里定义的属性与界面中定义的属性完全没有连接。您可以将其定义为只读,使其可以直接访问数据成员,使其成为私有的,或以任何其他方式与接口版本不同,包括完全删除它,并且不会影响接口的用户,进一步借用相信这是在接口中重要的方法,而不是属性。编译器将接口属性的任何使用直接转换为使用已公开的相应接口方法之一。在这个问题上从来没有咨询过实施的对象。

二,关于类的可见性说明符是无关紧要的。因为它们已经在接口上公开,所以不需要使这些方法保密。但是,将它们设置为私有并不是一个坏主意,因为它鼓励通过接口正确使用该类。

你可以抱怨的接口存取方法应该可以是私有的,但是这一样要求一般接口方法,以便能够为私有,这是没有意义的。明显不能调用的方法不是接口的一部分。回想一下,任何支持COM的语言都可以使用接口,即使是没有属性概念的接口,比如C和C++。这些语言也需要能够调用访问器方法。如果这些方法在某种程度上是私人的,那么界面就不能用这些语言。


当一个Delphi类的属性是指一个领域,这个细节实际上是类的面向公众的接口的一部分。任何使用该属性的代码都知道该属性只是该字段的别名(即使代码的作者不知道)。如果您更改属性定义,那么使用该类的任何代码都需要重新编译,以便编译器可以生成用于访问该属性的新代码。

当属性需要被方法支持时,你不能再真正改变属性定义。只有实现可以改变,所以没有接口的消费者需要重新编译,因为你选择按需计算属性而不是从存储字段读取。

+1

“如果更改属性定义,则需要重新编译使用该类的任何代码,以便编译器可以生成用于访问该属性的新代码”。那么说,这就是为什么我认为总是使用方法访问器定义属性而不是使用字段(甚至是简单的值属性)的好习惯,因为将来不需要重新定义属性。 – jfoliveira

+1

是的,对于与界面属性无关的具体属性你是绝对正确的,这确实是误导。无论如何,这看起来有点矛盾。 “德尔福方式”:保持你的getters/setters私有并只发布属性。比良好的面向对象操作说“程序接口,而不是具体的类”。所以,你无法保护你的获得者和制定者。 –

+0

但是有了接口,没有什么可以保护它们*。它们不仅仅是接口中的实现*细节*。它们是如何使用课程的一个组成部分。属性(如果存在的话)只不过是界面上的事后考虑,因为它们不像方法那样普遍可用。 –

3

然而,接口强制使用的getter/setter方法:

由于接口没有一个实例,因此它不能存储数据。

除此之外,Getter和Setter方法通常是私有的。在界面上定义 使它们可以被界面的用户访问。它 造成一些混淆:

接口不指定成员的可见性。实现接口来定义哪些属性和方法是可见的以及他们是谁(受保护或公开或发布)取决于该类。

如果您将仅通过您自己的类/模块来使用您的接口,但在更改实现类中的可见性方面没有问题,但这不是一个好习惯。

现在,我该如何设置计数:Counter.Count:= 0或 Counter.SetCount(0)?这是不是打破封装?

您可以通过两种方式设置count属性。如果设置为:

Counter.Count := 0 

访问方法将以相同的方式调用。没有封装是休息。

编辑:

让我把一个例子来阐明,没有什么突破,但可以有不同的行为:

鉴于此实现您的ICounter接口:

TMyCounter = class(TInterfacedObject, ICounter) 
    private 
    FCount: Integer; 
    function GetCount: Integer; 
    procedure SetCount(Value: Integer); 
    public 
    property Count: Integer read GetCount write SetCount; 
    end; 

implementation 

function TMyCounter.GetCount: Integer; 
begin 
    Result := FCount; 
end; 

procedure TMyCounter.SetCount(Value: Integer); 
begin 
    FCount := Value; 
end; 

一些可能您的财产使用如下:

var 
    c: TMyCounter; 
    ic: ICounter; 
begin 
    c := TMyCounter.Create; 
    try 
    //c.SetCount(1); //won't compile, since the setter is private 
    ICounter(c).SetCount(1); //it is ok, because the interface method is public 
    c.Count := C.Count + 1; //it is ok, cause the SetCount acessor will be invoked 
    if Supports(c, ICounter, ic) then 
     ShowMessage(IntToStr(ic.GetCount)); 
    finally 
    c := nil; 
    end; 

因此,我们可以得出结论:

  • 类具有“访问说明”,这将访问类的类型时,总是尊重
    ;
  • 接口没有“访问说明符”,所有访问接口类型的方法都将被视为公共的;
+0

不是旨在替代getter和setter方法的接口吗?这就是为什么getter和setter被实现为私有并且只有属性被发布的原因。所有的VCL都是这样设计的,这是对班级用户说的一种方式:“嘿用户,只要使用这个属性,每当我使用getter方法时,都是我的业务,​​而不是你的”。当我说打破封装时,我的意思是接口暴露了实现内部。 –

+0

但它们显然不是内部的,因为它们是按照定义公开的。由于所有接口属性都有方法支持,因此您不会向用户透露任何秘密实现细节,因为某个特定属性由方法支持。 –