我正在尝试将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模式时做错了什么?
由于
答案 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;
}