Strategy Pattern - multiple return types/values
Asked Answered
C

3

2

We are working on an image processing project using C# and EmguCV. Our team is composed of 3 people. To make faster progress, the 3 of us work on different sub-problems or experiment with different algorithms at the same time.

Currently each of us creates a function that contains the major code we are working at and all of us make changes to the driver to add calls to our new functions. All this happens on the same file. We are using source control, so we have not stepped into each other toes yet. But I don't think this will be sustainable as we make more progress. Also, I feel the code is getting messier.

I was thinking it may be better for us to implement the Strategy pattern and encapsulate our algorithms or sub-problem processing into classes of their own and call the execute method on each from the driver.

However I realize there may be some problems with this approach:

  1. Different algorithms take different inputs (source image, some different set of parameters etc)
  2. Different algorithms return different outputs (new image, feature set, a matrix etc)

The first problem I believe I can overcome by doing something like this

Class Strategy1 : IStrategy
{
    int _code;

    // Different *input* paramteres for the strategy may be passed in the 
    // constructor depending on the type of strategy
    public Strategy1(int c)
    {
        _code = c;
    }

    // This is the method defined in the IStrategy interface
    public void execute()
    {
        // Some code that uses _code and does some processing goes here
    }
}

I can change the constructors for the different strategies so they can take in different types of arguments.

When I think about how to deal with the issue of returning multiple types/values, the first thing I can think of is to change execute's return type from void to something like a hash table, where the different return parameters can be stored and returned OR have other members of the class, like "int _returnCode" which can be retrieved by another method or through read-only properties of that class.

I am not sure how good a solution this would be in terms of design principles, and would be happy to hear your opinion on this. Thanks

Cosenza answered 14/11, 2009 at 16:48 Comment(4)
If strategies are so different from each other -- should they implement the same interface at all?Debrief
I agree with dtb. See en.wikipedia.org/wiki/Interface_segregation_principleAlberik
When you say that the algorithms return different things, does each algorithm return one and only one thing of different types, or multiple things, some of them common? If you have no commonality between what the implementations return, it doesn't sound like the Strategy pattern is right.Fomentation
@TrueWill: EDITED Thanks for the link, I did NOT know about SOLID before (I made a typo before and left out the NOT)Cosenza
P
4

If the only thing the algorithms have in common is that they execute, you should be thinking about the command pattern rather than the strategy pattern. (At least, that best fits the solution you've described.)

This is fine! It doesn't buy you the fine-grained sustitutability of the strategy pattern, but it doesn't sound like you're in a position to do that. The command pattern will let you keep your algorithm-specific code separate from your processing flow control (the driver, from the sound of it).

For example, it would let you write code like this:

// ... logic to obtain an image and handle application flow ...

// I'm using 'image' here as a stand-in for a single object all your commands
// could derive their inputs from, or a container of all the inputs. If no such
// object exists, just do what you have to construct the commands.
ICommand[] commands = ImageCommandFactory.CreateCommands(image);

foreach(ICommand command in commands) {
    command.Execute();
}

// ... Handle commands ...

As you mentioned, command objects would use public members to expose the results of their execution - the simplest form would use a property like public object Results { get; }. (Of course, the more you can narrow that return value down to a standard ICommandResults type, the better off you'll be.)

How you handle results is up to you. You could use a factory (again) to create a handler suitable for the command you pass it:

// Picks the correct type of processor, depending on the command
IResultHandler handler = ResultHandlerFactory.CreateHandler(command, form);

// renders the results to the form provided to the factory method
handler.RenderResults();

Or use some other technique that fits your design better.

Precocity answered 14/11, 2009 at 18:17 Comment(3)
@Jeff: Thanks for your answer. I guess I can think of each "process" that is done as commands. How would I pass multiple parameters (I would pass not just the source image, but some parameters related to the command - for e.g. blur level) to the commands in this pattern? I am not very clear on why you use the ImageCommandFactor in the first code sample. Can you you please explain.Cosenza
[contd.] @Jeff: If the results are not renderable (the results will genereally be used in some other "process"; only at the end of all the processes, would a renderable result be produced), would there be any reason to implement result classes that inherit from IResultHandler?Cosenza
@aip - the factory is just a way to encapsulate the conditional logic to specify which parameters belong to which command. The idea is that you pass it enough information to generate all the commands / algorithms (assuming that's what you want to do). As for the results, I mean for the handler.RenderResults method to stand in for whatever process you might do next. Since you're performing additional processing before rendering anything, you might chain multiple commands together or just do that inside the original command.Precocity
S
0

There are several things here:

  • having everything in a single file is an anti-pattern "Big ball of mud"
  • You could split it into several files using partial classes, this solves your problem of many people working on the same file, but is only marginally better.
  • Instead of the strategy pattern, why not just create helper classes (helper pattern), each helper can be tested.
Simaroubaceous answered 14/11, 2009 at 16:59 Comment(0)
D
0

If you have the freedom to use C# 4, you could rely on the covariant return type feature. That way you can define the interface for execute as:

public object execute()

and then override it in derived classes with return types specific to that concrete class.

Drome answered 14/11, 2009 at 17:39 Comment(2)
C# 4.0 will not support covariant return types. stackoverflow.com/questions/1723648Debrief
OK, if Eric Lippert says so, it must be true.Drome

© 2022 - 2024 — McMap. All rights reserved.