Reputation: 83
Let's assume the following class structure with base class BC and 2 derived classes DC_A and DC_B; Furthermore there is a class XY with a methode goo() with a parameter of type BC and other methodes
// base class
public class BC
{
public virtual void foo();
}
// derived class A
public class DC_A : BC
{
public override void foo() {}
}
// derived class B
public class DC_B : BC
{
public override void foo() {}
}
public class XY
{
public void goo(BC o)
{
// perfectly fine using polymorphism; no Ifs' no casting, OOP at its best ;-)
o.foo();
// but what to do here?
if ( (o as DC_A) != null )
{
doX(o as DC_A);
}
else if ((o as DC_B) != null)
{
doY(o as DC_B);
}
}
private void doX(DC_A o) {}
private void doY(DC_B o) {}
}
In 'Is passing around value objects using polymorphism a bad practice?' the Visitor pattern is proposed;
The problem of the If cascade and the casting is moved (not eliminated) into the abstract base class.
Are there better solutions to completely avoid the if's?
Its no option for me (in this example) to move the functionality from doX/doY to class DC_A/DC_B
Your advice is much appreciated.
Edit: The background of this question is a C# / WinForms application with a form to manage a "test rule" consisting of differenct sub entities like TestSpec with a collection of measurement types, test limits, and so on (my DC_A, DC_B classes) all derived from EntityBase (=BC from above) The form sends an event to the controller that a sub entity has changed; simplified PropertiesChanged(EntityBase o) The controller calls the corresponding methode in the model class (methode goo in class XY from above) which is now responsible to do the businesslogic which is not only persisting the changed sub entity test limit but also e.g. creating a new test limit revision object, increasing a test spec revision, etc.. Maybe in this way the generic approach of having a methode like PropertiesChanged(EntityBase o) should be changed to more specifing events from the form and specific event handler in controller and model class to handle "TestLimitChanged" etc.
But this special case has led me to the more generic or "philosophical" question about polymorphism at all ;-)
Upvotes: 4
Views: 861
Reputation: 68
If you are using .NET 4 there is a possibility with overloading and dynamic type, maybe that is an alternativ for you.
class Program
{
static void Main(string[] args)
{
DC_A dca = new DC_A();
DC_B dcb = new DC_B();
XY xy = new XY();
xy.goo(dca);
xy.goo(dcb);
}
}
// base class
public abstract class BC
{
public abstract void foo();
}
// derived class A
public class DC_A : BC
{
public override void foo() { }
}
// derived class B
public class DC_B : BC
{
public override void foo() { }
}
public class XY
{
//public void goo<T>(T o) where T : BC
//{
// //dynamic dyn = Convert.ChangeType(o, o.GetType());
// //dynamic dyn = o;
// //gooi(dyn);
// gooi((dynamic)o);
//}
// http://smellegantcode.wordpress.com/2008/11/04/dynamic-typing-in-c-4-and-duck-generics/
public void goo<T>(T o) where T : BC
{
gooi((dynamic)o);
}
private void gooi(DC_A o)
{
o.foo();
doX(o);
}
private void gooi(DC_B o)
{
o.foo();
doY(o);
}
private void gooi(BC o)
{
o.foo();
}
private void doX(DC_A o) { }
private void doY(DC_B o) { }
}
Upvotes: 5
Reputation: 109557
My suggestion:
You could decouple things further by passing a delegate to DC_A and DC_B which they will call, instead of exposing doX() and doY(). It very much depends on the usage patterns.
However, this is only really very useful if you can add some value to the Do() implementations in the derived classes. If all Do() does is just call doX() or doY(), you don't really get any benefit from doing it this way.
So I'm going to agree with the other poster here that says just do the casting.
Upvotes: 3
Reputation: 63340
I don't think there's too much to worry about, once we fix a couple of things:
if
s can be true, so you don't need an else
After that, we have:
// perfectly fine using polymorphism; no Ifs' no casting, OOP at its best ;-)
o.foo();
var dca = o as DC_A;
if (dca != null)
{
doX(dca);
}
var dcb = o as DC_B;
if (dcb != null)
{
doY(dcb);
}
which I'd say looks fine.
If you want to go further, you could change the implementations of doX
and doY
so that they return immediately if passed null
; then you could just say
doX(o as DC_A);
doY(o as DC_B);
Upvotes: 2