2010-08-27 84 views
4

在我當前的項目中,我需要能夠同時具有可編輯和只讀版本的類。因此,當類在List或PropertGrid中顯示時,用戶不能編輯它們不應該被允許的對象。在複雜對象結構中創建類的只讀版本

爲此,我遵循下圖所示的設計模式。我從一個只讀接口(IWidget)開始,然後創建一個實現此接口的可編輯類(Widget)。接下來,我創建一個只讀類(ReadOnlyWidget),它只是包裝可變類並實現只讀接口。

我遵循這種模式的一些不同的無關的類型。但是現在我想爲我的程序添加一個搜索函數,它可以生成包括各種類型(包括可變和不可變版本)的結果。所以現在我想添加另一組接口(IItem,IMutableItem),它們定義適用於所有類型的屬性。因此IItem定義了一組通用的不可變屬性,並且IMutableItem定義了相同的屬性但可編輯。最後,搜索將返回一個IItems的集合,然後可以根據需要將其轉換爲更具體的類型。

但是,我不確定是否正確建立了與IMutableIItem的關係。現在我有從IItem繼承的每個接口(IWidget,IDooHickey),然後可變類(Widget,DooHickey)此外還實現了IMutableItem

另外,我也在想我可以設置IMutableItemIItem繼承,這將隱藏它的只讀屬性,它具有get和set訪問器的新屬性。然後可變類將實現IMutableItem,只讀類將實現IItem

我希望對此有任何建議或批評。

類圖

alt text

代碼

public interface IItem 
{ 
    string ItemName { get; } 
} 

public interface IMutableItem 
{ 
    string ItemName { get; set; } 
} 

public interface IWidget:IItem 
{ 
    void Wiggle(); 
} 

public abstract class Widget : IWidget, IMutableItem 
{ 
    public string ItemName 
    { 
     get; 
     set; 
    } 

    public void Wiggle() 
    { 
     //wiggle a little 
    } 
} 

public class ReadOnlyWidget : IWidget 
{ 
    private Widget _widget; 
    public ReadOnlyWidget(Widget widget) 
    { 
     this._widget = widget; 
    } 

    public void Wiggle() 
    { 
     _widget.Wiggle(); 
    } 

    public string ItemName 
    { 
     get {return _widget.ItemName; } 
    } 
} 

public interface IDoohickey:IItem 
{ 
    void DoSomthing(); 
} 


public abstract class Doohickey : IDoohickey, IMutableItem 
{ 
    public void DoSomthing() 
    { 
     //work it, work it 
    } 

    public string ItemName 
    { 
     get; 
     set; 
    } 
} 

public class ReadOnlyDoohickey : IDoohickey 
{ 
    private Doohickey _doohicky; 
    public ReadOnlyDoohickey(Doohickey doohicky) 
    { 
     this._doohicky = doohicky; 
    } 

    public string ItemName 
    { 
     get { return _doohicky.ItemName; } 
    } 

    public void DoSomthing() 
    { 
     this._doohicky.DoSomthing(); 
    } 
} 
+0

我已經(從繼承的iItem和IMutableItem使用get添加有一個屬性和設置)在我的項目之一,它工作得很好。您也可以考慮將像SetXXX這樣的方法添加到IMutableItem中,而不是使用setter的新屬性(請參閱http://stackoverflow.com/questions/623824/add-set-to-properties-of-interface-in-c-sharp)。 – ttil 2012-01-16 23:37:40

回答

1

那是正確的,當你需要一個只讀副本創建另一個對象?如果是這樣,那麼你可以使用包含的代碼中的技術。如果不是這樣,我認爲封面可能是你最好的選擇。

internal class Test 
{ 
    private int _id; 
    public virtual int ID 
    { 
     get 
     { 
      return _id; 
     } 
     set 
     { 
      if (ReadOnly) 
      { 
       throw new InvalidOperationException("Cannot set properties on a readonly instance."); 
      } 
     } 
    } 

    private string _name; 
    public virtual string Name 
    { 
     get 
     { 
      return _name; 
     } 
     set 
     { 
      if (ReadOnly) 
      { 
       throw new InvalidOperationException("Cannot set properties on a readonly instance."); 
      } 
     } 
    } 

    public bool ReadOnly { get; private set; } 

    public Test(int id = -1, string name = null) 
     : this(id, name, false) 
    { } 

    private Test(int id, string name, bool readOnly) 
    { 
     ID = id; 
     Name = name; 
     ReadOnly = readOnly; 
    } 

    public Test AsReadOnly() 
    { 
     return new Test(ID, Name, true); 
    } 
} 
0

放棄希望所有進入這裏的人!!!

我懷疑從長遠來看,你的代碼會非常混亂。您的類圖表明所有屬性在給定對象中都可編輯(或不可編輯)。或者你的(我)可變接口引入了全新的屬性,這些屬性都是不可變的,與「核心」/繼承類是分開的?

無論哪種方式,我認爲你要與玩的屬性名稱的變化遊戲和/或隱藏繼承屬性

標記接口也許就結了?
考慮讓您的中的所有屬性都可變。然後實現IMutable(我不喜歡名字IItem)和IImutable作爲標記接口。也就是說,在界面主體中幾乎沒有定義。但是,它允許客戶端代碼將對象作爲IImutable參考進行處理,例如。 (a)您的客戶端代碼播放起來不錯,並且尊重它的可變性,或者(b)您的所有對象都被一個「控制器」類包裝,這個類會強制給定對象的可變性。

1

我會建議,對於每個主類或接口,有三個定義的類:「可讀」類,「可變」類和「不可變」類。只有「可變」或「不可變」類應該作爲具體類型存在;它們都應該來自一個抽象的「可讀」類。希望存儲對象安全的代碼應該存儲「不可變」的類;希望編輯對象的代碼應該使用「可更改」類。不會寫入某些內容但不關心它是否永遠保持相同值的代碼可以接受「可讀」基本類型的對象。

的可讀版本應包括公共抽象方法AsChangeable()AsImmutable(),公共虛擬方法AsNewChangeable(),和受保護的虛擬方法AsNewImmutable()。 「可更改」類應定義AsChangeable()以返回this,並且AsImmutable返回AsNewImmutable()。 「不可變」類應定義AsChangeable()以返回AsNewChangeable()AsImmutable()以返回this

所有這一切的最大困難是,如果嘗試使用類類型而不是接口,繼承不會非常好地工作。例如,如果想要從繼承的EnhancedCustomer類,那麼ImmutableEnhancedCustomer應該從ImmutableBasicCustomerReadableEnhancedCustomer繼承,但.net不允許這種雙重繼承。人們可以使用接口IImmutableEnhancedCustomer而不是類,但有些人會認爲'不可變的接口'有點味道,因爲沒有辦法定義一個接口的模塊,以這種方式外部人員可以使用它,而不會允許外界定義他們自己的實現。

0

可能爲時已晚:-),但原因是「關鍵字'new'是屬性所必需的,因爲它隱藏了屬性...」是Resharper中的一個錯誤,編譯器沒有問題。請參閱下面的示例:

public interface IEntityReadOnly 
{ 
    int Prop { get; } 
} 


public interface IEntity : IEntityReadOnly 
{ 
    int Prop { set; } 
} 

public class Entity : IEntity 
{ 
    public int Prop { get; set; } 
} 

[TestClass] 
public class UnitTest1 
{ 
    [TestMethod] 
    public void TestMethod1() 
    { 
     var entity = new Entity(); 
     (entity as IEntity).Prop = 2; 
     Assert.AreEqual(2, (entity as IEntityReadOnly).Prop); 
    } 
} 

與沒有接口的情況相同。唯一的限制,你可以使用你說的那樣一種替代的辦法不使用自動性能

public class User 
{ 
    public User(string userName) 
    { 
     this.userName = userName; 
    } 

    protected string userName; 
    public string UserName { get { return userName; } } 
} 

public class UserUpdatable : User 
{ 
    public UserUpdatable() 
     : base(null) 
    { 
    } 

    public string UserName { set { userName = value; } } 
} 

[TestClass] 
public class UnitTest1 
{ 
    [TestMethod] 
    public void TestMethod1() 
    { 
     var user = new UserUpdatable {UserName = "George"}; 
     Assert.AreEqual("George", (user as User).UserName); 
    } 
}