2012-06-01 63 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

但它們顯然不是內部的,因爲它們是按照定義公開的。由於所有接口屬性都有方法支持,因此您不會向用戶透露任何祕密實現細節,因爲某個特定屬性由方法支持。 –