Mike Ruane-Torr said:
I thought I might just add an update to this thread, because I am
beginning to discover that this design pattern is needed more frequently
than I realised. I have just come up against another one!
-snip-
Hi Mike - I have been wrestling with this issue recently. I have
attached a longish example solution that works for me but I'm not sure
if it will pass the "good design" test. I haven't been able to get
much in the way of feedback.
I use the CommandFactory to create an appropriate command to process a
string, but the string format isn't known beforehand and I want each
type of command to determine if it can process the string. Obviously I
don't want to instantiate each command just to find out if it can
process the input.
My CommandFactory caches the method info for CanProcess(), the static
method used to determine if the command can process the input. I have
an abstract base Command from which all other Commands derive. Each
derived command must "hide" the CanProcess() method in the base
Command. If a derived Command fails to do this then the CanProcess()
from the base Command will be called. If the command can process the
input a new instance of the command is returned from CanProcess().
I can't see too much in the way of downside, caching the method info
should avoid performance issues associated with using Reflection. New
commands can be added without difficulty. Any comments would be
appreciated. I've wondered whether delegates might be more appropriate
but I haven't yet been able to figure out how to apply them
appropriately to this situation.
I hope this might be of some value.
Dave
using System;
using System.Text;
using System.Reflection;
namespace Reflect
{
abstract class Command
{
// this method will be invoked if derived class fails to
// provide a "new" version because of
// "BindingFlags.FlattenHierarchy", always returns null
public static Command CanProcess( string Message)
{
Console.WriteLine("Command.CanProcess()");
return null;
}
protected string message;
protected Command( string Message) { message = Message; }
public abstract string Process();
}
class BalanceCommand: Command
{
// "new" keyword required to override base method
public new static Command CanProcess( string Message)
{
Console.WriteLine("BalanceCommand.CanProcess()");
if (Message == "BAL")
return new BalanceCommand( Message);
else
return null;
}
public BalanceCommand( string Message) : base( Message) { }
public override string Process() { return "BalanceCommand"; }
}
// UnknownCommand is a Special Case(496), Patterns of Enterprise
// Application Architecture
class UnknownCommand: Command
{
// catch all method always returns new Command
public new static Command CanProcess( string Message)
{
Console.WriteLine("UnknownCommand.CanProcess()");
return new UnknownCommand( Message);
}
public UnknownCommand( string Message) : base( Message) { }
public override string Process() { return "UnknownCommand"; }
}
class BadCommand: Command
{
// BadCommand has no implementation for canProcess, base method
// will be invoked because of "BindingFlags.FlattenHierarchy" in
// GetMethod call
public BadCommand( string Message) : base( Message) { }
public override string Process()
{ return "BadCommand (should never be called)"; }
}
sealed class CommandFactory
{
private CommandFactory(){} // disable ctor
// method info cache
private static MethodInfo [] methods = null;
// UnknownCommand should always be last value in array
private static string [] commands = {
"BalanceCommand",
"BadCommand",
"UnknownCommand"
};
public static Command Create( string Input)
{
Command cmd = null;
Object [] input = { Input };
foreach (MethodInfo m in Methods)
{
cmd = m.Invoke( null, input) as Command;
if (cmd != null) break;
}
return cmd;
}
private static MethodInfo [] Methods
{
get
{
if (methods == null)
{
int commandCnt = commands.Length;
methods = new MethodInfo[ commandCnt];
Type [] paramTypes = { typeof( string) };
for (int i=0; i < commandCnt; i++)
{
Type t = Type.GetType("Reflect." + commands[ i]);
methods[ i] = t.GetMethod( "CanProcess",
BindingFlags.Static |
BindingFlags.Public |
BindingFlags.FlattenHierarchy,
null,
paramTypes,
null);
}
}
return methods;
}
}
}
class Class1
{
[STAThread]
static void Main(string[] args)
{
Object [] msg = {"BAM"}; // message text to be processed
Command cmd = CommandFactory.Create( "BAM");
Console.WriteLine( cmd.Process());
}
}
}