Strategy Pattern - multiple return types/values
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:
- Different algorithms take different inputs (source image, some different set of parameters etc)
- 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 o开发者_运维百科f 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
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.
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.
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.
精彩评论