2012-12-20 42 views
9

tôi có lớp học đơn giản này:Decorator pattern cho các lớp học với nhiều tài sản

public class DataBag 
{ 
    public string UserControl { get; set; } 
    public string LoadMethod { get; set; } 
    public dynamic Params { get; set; } 
    public int Height { get; set; } 

    public DataBag(string Control, 
     object vars, string lm) 
    { 
     UserControl = Control; 
     LoadMethod = lm; 
     Params = vars; 
     Height = 0; 
    } 
} 

sau đó tôi muốn tạo một trang trí cho nó mà sẽ thêm một loạt các tính chất riêng của nó. Câu hỏi là cách ngắn gọn và thanh lịch nhất để cung cấp quyền truy cập vào các thuộc tính được trang trí là gì?

Cho đến nay tôi có hai lựa chọn: hoặc là tôi cung cấp một cặp get-set cho mỗi bốn thuộc tính trang trí trang trí (mà dường như tideous và mouthful và về cơ bản nó là những gì tôi muốn tránh) hoặc tôi kế thừa DataBag từ DynamicObject và sau đó bằng cách nào đó quản lý để có được các thuộc tính được trang trí bằng cách sử dụng phương thức TryGetMember (có tính động và dường như không đúng cách để thực hiện các công việc trong C#).

Bạn có lời khuyên nào không?

+0

thông thường, trang trí tất cả được kế thừa từ một giao diện chung. Trong trường hợp này, giao diện đó sẽ là gì? Nếu nó là năng động, thì có lẽ trang trí không phù hợp. –

+0

Bạn đã xem xét việc có trang trí * kế thừa * từ 'DataBag'? Vấn đề duy nhất là sau đó bạn không thể đúc từ trang trí xuống loại cơ sở. – McGarnagle

+0

@dbaseman Tôi đã sử dụng thừa kế lúc đầu nhưng rất sớm đã đến một tình huống mà rất nhiều lớp học với các thuộc tính hơi khác nhau và phương pháp là cần thiết. – Anton

Trả lời

7

Khi thực hiện trang trí tôi thường làm sau.Đầu tiên - giải nén giao diện của đối tượng trang trí và làm cho đối tượng trang trí thực hiện giao diện:

public interface IDataBag 
{ 
    string UserControl { get; set; } 
    string LoadMethod { get; set; } 
    dynamic Params { get; set; } 
    int Height { get; set; } 
} 

Tiếp - tạo ra một trang trí, mà đại biểu tất cả các cuộc gọi đến đối tượng trang trí (tất cả trang trí sẽ kế thừa từ trang trí này):

public class DataBagDecorator : IDataBag 
{ 
    private IDataBag _dataBag; 

    public DataBagDecorator(IDataBag dataBag) 
    { 
     _dataBag = dataBag; 
    } 

    public virtual string UserControl 
    { 
     get { return _dataBag.UserControl; } 
     set { _dataBag.UserControl = value; } 
    } 

    // other members 
} 

cuối - trang trí tạo:

public class FooDataBag : DataBagDecorator 
{ 
    public FooDataBag(IDataBag dataBag) 
     : base(dataBag) { } 

    public override string UserControl 
    { 
     // added behavior 
     get { return "Foo" + base.UserControl; } 
     set { base.UserControl = value; } 
    } 

    // you don't need to override other members 
} 

Cách sử dụng:

IDataBag dataBag = new FooDataBag(new DataBag()); 
1

Cập nhật: đúng @JeremyDWill chỉ ra rằng bạn không thể lấy được một kiểu generic từ một trong các thông số của nó ...

Nếu bạn nghĩ về trang trí như một "lớp con có thêm thuộc tính mới", bạn có thể làm một cái gì đó như:

public class MyDecorator<T> : T 
{ 
    public int MyDecoratorProperty1 { get; set; } 
    public int MyDecoratorProperty2 { get; set; } 
} 

Sau đó, bạn có thể tạo ra các trường hợp MyDecorator<DataBag>, và MyDecorator<OtherClass> vv các tính chất hiện có thể truy cập vì MyDecorator<> là cụ thể cho các loại lập luận chung chung, và xuất phát từ lớp đó.

Bạn có thể tạo một wrapper chứa các đối tượng trang trí:

public class MyDecorator<T> 
{ 
    public MyDecorator(T decoratedObject) 
    { 
     this.DecoratedObject = decoratedObject; 
    } 

    public T DecoratedObject { get; private set; } 

    public int MyDecoratorProperty1 { get; set; } 
    public int MyDecoratorProperty2 { get; set; } 
} 

Ưu điểm là nhận được các thuộc tính trang trí rất dễ dàng: myObj.MyDecoratorProperty1. Nhược điểm là bây giờ bạn phải đi qua các DecoratedObject thành viên để có được các đối tượng cơ sở:

DataBag bag = new DataBag("", null, null); 
MyDecorator<DataBag> deco = new MyDecorator<DataBag>(bag); 
deco.DecoratedObject.Height = 2; 

Nếu bạn không thể phân lớp từ decorate (bạn cần hỗ trợ nhiều trang trí cùng một lúc, nói), bạn sẽ phải làm một cái gì đó như một "thuộc tính đính kèm" ... lớp trang trí của bạn sẽ phải giữ một từ điển của các đối tượng ban đầu và các thuộc tính được trang trí. Với một vài phương pháp mở rộng, bạn có thể làm cho các đặc tính này "nhìn" như các thành viên bản địa của lớp trang trí miễn là bạn biết các loại việc trang trí trước (hoặc sẵn sàng để trang trí bất kỳ đối tượng):

public static class AttachedDecorator 
{ 
    private class Properties 
    { 
     public int MyDecoratorProperty1 { get; set; } 
     public int MyDecoratorProperty2 { get; set; } 
    } 

    private static Dictionary<object, Properties> map = new Dictionary<object, Properties>(); 

    public static int GetMyDecoratorProperty1(object obj) 
    { 
     Properties props; 
     if (map.TryGetValue(obj, out props)) 
     { 
      return props.MyDecoratorProperty1; 
     } 

     return -1; // or some value that makes sense if the object has no decorated property set 
    } 

    public static int GetMyDecoratorProperty2(object obj) { /* ... */ } 

    public static void SetMyDecoratorProperty1(object obj, int value) 
    { 
     Properties props; 
     if (!map.TryGetValue(obj, out props)) 
     { 
      props = new Properties(); 
      map.Add(obj, props); 
     } 

     props.MyDecoratorProperty1 = value; 

    } 

    public static void SetMyDecoratorProperty2(object obj, int value) { /* ... */ } 
} 

public static class DecoratorExtensions 
{ 
    private static int GetMyDecoratorProperty1(this object obj) 
    { 
     return AttachedDecorator.GetMyDecoratorProperty1(obj); 
    } 

    private static void SetMyDecoratorProperty1(this object obj, int value) 
    { 
     return AttachedDecorator.GetMyDecoratorProperty1(obj, value); 
    } 
    // ... 
} 

mã của bạn sau đó có thể trông giống như:

DataBag myData = new DataBag(); 
myData.SetMyDecoratorProperty1(7); 
Console.WriteLine("prop1: {0}", myData.GetMyDecoratorProperty1()); 
+1

Nó khác với kế thừa đơn giản 'MyDataBagDecorator: DataBag' như thế nào? Cũng thường nhiều trang trí được tạo ra cho một đối tượng trang trí, không phải một trang trí cho nhiều đối tượng thuộc các loại khác nhau. –

+0

@lazyberezovsky: Trang trí thường không cụ thể cho một lớp duy nhất. Nếu có, chúng chỉ là một phân lớp, không phải là một trang trí. Khi có nhiều trang trí, tất cả phụ thuộc vào ngữ cảnh, đó là lý do tại sao một đoạn bắt đầu "Nếu bạn không thể phân lớp từ trang trí ...". Như may mắn sẽ có nó, tôi đã viết lên "trang trí kèm theo" giải pháp của tôi cùng một lúc bạn thêm bình luận của bạn! – JaredReisinger

+0

Trong trường hợp này, người trang trí sẽ không có bất kỳ kiến ​​thức nào về các thành viên của T, vì vậy nó không thể truy cập chúng. Tôi nghĩ rằng giải pháp này là tốt nhất nhưng T nên bị buộc phải thực hiện một giao diện. –

1

Đây là cách đơn giản nhất để trang trí:

public class PrettyBag : DataBag 
{ 
    public int Decoration1 { get; set; } 
    public int Decoration2 { get; set; } 
} 

Nếu bạn muốn cr ăn một mặt tiền và ẩn một số thuộc tính DataBag thay vì chỉ thêm thuộc tính sau đó bạn có thể làm cho các thành viên của DataBag được bảo vệ.

Với giao diện bạn có thể làm:

public interface IDataBag 
    { 
     ... 
    } 
    public class DataBag : IDataBag 
    { 
     ... 
    } 
    public interface IPrettyBag : IDataBag 
    { 
     int Decoration1 { get; set; } 
     int Decoration2 { get; set; } 
    } 
    public class BigBag : IPrettyBag 
    { 
     public int Decoration1 { get; set; } 
     public int Decoration2 { get; set; } 
    } 
    public interface SmallBag : IPrettyBag 
    { 
     public int Decoration1 { get; set; } 
     public int Decoration2 { get; set; } 
    } 
Các vấn đề liên quan