添加更多行为而不创建新类

时间:2013-04-19 07:40:02

标签: c# oop design-patterns design-principles

这是接受采访时提出的问题。

  

Label有一个属性Text
  在一个页面中,标签很简单Label,在其他页面中,它可以处理以下行为中的任何一个或组合   可点击
  可调整大小
  可拖动

     

如何设计应用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

但是,我觉得这是不正确的方法,因为我不想添加那些具体的类。我希望避免使用ClickableAndDraggableLabelClickableDraggableResizableLabel

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

6 个答案:

答案 0 :(得分:9)

我会使用Decorator pattern。它在.net世界中广泛用于不同类型的流,例如,它允许您为字节流组合加密的,压缩的文本流包装器。类图取自维基

enter image description here

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

// 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);

答案 1 :(得分:1)

我只有CanClickdragresize的布尔属性,默认为true,并根据需要(或继承)进行调整。

构造函数如下

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

如果他们一次扩展课程,可能会在以后进一步扩展

答案 2 :(得分:1)

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

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

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

如果定义了多个枚举,您可以查看以下链接:

然后在类Label中定义一个成员,执行操作:

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();
}

如果您需要添加操作,则必须向枚举LabelAction添加一个项目,向CanDo()类添加一个方法Do()和一个方法Label 。不是那么多。

答案 3 :(得分:0)

您可以拥有一个实现所有接口的基类,并将其行为委托给具体的策略类。 然后你会有一个NullDraggable,nulResizeable,NullClickable默认情况下什么都不做(所以你的基本标签不可点击,可调整大小和可绘制) 然后,您创建不同的策略,如Clickable,DoubleClickable,WidthResizeable等... 然后,您将所需的组合传递给您的班级。 通过这种方式,您可以获得很多很少的策略,这些策略很容易在具有相同接口的其他组件中重用。 通过使用复合模式可以有多种行为(例如,您可以使用可点击和双击的togheter)

虽然

可能会有点过时

答案 4 :(得分: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 = new ComplexLabel();

你应该将ComplexLabel构造函数设为私有,并将任务分配给某个工厂。像

var rd = Factory.GetResizableDragableLabel();

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

答案 5 :(得分:-1)

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

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

Drag and Drop

Label Class

此致