Updated: See end of question for how I implemented the solution.
Sorry for the poorly-worded question, but I wasn\'t sure how best to ask it. I\'m not sure
There are two very simple approaches:
Make OutboxManager
an abstract class, and provide a subclass per vendor. The SendMessagesToVendor
can be marked abstract, forcing it to be reimplemented by each vendor. This approach is simple, fits OO principles well, and also has the advantage of allowing you to supply the implementation for the other methods, but still allowing overriding for a vendor specific version if you want to allow that later.
Have OutboxManager
encapsulate some other class or interface which provides the vendor-specific information required in SendMessagesToVendor
. This could easily be a small interface that is implemented per-vendor, and SendMessagesToVendor
could use this interface implementation to send its messages. This has the advantage of allowing you to write some of the code here - potentially reducing duplication across vendors. It also potentially allows your SendMessagesToVendor
method to be more consistent, and more easily testable, since you only have to rely on the specific vendor functionality required here. This could also, potentially, be implemented as a delegate passed in as a related (but slightly different) approach (I personally prefer an interface to be implemented over a delegate, however).
It looks to me like you're most of the way there.
Some basic steps:
1 Figure out what parts of your code are the same no matter what the vendor.
2 Write those into a re-usable module (probably a .dll)
3 Determine what changes per vendor.
4 Determine what (of the above) is code - write specific modules for that.
5 Determine what (of the above) is configuration - create a config scheme for those.
Your .exe will then acually call the appropriate OutboxManager
object for the correct vendor.
If you make this an abstract base class so it has to be inherited you can force this method to be implemented in the concrete object.
using System;
using System.Collections.Generic;
public abstract class OutboxManagerBase
{
private List<string> _OutboxMsgs;
public DistributeOutboxMessages()
{
try {
RetrieveMessages();
SendMessagesToVendor();
MarkMessagesAsProcessed();
}
catch Exception ex {
LogErrorMessageInDb(ex);
}
}
private void RetrieveMessages()
{
//retrieve messages from the database; poplate _OutboxMsgs.
//This code stays the same in each implementation.
}
protected abstract void SendMessagesToVendor();
private void MarkMessagesAsProcessed()
{
//If SendMessageToVendor() worked, run this method to update this db.
//This code stays the same in each implementation.
}
private void LogErrorMessageInDb(Exception ex)
{
//This code writes an error message to the database
//This code stays the same in each implementation.
}
}
public class OutBoxImp1 : OutboxManagerBase
{
protected override void SendMessagesToVendor()
{
throw new NotImplementedException();
}
}
I would say use Dependecy Injection. Basically, you pass an abstraction of the send method.
Something like:
interface IVendorMessageSender
{
void SendMessage(Vendor v);
}
public class OutboxManager
{
IVendorMessageSender _sender;
public OutboxManager(IVendorMessageSender sender)
{
this._sender = sender; //Use it in other methods to call the concrete implementation
}
...
}
Another approach, as already mentioned, inheritance.
In either case: try to remove DB retrieval code from this class. Use another abstraction for that (ie: passing an IDataProvider interface or something like that to the constructor). It will make your code more testable.
Ditto to Mr Copsey. Manifest solution #1 is indeed sub-classing. You have, whether by luck or skill, already structured your code to make this easy to implement.
Depending on the nature of the differences between vendors, if there is a lot of common functionality, another alternative might be to have a database with a record for each vendor, and have a couple of flags that control processing. If you can break it down to "if flag1 is true do thing A else do thing B; always do thing C; if flag2 is true do thing D else we're done", then rather than repeating a bunch of code across vendors you may be able to let data control the processing.
Oh, and I might add the perhaps obvious: If the only difference is data values, then of course just store the data values somewhere. Like to take a trivial example, if the only difference between vendors is the domain name that you connect to, then just create a table with vendorid and domain name, read the value and plug it in.
One way you could do it is through the use of interfaces.
public interface IVendorSender
{
IEnumerable<OutboxMsg> GetMessages();
}
Then in your constructor take an instance as a parameter.
public class OutboxManager
{
private readonly IVendorSender _vendorSender;
public OutboxManager(IVendorSender vendorSender)
{
_vendorSender = vendorSender ?? new DefaultSender();
}
private void SendMessagesToVendor() // <== THIS CODE CHANGES EACH IMPLEMENTATION
{
_vendorSender.GetMessages(); // Do stuff...
}
}