6

I have a set of classes, each of which need to decide at some point which of two or three approaches they should use internally to implement the same functionality externally. Ideally, this should include fallback functionality, where if ApproachA fails, it falls through to try ApproachB (and perhaps approaches C, D, etc.). Up until now, I've just been using coding like if (!success) { ApproachB code }. The problem with this is that several later methods also need to be aware of which approach was chosen and all of them develop their own if (MethodChosen) { } else { } statements as well. I really want to address the issue with something less unwieldy...except none of the other options I've considered seems all that "wieldy". Here are the three approaches I thought of:

  1. Implement a static .Create method that decides which of two derived classes to create, where the two classes have an Interface backing them. The disadvantage of this is that you're writing a lot of the same code twice, and it's not really creating a "fallback" since it forces all decision-making to be done up front in the .Create method. This should work 9/10 times, but there'll be the other 1/10 times where I want the fallback to kick in only when the primary has tried and failed.
  2. The same as above, but with a base or abstract class involved, either as a backing class for both, or with the primary as the base class for the fallback. This has the same fallback disadvantage, but at least there's little or no repeated code.
  3. Implement a normally-constructed abstract class with child classes which can be changed at run-time: i.e.

    public void SomeMethodOrConstructor()
    {
        if (someConditions)
            MyChild = ChildClassA;
        else
            MyChild = ChildClassB;
    }
    
    
    public void Execute()
    {
        MyChild.Execute();
    }
    

The problem with option 3 is passing data between the two when needed. Since some of these methods are modelling outside objects, that'll be fairly frequent. Do nested classes share data with their parent class automatically? Or will I have to pass it all with every call?

Anything else I should consider?


Update: The first class is up and running with the Chain of Responsibility. For now, I've opted not to use the Strategy Pattern or the fallback during method execution, as I believe it may be unnecessary in the end. I think most such execution-fallbacks will actually be better off by staying within their own classes, since there won't be a complete change of gameplan, just a few minor tweaks to deal with. If that turns out not to be the case, I at least know what it is I need to investigate now.

Thanks to everyone who helped with the ultimate solution!

For the curious, my ultimate solution worked roughly like this:

  • Create Handler abstract class, pretty much as outlined in the Wikipedia article, but with a public abstract Handler GetHandler() function, and adding other abstract methods like Load, Save, etc.
  • Implement private handler sub-classes for the parent class (they might as well be sub-classes, since they'll only be handling things for that particular class...avoids later naming issues too). The child classes all take a parameter of the parent object's type in their constructor, so they have easy access to the parent's data.
  • From the parent class's constructor, setup the Chain of Responsibility handlers/successors (again, just like the example), then call FirstHandler.GetHandler(this) and store the result so the class then knows which handler to use in future.
  • Most handled methods then simply reduce to Handler.MethodName().

3 Answers3

4

Use Chain of Responsibility.

chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains a set of logic that describes the types of command objects that it can handle, and how to pass off those that it cannot handle to the next processing object in the chain. A mechanism also exists for adding new processing objects to the end of this chain.

This perfectly fits with what you need to do.

Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • I'll play with it this afternoon, but I'm pretty sure this is the answer I was looking for! –  Nov 15 '10 at 19:43
  • Anything, just give me a shout. – Aliostad Nov 15 '10 at 19:47
  • Any tips on how to implement this in terms of the later calls? To put my specific case into a more generic example, suppose I have a Load and Save methods, where Load handles XML or TXT files. Save doesn't need to "re-figure it out", Load has already determined what it's handling. I'm thinking the parent class just needs to have a Handler property that indicates which handler ultimately handled the event, but I'm not sure about how best to implement that. –  Nov 15 '10 at 20:28
  • I agree, you just need a chain-of-responsibility for the load - save already knows the format so Strategy pattern would work. Have a factory to send back a list of IHandlers, objects that implement IHandler that you call Handle and it returns the result, if false, you go down the change until you find one or after last one exception. – Aliostad Nov 15 '10 at 20:40
  • I think you're right...now I just have to wrap my head around it all. I'm a DBA who's been doing a lot of VB6/VBA until recently, so I'm behind-the-times when it comes to a lot of programming patterns & paradigms. Thanks for the help! –  Nov 15 '10 at 20:53
  • No worries,I was also down in the deep with my studies and only recently started to catch up. You just need 6-7 months, for me it was. – Aliostad Nov 15 '10 at 21:28
0

I think you need Task/Task<T>, especially ContinueWith(Action<Task>) method and 4 properties: IsCanceled,IsCompleted,IsFaulted and Result.

Community
  • 1
  • 1
Cheng Chen
  • 42,509
  • 16
  • 113
  • 174
0

I would probably use the strategy pattern here.

http://en.wikipedia.org/wiki/Strategy_pattern

You only need to pass delgates instead of whole classes. If all of the methods use the same information for processing, this might be helpful.

Ryan Bennett
  • 3,404
  • 19
  • 32
  • This is also a good solution, though if I'm understanding correctly, it would not have the fallthrough behaviour I was looking for. –  Nov 15 '10 at 19:48
  • Looks like your strategy pattern is coming into play after all. :) Thanks! –  Nov 15 '10 at 20:54