11

这是采访中被问到的问题。

有一个Label带有属性Text
的标签在一个页面中很简单Label,在其他页面中它可以处理以下任何一个或组合的操作
Clickable
Resizable
Draggable

你如何设计这个应用 OOP 设计原则和设计模式的标签组件?

我说我将创建以下内容:

public class Label
{
  public string Text{get;set;}
}
public interface IClickable
{
 void Click();
}

public interface IDraggable
{
 void Drag();
}
public interface IResizable
{
 void Resize();
}

这样如果客户想要 Resizable Label

public class ResizableLabel:Label,IResizable
{
  ....
}

同样的方式ClickableLableDraggableLabel

但是,我觉得这是不正确的做法,因为我不想添加那些具体的类。我想避免使用ClickableAndDraggableLabelor ClickableDraggableResizableLabel

是否有任何设计模式可以在不添加这些具体类的情况下解决这个问题?

4

6 回答 6

9

我会使用装饰器模式。它在 .net 世界中广泛用于不同类型的流,例如,允许您为字节流编写加密的、压缩的、文本流包装器。类图取自wiki

在此处输入图像描述

您的情况的示例在实现中并不是那么微不足道,但是使用不需要其他类来实现新的编译行为:

// Define other methods and classes here
public class Label
{
    public string Text{get;set;}

    public virtual void MouseOver(object sender, EventArgs args) { /*some logic*/ }
    public virtual void Click(object sender, EventArgs args) {  /*some logic*/ }

    //other low level events
}

public class ClikableLabel : Label
{
    private Label _label;

    public ClikableLabel(Label label)
    {
        _label = label; 
    }

    public override void Click(object sender, EventArgs args) 
    {   
        //specific logic
        _label.Click(sender, args);
    }
}

public class DraggableLabel : Label
{
    private Label _label;

    public DraggableLabel(Label label)
    {
        _label = label; 
    }

    public override void Click(object sender, EventArgs args) 
    {   
        //specific logic
        _label.Click(sender, args);
    }
}
public class ResizableLabel : Label
{
    private Label _label;

    public ResizableLabel(Label label)
    {
        _label = label; 
    }

    public override void MouseOver(object sender, EventArgs args) 
    {   
        //specific logic
        _label.MouseOver(sender, args);
    }

    public override  void Click(object sender, EventArgs args) 
    {
        //specific logic
        _label.Click(sender, args);
    }
}

现在你可以

var clickableDragableLabel = new ClikableLabel(new DraggableLabel(new Label{Text = "write me!"}));

var makeItResizable = new ResizableLabel(clickableDragableLabel);
于 2013-04-19T07:51:59.677 回答
1

我不认为Interface可以解决您的问题。
我会做更像这样的东西:

首先,定义一个列出所有操作的枚举:

public Enum LabelAction{ None = 0, Clickable = 1, Resizable = 2, Draggable = 4 }

对于定义多个枚举,您可以查看以下链接:

然后在你的类标签中定义一个成员,采取行动:

public class Label
{
    private readonly LabelAction _action;
    private string Text { get; set; }

    public class Label(string text)
        : Label(text, LabelAction.None) { } 

    public class Label(string text, LabelAction action)
    {
        this.Text = text;
        this._action = action; 
    }

    public bool CanClick 
    { 
        get
        {
            return this._action & LabelAction.Clickable == LabelAction.Clickable;
        }
    }

    public bool CanResize { get { return this._action & LabelAction.Resizable == LabelAction.Resizable ;} }
    public bool CanDrag { get { return this._action & LabelAction.Draggable == LabelAction.Draggable ;} }

    public Click()
    {
       if(this.CanClick) { /* click */ }
       else { throw new Exception("Not clickable");}
    }
    public Drag()
    {
       if(this.CanDrag) { /* drag */ }
       else { throw new Exception("Not draggable");}
    }
    public Resize()
    {
       if(this.CanResize) { /* resize */}
       else { throw new Exception("Not resizable");}
    }
}

用法:

var simpleLabel = new Label("simple");
var clickable = new Label("clickable", LabelAction.Clickable);
var clickableDraggable = new Label("clickable and draggable", LabelAction.Clickable | LabelAction.Draggable);

public void DoEvent(Label label)
{
    if(label.CanClick) label.Click();
    if(label.CanDrag) label.Drag();
    if(label.CanResize) label.Resize();
}

如果您需要添加一个动作,您将必须向 Enum 添加一项,向类添加LabelAction一种方法CanDo()和一种方法。没有那么多。Do()Label

于 2013-04-19T07:52:51.347 回答
1

我只需要、 和的布尔属性CanClick,它们都默认为 true,并根据需要(或继承)为 false。dragresize

构造函数如下

public Label(bool canClick = true, bool canDrag = true, bool canResize = true){}

很有可能,如果他们扩展了一个类一次,它会在以后进一步扩展

于 2013-04-19T07:44:50.703 回答
0

我认为对于这种情况,没有必要重新发明轮子。即使问题明确要求 OOP,它也没有明确要求您忽略组件模型编程或基于事件的行为。这就是为什么我会遵循一种允许责任划分的方法,其中 Label 负责在单击或拖动时通知它,并且 SomeOtherComponent 可能会或可能不会侦听此类通知(事件)以执行其他逻辑。

请查看以下链接,了解这些用户操作的事件分派方法示例:

拖放

标签类

问候,

于 2013-04-19T08:52:26.633 回答
0

好吧,您可以拥有一个实现所有接口并将其行为委托给具体策略类的基类。然后你将有一个 NullDraggable、nulResizeable、NullClickable 默认情况下什么都不做(所以你的基本标签不可点击、调整大小和可拖动)然后你创建不同的策略,如 Clickable、DoubleClickable、WidthResizeable 等......然后你传递你的组合想上你的课。通过这种方式,您可以获得许多易于在具有相同界面的其他组件中重用的小策略。通过使用复合模式,您可以拥有多种行为(例如,您可以同时拥有可点击和可双击)

不过,这可能有点过于ingeneered

于 2013-04-19T07:45:50.820 回答
0

我认为你过度思考了面试官的想法。如果案例一样简单实用,为了避免过度抽象的复杂性,那么这就足够了:

public class Label
{
    public string Text{get;set;}
}

public class ComlexLabel : Label
{
    Click();
    Drag();
    Resize();
}

你可以对它进行任何操作。现在,如果对于挑战,您只需要一个具体实例并且需要单独类型的对象才能仅执行这些事情的组合,那么它再次简单 - 只是这次您必须创建类似的原型/接口:

public class Label
{
    public string Text{get;set;}
}

public interface Clickable
{
    Click();
}

public interface Resizable
{
    Resize();
}

public interface Dragable
{
    Drag();
}

public interface ClickableDragable : Clickable, Draggable
{

}

public interface ClickableResizable : Clickable, Resizable
{

}

public interface ResizableDragable : Resizable, Draggable
{

}

public interface ClickableDragableResizeable : Resizable, Clickable, Draggable
{

}

public class ComlexLabel : Lable, ClickableDragableResizeable
{
    Click();
    Drag();
    Resize();
}

现在,您可以ComlexLabel通过创建提供所需功能的类型来获得实例。喜欢:

ResizableDragable rd = new ComlexLabel();
ClickableResizable cr = new ComlexLabel();
ClickableDragableResizeable cdr = new ComlexLabel();

现在rdcrcdr拥有了不同的能力。他们背后只有一个具体的例子。为了防止客户通过这样做获得完全特权

var cdr = 新的 ComplexLabel();

您应该将ComplexLabel构造函数设为私有并将任务分配给某个工厂。喜欢

var rd = Factory.GetResizableDragableLabel();

现在rd必须只是ResizableDragable没有Click功能..

于 2013-04-19T08:15:53.823 回答