设置支持具有相同基本类型的多个对象的表单有什么好方法?

时间:2012-03-15 15:43:53

标签: c# winforms oop mvp

我有一种基础类型(A),它有两个衍生物(B和C)。基类型不是抽象的。所以,我有三个对象。

B和C之间的唯一区别是它们都有一个额外的不同属性:

  • B.Foo
  • C.Bar

现在我的代码中有这样的条件:

if(myObject is B)
     myDatabindB.DataSource = ((B)myReport).Foo);
else if(myObject is C)
     myDatabindC.DataSource = ((C)myReport).Bar);

并在另一种方法中:

pnlSomePanel.Visible = myObject is B;
pnlSomeOtherPanel.Visible = myObject is C;

但你可以想象,当有新类型时,我必须更新所有的if-else语句。这违反了许多OO原则。

但问题是我无法想出一个解决这个问题的漂亮而干净的解决方案。 你有解决这个问题的建议/想法吗?

编辑: 如果重要,我正在使用MVP模式。

4 个答案:

答案 0 :(得分:2)

首先,你只用三个项目来问这个很好 - 它可以更快地修复问题:)。您的代码非常通用,因此我只能提供通用解决方案。

此处的主要目标是增加A,B和C类的封装,以确保与A,B或C相关的任何内容都存储在这些类中而不会移动比如说if语句在其他地方。

我们可以移动逻辑来确定从Controller到控制器(正在进行绑定)的正确数据源是什么。此方法的名称应该是描述性的,如GetReportSubjectLine()。

class A{
    <snip>
    public virtual SomeDataType getDataSourceForViewType(){
     throw new NotImplementedException()
    }
}

class B{
<snip>
public override SomeDataType getDataSourceForViewType(){
    return this.Foo;
    }
}

class C{
 public override SomeDataType getDataSourceForViewType(){
    return this.Bar;
    }
}

如果您想要制作不同的UI,而您仍需要报告中的此类信息来生成您正在生成的任何图形视图,则此代码将是可重用的。

围绕你提出的第二个问题没有好办法。我们总是可以将面板的可见性也移动到报告中,但这会增加耦合 - 一个类与另一个类绑定多少 - 太多了。您的报告不应与特定视图相关联。

最好的解决方案是添加另一层间接 - 在这种情况下,是一个中间类,用于处理在何时显示哪些面板的逻辑。这样,您的控制器就不必承担管理面板可视性的责任。

public class PanelVisibilityManager{
    ICollection<Panel> ManagedPanels {get; set;}
    //
    public IDictionary<System.Type, ICollection<Panel>> Switchboard {get; set;}

    public void TogglePanelsFor(System.Type item){
        foreach(var panel in ManagedPanels){
            panel.Visible=false;
            }
        foreach(var panel in Switchboard[item]){
                 panel.Visible=true;              
            }

}

希望这有帮助!

答案 1 :(得分:0)

避免这种类型代码的方法之一就是将决策响应能力转移到对象本身。例如:

定义一些A的集合。

List<A> objects = new List<A>{new B(), new C()}

而不是让if/else使用foreach优先于集合并在每个对象上对一个虚拟方法进行定义,并在子节点中重写,例如

virtual bool ThisIsMe(A objectToCheck){}

B和C通过检查objectToCheck是否为其类型并覆盖truefalse来覆盖此方法。

编辑

示例:

public class A 
{
   public virtual bool ThisIsMe(A objectToCheck){}
   public virtual object GetData{}
}

public class B : A 
{
   public override  bool ThisIsMe(A objectToCheck)
   {   
      return objectToCheck is B;
   }

   public override object GetData()
   {
      return this.Foo;
   }
}

public class C : A 
{
   public override  bool ThisIsMe(A objectToCheck)
   {   
      return objectToCheck is B;
   }

   public override object GetData()
   {
      return this.Bar;
   }
}

现在代替那个if/else,像这样:

foreach(A objA in objects) { if(objA.ThisIsMe(myObject)) { myDatabindB.DataSource = objA.GetData(); break; } }

也可以用一些花哨的LINQ指令代替它。

希望这有帮助。

答案 2 :(得分:0)

Strategy Pattern非常适合第一种情况

对于第二种情况,如果你有一对一的面板映射,你最终可能会得到一个静态的只读字典&lt; Type,Panel&gt;如果有很多类型的面板。 WinForms中有一个选项卡控件,可以显示一些特定的选项卡

答案 3 :(得分:0)

Dictionary<Type, Action>怎么样?

然后你可以这样做:

var myActors = new Dictionary<Type, Action<BaseClass>>();

myActors.Add(typeof(classA), DoSomethingWithA);
myActors.Add(typeof(classB), DoSomethingWithB);

...

Action actor;

if(myActors.TryGetValue(specialRetrievedOnlyAsBase.GetType(), actor))
{
    ResetEverything();
    actor(specialRetrievedOnlyAsBase);
}
else
{
    // ToDo: What should happen if this type is not supported?
}

...

private void DoSomethingWithA(BaseClass)
{
    var classAObject = (ClassA)BaseClass;

    // ToDo: What should happen if classA arrives?
}

private void DoSomethingWithA(BaseClass)
{
    var classAObject = (ClassB)BaseClass;

    // ToDo: What should happen if classB arrives?
}