I'm thinking about designing a method that would return an object that implements an interface but whose concrete type won't be know until run-time. For example suppose:
ICar
Ford implements ICar
Bmw implements ICar
Toyota implements ICar
public ICar GetCarByPerson(int personId)
We don't know what car we will get back until runtime.
a) I want to know what type of car the person has.
b) depending on the concrete car type we get back we will call different methods (because some methods only make sense on the class). So the client code will do something like.
ICar car = GetCarByPerson(personId);
if ( car is Bmw )
{
((Bmw)car).BmwSpecificMethod();
}
else if (car is Toyota)
{
((Toyota)car).ToyotaSpecificMethod();
}
Is this a good design? Is there a code smell? Is there a better way to do this?
I'm fine with the method that returns the interface, and if the client code was calling interface methods obviously this would be fine. But my concern is whether the client code casting to concrete types is good design.
This is a classic double dispatch problem and it has an acceptable pattern for solving it (Visitor pattern).
Using the
is
keyword in C# (in the manner you have demonstrated above) is almost always a code smell. And it stinks.The problem is that something which is supposed to only know about an
ICar
is now required to keep track of several different classes that implementICar
. While this works (as in it produces code that operates), it's poor design. You're going to start off with just a couple cars...And later on, another car is going to do something special when you
FloorIt
. And you'll add that feature toDriver
, and you'll think about the other special cases that need to be handled, and you'll waste twenty minutes tracking down every place that there is aif (car is Foo)
, since it's scattered all over the code base now -- insideDriver
, insideGarage
, insideParkingLot
... (I'm speaking from experience in working on legacy code here.)When you find yourself making a statement like
if (instance is SomeObject)
, stop and ask yourself why this special behavior needs to be handled here. Most of the time, it can be a new method in the interface/abstract class, and you can simply provide a default implementation for the classes that aren't "special".That's not to say that you should absolutely never check types with
is
; however, you must be very careful in this practice because it has a tendency to get out of hand and become abused unless kept in check.Now, suppose you have determined that you conclusively must type-check your
ICar
. The problem with usingis
is that static code analysis tools will warn you about casting twice, when you doThe performance hit is probably negligible unless it's in an inner loop, but the preferred way of writing this is
This requires only one cast (internally) instead of two.
If you don't want to go that route, another clean approach is to simply use an enumeration:
followed by
You can quickly
switch
on an enum, and it lets you know (to some extent) the concrete limit of what cars might be used.One downside to using an enum is that
CarType
is set in stone; if another (external) assembly depends onICar
and they added the newTesla
car, they won't be able to add aTesla
type toCarType
. Enums also don't lend themselves well to class hierarchies: if you want aChevy
to be aCarType.Chevy
and aCarType.GM
, you either have to use the enum as flags (ugly in this case) or make sure you check forChevy
beforeGM
, or have lots of||
s in your checks against the enums.You would want just a virtual method,
SpecificationMethod
, which is implemented in each class. I recommend reading FAQ Lite's content on inheritence. The design method's he mentions can be applied to .Net as well.A better solution would have ICar declare a GenericCarMethod() and have Bmw and Toyota override it. In general, it's not a good design practice to rely on downcasting if you can avoid it.