views:

350

answers:

6

OK so I'm looking a some code which looks roughly like this:

void DoSomething(object o)
{
    if (o is Sometype1) { 
    //cast o to Sometype and do something to it
    }
    else if (o is Sometype2) {
    //cast o to Sometype2 and do something to it
    }
    ...
    else if (o is SometypeN) {
    //cast o to SometypeN and do something to it
    }
}

Now one approach would be to make all the objects o that are used as parameters implement an interface like

interface ICanHaveSomethingDoneToMe
{
    //expose various properties that the DoSomething method wants to access
}

But the problem with this is that I don't want all my objects to implement this interface - the logic for the do something method doesn't really belong with them. What pattern should I be using to deal with this?

I suspect something like a series of implementations of

interface IPropertiesForDoingSomethingTo<T>
{
    //expose various properties that the DoSomething method wants to access
}

might be better. I'd have an implementation for each of the object types that I want to do it to, but then I have this new problem. I would at point need to have a method like

IPropertiesForDoingSomethingTo<T> GetPropsGeneric(T t);

but is this going to need to have a massive switch on it? Should I define a class with loads of methods like

IPropertiesForDoingSomethingTo<Someobject1> GetProps(Someobject1 t);
...
IPropertiesForDoingSomethingTo<Someobject1> GetProps(SomeobjectN t);

This has the problem compared to the generic version that you wouldn't be able to add new types at runtime. Ts there something cunning one can do with a DI container in GetPropsGeneric to resolve the container? Thanks!

+2  A: 

It looks like you might be using C#. I believe that you can create "extension methods" that attach to already established classes.

Another approach would be to create handler delegates for each type and store references to the delegates in a hashtable keyed by object type.

Then your "DoSomething" method could just lookup the approprate delegate by the type of the object passed in and execute.

bobwienholt
+6  A: 

any time you see a switch statement (or a series of if-statements) that are checking the type of an object, this is a Big Red Flag for a missing base class or interface. In other words, the code should be relying on polymorphism, not testing the object type

if you cannot change the base class or implement an interface, you're probably left with a dictionary to simulate dynamic dispatching. In C# you could use an anonymous delegate for the method that included the cast

as for the property access, if the properties don't conform and accessing via reflection is not an option, you may need to extract the property values in the method/delegate above and pass them to a generic function instead

Steven A. Lowe
You could implement a wrapper around any class that you can't change and have it implement your interface. Then only deal with objects that have been wrapped. I do this with DataContext, for example, to make it easier to unit test.
tvanfosson
how about if you have an if statement/switch to check a parameter then create an object i.e. The Factory Pattern
Franco
A: 

A real example would be more helpful. If you are simply changing the implementation of a method for a family of related classes then, as @Steven A. Lowe says, you're best off using polymorphism and using the subclass relationships for this. If the classes don't participate in "is a" relationships, then other patterns like Visitor may be more appropriate.

tvanfosson
A: 

Polymorphism is the answer, where a base object is passed in. However, it is significantly more boilerplate and semantic complexity than perhaps you want.

It will require your functionality to be shunted somehow into the derived classes and a "virtual" function to be implemented.

Paul Nathan
A: 

I think this is more of a question for an Aspect Oriented Programming approach.

I'd want your DoSomething method to take an ICanHaveSomethingDone interface as its parameter. Then, I'd define the ICanHaveSomethinhgDone interface, and derive from it sublcasses (one for each object which you'd like to DoSomething to) which implement the DoSomethingToMe, differently for each implementing class. Each of them simply takes a constructor of the type you want to do something to, so that when you go to invoke the DoSomething, you will actually invoke a Factory (very simple, just creating an instance of your ICanHaveSomethingDone class from the input type to create an instance of a class which implements the DoSomethingToMe method, and which has the proper code for the underlying object.

Essentially, think of it this way; you're defining an interface contract that you want the parameter objects to implement; and defining "decoration" in the form of subclasses of your object and the Interface which implement specific implementations of the interface behavior (therefore fulfilling the contract) for your specific class. In this manner, you can make your implementation of the DoSomething method for each of the class completely separate from the source of those classes.

One other thing this does; this means that you can add new types at runtime if you make your factory into a DI container; by injecting into your factory container the new types that you'd like to be able to do something to, so long as you have an implementation of the action you'd like to take defined as a class derived from your interface and from that class, you're good. You can even define the behavior at runtime without having the derived class implemented, if you implement a complete AOP approach; defining your interface, defining your behaviors on the actions, and parameterizing the implementation of your derived classes to compose together at runtime the behaviors you want with the object you're passing in. But that's complicated... :-)

Spring AOP is great about this stuff, by the way. I'd read up on it.

McWafflestix
A: 

I agree with Steven, also this problem context reminded me double dispatching problem, therefore Visitor Pattern may be the right solution. But, it is obvious that your hierarchy is missing some interface definitions.

baris_a