Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How avoid 'If cascade' and type casting by using polymorphism?

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

like image 750
lampi Avatar asked Dec 27 '22 13:12

lampi


1 Answers

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) { }
}
like image 55
eisentor Avatar answered Dec 29 '22 03:12

eisentor