Careful application of abstraction can provide a much cleaner approach to solving many of the structural issues we face in our coding every day. While the combination of words like 'abstraction' and 'cleaner' may seem like opposite concepts, once you get used to thinking in terms of abstracting 'similar but slightly different' modules using a single abstracted interface, it soon becomes second nature and a very powerful approach. By abstracting your various similar modules using a common interface, you empower yourself to write a SINGLE set of code that can operate with all of the various modules in an identical manner. While this sounds simple enough, in practice we all tend to see a lot of cut and paste approaches to this common problem, where you find variations of logic within the same blocks of code using many case like statements, each of which takes care of a slightly different aspect of your various module's needs. This type of code is a prime contributor to increasing pasta levels and global warming.
Possible scenario's might include:
- Creating a program that must operate using several different databases, depending on customer needs.
- Creating a transmission program that can utilize several different protocols, or even flat-file output.
- Creating a Payment managment program that must interface with various Payment Processors.
- Creating a 'plug-in' architecture for a new program where additional modules can be added at run-time to do new things.
- and hundreds more..
I'm sure if you think back through your experiences, you will find some examples of where you see entirely seperate programs being developed, that all do basically the very same thing, but they all operate using perhaps a different database, or perhaps a different set of input files, or output methods. It actually occurs quite often, and forces developers to make the very same fixes or enhancements in multiple code-bases rather than just one. For our example, we're going to show the basic structure of what I call a message transmitter pump. This beast basically takes a message and sends it out using a protocol chosen by the user. The main program and TransmitPump class deal strictly with an interface and have no knowledge of the underlying workings of any specific transmitter, which is the point of the exercize.
Using the Code
In the example below, you can see a common interface (ITransmit) which is supported by any transmitter we choose to implement. The code in Main() or in the TransmitPump class has no knowledge of the inner workings of any specific transmitter module, which means we have effectively abstracted those details away from the main logic of our program.
public interface ITransmit
void Transmit(SomeMessageType message);
public class TCPIP_Transmitter : ITransmit
public class FTP_Transmitter : ITransmit
public class TransmitPump
private ITransmit m_Transmitter = null;
public TransmitPump(ITransmit Transmitter)
m_Transmitter = Transmitter;
public void SendMessage(SomeMessageType Message)
if (m_Transmitter != null)
TransmitPump tp = new TransmitPump(new TCPIP_Transmitter());
SomeMessageType message = new SomeMessageType(....);
Points of Interest
This is a very simple example, but it demonstrates the pattern effectively. For simple programs this may seem like overkill, but don't be fooled: simple programs have a tendency to grow over time. As your needs and code get more complex (and it always does), using this pattern always tends to yield far better returns in terms of structural simplicity in your upper-level code than taking a hard-coded "case statement" type of approach to dealing with different behaviors.