将装饰器模式应用于表单

时间:2010-03-31 15:22:05

标签: c# design-patterns decorator

我正在尝试将Decorator设计模式应用于以下情况:

我有3种不同的形式:绿色,黄色,红色。

现在,每个表单都可以有不同的属性集。他们可以禁用最小化框,禁用最大化框,并且它们可以始终位于顶部。

我尝试按以下方式对此进行建模:

              Form <---------------------------------------FormDecorator
              /\                                                  /\
     |---------|-----------|               |----------------------|-----------------|
GreenForm  YellowForm   RedForm  MinimizeButtonDisabled MaximizedButtonDisabled AlwaysOnTop

这是我的GreenForm代码:

public class GreenForm : Form {
    public GreenForm() {
        this.BackColor = Color.GreenYellow;
    }

    public override sealed Color BackColor {
        get { return base.BackColor; }
        set { base.BackColor = value; }
    }
}

FormDecorator:

public abstract class FormDecorator : Form {
    private Form _decoratorForm;

    protected FormDecorator(Form decoratorForm) {
        this._decoratorForm = decoratorForm;
    }
}

最后是NoMaximizeDecorator:

public class NoMaximizeDecorator : FormDecorator
{
    public NoMaximizeDecorator(Form decoratorForm) : base(decoratorForm) {
        this.MaximizeBox = false;
    }
}

所以这是正在运行的代码:

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(CreateForm());
}

static Form CreateForm() {
    Form form = new GreenForm();
    form = new NoMaximizeDecorator(form);
    form = new NoMinimizeDecorator(form);

    return form;
}

问题是我得到的表格不是绿色的,但仍然允许我最大化它。它只考虑了NoMinimizeDecorator表单。我理解为什么会发生这种情况,但我无法理解如何使用此模式进行此工作。

我知道可能有更好的方法来实现我想要的。我将这个例子作为尝试将Decorator模式应用于某事。也许这不是我能用到的最佳模式(如果有的话)。还有其他模式比装饰器更适合实现这一目标吗?我在尝试实现Decorator模式时做错了什么?

由于

3 个答案:

答案 0 :(得分:6)

这里的问题是你实际上没有实现装饰器模式。为了正确实现模式,您需要创建装饰器的子类Form,然后拦截装饰器上的所有操作并将它们转发到您的私有Form实例。除此之外,除了在FormDecorator构造函数中分配引用之外,您再也不会使用该私有Form实例。最终结果是您创建了GreenForm,然后将其包装在NoMaximizeDecorator中,然后将其包装在NoMinimizeDecorator中。但是因为您从未将针对NoMinimizeDecorator的操作转发到包装的Form实例,所以只有NoMinimizeDecorator实例实际将任何行为应用于所使用的实例。这符合您在运行代码时所观察到的内容:带有禁用的最小化按钮的标准窗口。

Form是在C#中创建装饰器的一个非常糟糕的例子,因为它的大部分属性和方法都是非虚拟的,这意味着如果你通过Form引用访问装饰的表单,没有办法拦截基类的属性 - 你无法有效地“包裹”Form

修改

我发现语句“Form是在C#中创建装饰器的一个非常糟糕的例子”,这真的引出了 这个好例子的问题。通常,您将使用装饰器模式来提供自定义接口实现,而无需从头开始实现整个实现。 非常常见示例是泛型集合。大多数需要列表功能的内容都不依赖于,例如List<String>,而是依赖于IList<String>。因此,如果您想要一个不接受短于5个字符的字符串的自定义集合,您可以使用以下内容:

public class MinLengthList : IList<String>
{
    private IList<string> _list;
    private int _minLength;

    public MinLengthList(int min_length, IList<String> inner_list)
    {
        _list = inner_list;
        _minLength = min_length;
    }

    protected virtual void ValidateLength(String item)
    {
        if (item.Length < _minLength)
            throw new ArgumentException("Item is too short");
    }

    #region IList<string> Members

    public int IndexOf(string item)
    {
        return _list.IndexOf(item);
    }

    public void Insert(int index, string item)
    {
        ValidateLength(item);
        _list.Insert(index, item);
    }

    public void RemoveAt(int index)
    {
        _list.RemoveAt(index);
    }

    public string this[int index]
    {
        get
        {
            return _list[index];
        }
        set
        {
            ValidateLength(value);
            _list[index] = value;
        }
    }

    #endregion

    #region ICollection<string> Members

    public void Add(string item)
    {
        ValidateLength(item);
        _list.Add(item);
    }

    public void Clear()
    {
        _list.Clear();
    }

    public bool Contains(string item)
    {
        return _list.Contains(item);
    }

    public void CopyTo(string[] array, int arrayIndex)
    {
        _list.CopyTo(array, arrayIndex);
    }

    public int Count
    {
        get { return _list.Count; }
    }

    public bool IsReadOnly
    {
        get { return _list.IsReadOnly; }
    }

    public bool Remove(string item)
    {
        return _list.Remove(item);
    }

    #endregion

    #region IEnumerable<string> Members

    public IEnumerator<string> GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return ((IEnumerable)_list).GetEnumerator();
    }

    #endregion
}

public class Program
{

    static void Main()
    {
        IList<String> custom_list = new MinLengthList(5, new List<String>());
        custom_list.Add("hi");
    }
}

答案 1 :(得分:2)

这是装饰器模式的误用。装饰器模式与对象的行为有关。你是构建对象,这些对象属于创造伞。虽然你可能能够围绕“没有最大化按钮”这一行为,但听起来有点不合时宜。

我认为没有一种真正的方法来修复你的设计。装饰图案不合适。当你可以使用Builder时,任何解决这个问题的尝试都会非常苛刻。

可以看到做的是装饰表单的 Builder 以在构建时执行这些操作。它看起来像这样......

public interface IFormBuilder {
    public Form BuildForm();   
}

public class FormBuilder : IFormBuilder {
   public Form BuildForm(){
        return new Form();
   }
}

public class NoMaximizeFormBuilder : IFormBuilder {
    private IFormBuilder _builder;
    public NoMaximizeFormBuilder (IFormBuilder builder){
        _builder = builder;             
    }
    public Form BuildForm(){
        f = _builder.BuildForm();
        f.MaximizeBox = false;
        return f;
    }
}

你可以像这样使用它......

static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(CreateForm());
}

static Form CreateForm() {

    var b = new FormBuilder();
    var b = new NoMaximizeFormBuilder(b);
    return b.Build();
}

但即使这有点难看。您可以将其转换为用于构建表单的流畅界面。

答案 2 :(得分:0)

尝试使您的模式将装饰器属性应用于同一对象,而不是创建新的表单:

public abstract class FormDecorator {
    protected Form _decoratorForm;

    protected FormDecorator(Form decoratorForm) {
        this._decoratorForm = decoratorForm;
    }
    public abstract void Decorate();
}

public class NoMaximizeDecorator : FormDecorator
{
    public NoMaximizeDecorator(Form decoratorForm) : base(decoratorForm) {
         Decorate();
    }
    public override void Decorate() {
        _decoratorForm.MaximizeBox = false;
    }
}

在你的主要地方:

static Form CreateForm() {
    Form form = new GreenForm();
    new NoMaximizeDecorator(form);
    new NoMinimizeDecorator(form);

    return form;
}