I have a situation in a project I am currently working on at work that has left my mind restless the entire weekend. First, I need to explain my scenario, and the possible solutions I have considered.
I am writing a composite WCF service that will be aggregating a large amount of external API's . These API's are arbitrary and their existence is all that is needed for this explanation.
These services can be added and removed throughout the period of development. My WCF service should be able to consume the services using several methods (REST,SOAP,etc). For this example, I am focusing on communicating with the external APIS by manually creating the requests in code.
For example, we might have two API's ServiceX and ServiceY.
ServiceX is consumed by POST ing a web request with the data in the request body specifically.
ServiceY is consumed by POST ing a web request with the data appended to the URL(Yes...I know this should be a GET, but I didn't write the external API, so don't lecture me about it.)
In order to avoid redundant, duplicate code, I have wrapped the web requests using the command pattern, and am using a factory to build the requests.
For ServiceX, the data needs to be encoded and put into the request body, as oppose to ServiceY where the data needs to be iterated over and placed on the Post string.
I have a class structure like the following:
public abstract class PostCommandFactory
{
public ICommand CreateCommand();
}
public class UrlPostCommandFactory:PostCommandFactory
{
public ICommand CreateCommand()
{
//Initialize Command Object Here
}
}
public class BodyPostCommandFactory:PostCommandFactory
{
public ICommand CreateCommand()
{
//Initialize Command Object Here
}
}
public interface ICommand
{
string Invoke();
}
public class UrlPostCommand:ICommand
{
public string Invoke()
{
//Make URL Post Request
}
}
public class BodyPostCommand:ICommand
{
public string Invoke()
{
//Make Request Body Post Request
}
}
This allows me to cleanly separate the way that I am binding data to the request when they need to be send out, and essentially, I can also add additional classes to handle GET requests. I am not sure if this is a good use of these patterns. I am thinking an alternative might be using the Strategy pattern and specifying strategy objects for the different Request methods I might need to use. Such as the following:
public class RequestBodyPostStrategy:IPostStrategy
{
public string Invoke()
{
//Make Request Body POST here
}
}
public class UrlPostStrategy:IPostStrategy
{
public string Invoke()
{
//Make URL POST here
}
}
public interface IPostStrategy
{
string Invoke();
}
public class PostContext
{
pubic List<IPostStrategy> _strategies;
public IPostStrategy _strategy;
public PostContext()
{
_strategies = new List<IPostStrategy>();
}
public void AddStrategy(IPostStrategy strategy)
{
_strategies.Add(strategy);
}
public void SetStrategy(IPostStrategy strategy)
{
_strategy = strategy;
}
public void Execute()
{
_strategy.Invoke();
}
}
I am starting to think the Strategy pattern may be the cleaner solution.
Any thoughts?
I would use both.
Command is best practice for encapsulating requests and hiding implementation details. You should probably use it even if you only have one kind of request, as it promotes cleaner code. Essentially it's good practice to consider "what is the absolute minimum the rest of my code needs to know about how requests are executed and handled", which will lead you to the Command pattern.
Strategy is basically configuring your system at runtime with a general, consitent way to handle some ascpect of the operation, in this case generating the requests. This is also a good practice for testing, as you can substitute a test implementation of your strategy/request factory to fake actual connections etc.
Based on the examples for Command and Strategy that you have given, the Command pattern example looks exactly like a Strategy which I guess lead you to Strategy. I would also go with Strategy but would like to add that there's more to Command pattern than what you have included in the example. You should ask yourself questions like:
If this is the case then you should choose Command Pattern.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With