Reputation: 11380
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 */
}
Upvotes: 3
Views: 291
Reputation: 233150
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.
Upvotes: 1
Reputation: 23603
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:
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.
Upvotes: 6
Reputation: 245419
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.
Upvotes: 1