【问题标题】:What is a good way to setup a form that supports multiple objects with the same base type?设置支持具有相同基本类型的多个对象的表单的好方法是什么?
【发布时间】:2012-04-01 03:58:43
【问题描述】:

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

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

  • B.Foo
  • C.巴

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

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 语句。这违反了很多面向对象的原则。

但问题是我想不出一个好的和干净的解决方案来解决这个问题。 您有解决此问题的建议/想法吗?

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

【问题讨论】:

  • 我想保持 OOP 并不是那么简单,但是......如果他们只有一个不同的属性,你不能只为同一个对象创建一个新实例并将不同的属性设置为空,对于每个例如,他们不会使用?
  • 在这里澄清一下——您无法控制将其更改为抽象的基本类型?
  • @BryanCrosby 我有控制权

标签: c# winforms oop mvp


【解决方案1】:

首先,很高兴您只问了三个问题 - 它可以更快地解决问题:)。您的代码非常通用,所以我只能提供通用解决方案。

这里的主要目标是增加 A、B 和 C 类的封装——确保与 A、B 或 C 相关的任何内容都存储在这些类中并且不会被移动例如,其他地方的 if 语句。

我们可以将用于确定正确数据源的逻辑从控制器(正在执行您的绑定)移动到您的报告中。此方法的名称应该是描述性的,例如 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:对于 OP,我可能还会为 SomeDataType 字段指定 IList&lt;IBaseType&gt;。由于他使用的是 WinForms,他可以使用BindingList&lt;T&gt; 轻松进行数据绑定。
【解决方案2】:

Strategy Pattern 非常适合第一种情况

对于第二种情况,如果您的面板具有一对一的映射关系,那么如果有多种类型,您最终会得到一个静态只读 Dictionary 面板。 WinForms 中有一个选项卡控件也可以显示一些特定的选项卡

【讨论】:

  • 谢谢,您将如何处理数据绑定?每个衍生物都有一个绑定源。
【解决方案3】:

避免此类代码的一种方法是将决策责任转移到对象本身。例如:

定义某个 A 的集合。

List&lt;A&gt; objects = new List&lt;A&gt;{new B(), new C()}

不要让if/else 在集合上使用foreach 并在每个对象上调用一个在 A 中定义并在子项中覆盖的虚拟方法,例如

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 指令来代替它。

希望这会有所帮助。

【讨论】:

  • 您能再解释一下吗?我不明白为什么要使用 List.. 你将如何处理数据绑定?
  • 这样你就有了一个A对象的集合,它们的真实类型为BC。所以你有一个循环集合,为每个 A 类型调用该布尔方法,这将调用 real 类型的方法重载。
  • @Khelvaster:OP 报告的问题之一实际上是避免在每次有新的派生类型时更改 if/else。拥有这种结构,您应该只派生该类型的新对象并将其添加到集合中。一切都将正常工作而无需更改任何内容。所以这确实解决了 OP 报告的问题。
【解决方案4】:

Dictionary&lt;Type, Action&gt; 怎么样?

然后你可以这样做:

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?
}

【讨论】:

    猜你喜欢
    • 2012-02-14
    • 2018-04-08
    • 2013-05-19
    • 1970-01-01
    • 1970-01-01
    • 2021-10-16
    • 1970-01-01
    • 2013-08-26
    • 1970-01-01
    相关资源
    最近更新 更多