views:

118

answers:

4

Using interfaces is a very easy way to remove dependencies, but what happens when one of your classes needs a method not defined by the interface? If you're using constructor injection or a factory, how do you access that extra method without casting? Is this possible?

Here is an example of a factory with this problem. Am I trying to do something impossible? Thanks for your help.

interface IFoo {
    int X { get; set; }
    int Y { get; set; }
}

public class A : IFoo {
    int X { get; set; }
    int Y { get; set; }
}

public class B : IFoo {
    int X { get; set; }
    int Y { get; set; }
    int Z { get; set; }
}

public static class FooFactory {
    public static IFoo GetFoo(string AorB) {
        IFoo result = null;
        switch (AorB) {
            case "A":
                result = new A();
                break;
            case "B":
                result = new B();
                break;
        }
        return result;
    }
}

public class FooContainer {
    private IFoo foo;

    public FooContainer(IFoo foo) {
        this.foo = foo;
    }

    /* What methods would you define here. I'm new to IoC. */
}

public class Main(...) {
    int x,y,z;
    IFoo fooA = FooFactory.GetFoo("A");
    x = foo.X;
    y = foo.Y;

    IFoo fooB = FooFactory.GetFoo("B");
    x = foo.X;
    y = foo.Y;
    z = foo.Z; /* Does not compile */
    z = ((B)foo).Z; /* Compiles, but adds unwanted dependency */
}
+6  A: 

You do indeed need to cast. This is normal, and sometimes necessary. It is usually a sign that something is wrong though.

The ideal is that if a method/routine takes/returns an interface, then your logic only cares about the members exposed by that interface. If inside that method you find yourself checking the exact type so that you can cast to that type and call different members depending on the type, then something probably be wrong.

Let's say you have an IContact interface, and some of your entities that implement this are your classes Customer, Purchaser, and Contractor. If you have a SendChristmasCard method that takes IContact, it should only care about the IContact members. If you have logic inside this method that is doing a select Case on the obj.GetType().ToString to find out if it's a Customer or not, then:

  1. That functionality should probably be over in the Customer-centric side of your code base, taking a Customer object as a parameter. (In your example, there would be separate logic for acting upon class A and class B.)
  2. IContact should define common members that your SendChristmasCard method would call, and be completely ignorant of the logic that goes on inside the particular object. Each class that implements IContact would implement these member differently. (In your example, class A would also implement property B, but it wouldn't do anything with it.)

In the case where a method returns an interface and you use the object, the above still applies but in my experience it can, now and then, be best to put up with the casting. The complication you add by "fixing" the situation might make it more complicated. I would say that the further up and non-standard the logic is, just take the simple way out. SendChristmasCard is obviously not core functionality; and if an IContact factory method is only handy method that gives you All the contacts, then maybe just use that, pass it to SendChristmassCard(IContact Contact), and inside there check for the type to say "it was great buying from you this year" or "it was great selling to you this year" etc. But if this is core logic in your system, you really need to look for a better way.

Check out the Decorator Pattern though, which can help in situations like this.

Patrick Karcher
+1  A: 

If you're trying to access a method that isn't available to the interface, then don't use the Factory. You're obviously hard coding a dependency...so just go with it (but only if it's really necessary).

No need to over-complicate things.

Trying to cast back to an Object type rather than the interface is going to introduce a dependency...but it's going to hide it rather than obviously expose the dependency. If somebody changes the Factory in the future and your call returns a different Object type, your code is now going to break in a non-obvious way.

Justin Niessner
+1  A: 

When you encounter the need to downcast an object, it is usually a sign that the API could be better.

Downcasting an abstract type is a violation of the Liskov Substitution Principle. It can usually best be addressed by changing the style of the interface in question. Instead of exposing a lot of properties and queries (in CQS terminology), reverse the focus towards a more command-oriented approach. This is the Hollywood Principle.

Instead of having IFoo expose the X and Y properties, you may be able to redefine its behavior towards a set of commands:

public interface IFoo
{
    void DoStuff();

    void DoSomethingElse(string bar);

    void DoIt(DateTime now);
}

Concrete implementations can then encapsulate whatever data they would like (such as X, Y or Z properties) without the consumer needing to know about them.

When the interface grows to become too big, it's time to apply the Interface Segregation Principle or the Single Responsibility Principle.

Mark Seemann
Thanks, this is really helpful. In fact, this is the pattern I already began to follow. I like the references to SOLID principles.
whatispunk
A: 

Hi, i'm reading this thread and i like to know what is the best approach in the following scenario:

I'm writing a generic mvc framework, i have the following entities among others:

  • a request ( this could be a CLI request or a HTTP request, there's a common interface inheritance from the IRequest interface )
  • a front controller, router, routes, dispatcher, etc
  • a action controller for each type of request ( i need to do different things depending on the request type )

to maintain the front controller and core classes as generic as possible i use the type IRequest for all the internal procedures ( routing, dispatching, etc ) but when i need to instantiate the controller i need to cast back the IRequest to the concrete type ( http for instance, because i need to use get and post methods in the controller for example )

is there a better approach?

thanks

Gaston