我有一种基础类型(A),它有两个衍生物(B和C)。基类型不是抽象的。所以,我有三个对象。
B和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语句。这违反了许多OO原则。
但问题是我无法想出一个解决这个问题的漂亮而干净的解决方案。 你有解决这个问题的建议/想法吗?
编辑: 如果重要,我正在使用MVP模式。
答案 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
是否为其类型并覆盖true
或false
来覆盖此方法。
编辑
示例:
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?
}