Reputation: 210
I have a lot of methods (in this case, from web services, but maybe this has no impact?) to call. They're already fixed release versions and won't be changed, it's up to me to adapt to them. I already have the proxies on my project, and in fact, I already call them and the project is ok.
This class main method gets some input parameters (transaction type, and a XML string containing the transaction data). Based on TransactionType, I know which class and method I should call. I also must provide it with a type variable it expects, already built from the provided XML. Here's how it is today (I don't have the code right here, so pardon me for any syntax errors), approximately:
public class MyClass ()
{
public void MyMethod( string TransactionType, string XML )
{
switch( TransactionType ) {
case "1":
type1VO type1Object = ( new Deserializer<Type1>() ).XML2Object( XML );
ws = new WSProxy1();
string response = ws.Method1( type1VO );
//
// lots of other lines of code that use type1VO, type1Object, the response, etc.
//
break;
case "2":
type2VO type2Object = ( new Deserializer<Type2>() ).XML2Object( XML );
ws = new WSProxy2();
string response = ws.Method2( type2VO );
//
// same structure here, but handling types specific for "case 2"
//
break;
}
...
}
}
And it goes on and on. Today, this code is already working, handling about 15 different transaction types, but it was developed in the way you see above. As I'm about to change it (will move this code to a library of itself, 'cause other systems need this logic), I thought it could benefit from some code refinement. Also, the above code is pretty reduced: there are more lines that handle the specific types for each case, I just gave an example.
As it's working, I'm not so worried, but it doesn't seem so "elegant" to me. Gives me the impression that some kind of design pattern could handle this, and that I could handle any transaction with a single block, instead of repeating it for every transaction type. Maybe I'm wrong and this can't be done, I just "felt" it could by looking at the repeating code.
It's C# on .NET v2.0, but I don't mind if there are answers involving other versions or languages. I care much more about the involved concept. I thank you all for any hints you could provide, they're all always great.
Upvotes: 3
Views: 165
Reputation: 733
You can try a combination of the Adapter Pattern and Strategy Pattern.
Create an interface for calling the method and write adapters for each of your proxies that support this interface. The adapters should encapsulate any behavior that is specific to the object it is adapting. You can also have the interface return the transaction type that they support to enable switching at runtime.
An example might be:
public interface IExecuteStrategy
{
string TransactionType {get;}
void Execute( string xmlData );
}
public class WsProxy1Adapter : IExecuteStrategy
{
public string TransactionType
{
get { return "1"; }
}
public void Execute(string xmlData)
{
Type1 type1Object = ( new Deserializer<Type1>() ).XML2Object( XML );
var ws = new WSProxy1();
string response = ws.Method1( type1Object );
//
// lots of other lines of code that use type1VO, type1Object, the response, etc.
//
}
}
public class WsProxy2Adapter : IExecuteStrategy
{
public string TransactionType
{
get { return "2"; }
}
public void Execute(string xmlData)
{
Type2 type2Object = ( new Deserializer<Type2>() ).XML2Object( XML );
var ws = new WSProxy2();
string response = ws.Method1( type2Object );
//
// lots of other lines of code that use type1VO, type1Object, the response, etc.
//
}
}
public class MyClass
{
private static Dictionary<string, IExecuteStrategy> _transactionHandlers;
static MyClass()
{
_transactionHandlers = new Dictionary<string,IExecuteStrategy>();
IExecuteStrategy obj = new WsProxy1Adapter();
_transactionHandlers.Add(obj.TransactionType, obj);
obj = new WsProxy2Adapter();
_transactionHandlers.Add(obj.TransactionType, obj);
}
public void MyMethod( string TransactionType, string XML )
{
_transactionHandlers[TransactionType].Execute( XML );
}
}
Upvotes: 1
Reputation: 17321
You should make an abstract class that understands the basic flow how you work on a transaction type. Then make a child class for each transaction type, that fills in the type specific code. This could be a start to your base class based on the code you gave:
abstract class Base<T, U>
{
private U _obj;
public Base(string xml)
{
_obj = (new Deserializer<T>()).XML2Object(xml);
}
public abstract void process();
protected abstract String getResponse();
}
Then make a simple factory to return the correct child class based on the transaction type. This then allows your large case statement into something like this:
public void MyMethod(string transactionType, string xml)
{
Base.getByTransactionType(transactionType).process();
}
Upvotes: 1