February 2008 Entries
My eldest, Gemma, is in her final year at school. The prelim exams have just finished and her year have organised a Ceilidh to celebrate. Here's a picture of Gemma before she left; quite the young lady. Apparently she'll be 17 soon; jeez, where'd those years go?
Developer Developer Developer Day Ireland (AKA Techprechaun I) is open for business! I'll be there giving my patterns talk, check out the agenda for more details.
Just to let you know, voting for Developer Day Scotland has now opened. If you are coming along and you'd like to see one of sessions I'm doing then you can vote for it using the badge on the sidebar.
Let's say you are designing a system and you have worked out what classes you are going to need; but you also realise that as time goes on you are going to have to release other "collaborator" objects for your system. You would like to release these new objects only, and not all the objects in the system, but how are you going to allow existing objects to handle these new "collaborator" objects, when you have no idea what these new objects will do at the time that you are designing the original objects? Yeah its a bummer isn't it?
Well, luckily there is a pattern to deal with this situation, it's called Double Dispatch. In this pattern the original object does not handle messages from its collaborating objects; instead it dispatches the message back to the collaborator along with a reference to itself. The "double dispatching" of a message allows collaborating objects to be added to a system without affecting the original objects.
Below is an example showing an Account object that has to handle a number of Transaction objects. To allow other Transaction objects to be added to the system, as and when required, without affecting the original Account object; the Account object uses "double dispatch" to dispatch the message back to the Transaction object for processing. Clever eh? :-)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DoubleDispatch
{
/// <summary>
/// I am an abstract class from which all transactions derive
/// </summary>
public abstract class Transaction
{
//GS - Ensure each derived class implements this method
public abstract void ExecuteWithAccount(Account theAccount);
}
/// <summary>
/// I represent a bank account
/// </summary>
public class Account
{
public double Balance { get; set; }
public void ExecuteTransaction(Transaction theTransaction)
{
//GS - I have no knowledge of how this transaction
//works, so dispatch the message back to the caller
theTransaction.ExecuteWithAccount(this);
}
}
/// <summary>
/// I am a debit transaction, I debit an account
/// balance by my value.
/// </summary>
public class DebitTransaction : Transaction
{
public double Value { get; set; }
/// <summary>
/// GS - Handle the double dispatched message
/// </summary>
/// <param name="anAccount"></param>
public override void ExecuteWithAccount(Account anAccount)
{
//GS - Show the user the message has been
//double dispatched
Console.WriteLine(
"Message dispatched back to Debit Transaction.");
anAccount.Balance -= Value;
}
}
/// <summary>
/// I am a credit transaction, I credit an account
/// balance by my value.
/// </summary>
public class CreditTransaction : Transaction
{
public double Value { get; set; }
/// <summary>
/// GS - Handle the double dispatched message
/// </summary>
/// <param name="anAccount"></param>
public override void ExecuteWithAccount(Account anAccount)
{
//GS - Show the user the message has been
//double dispatched
Console.WriteLine(
"Message dispatched back to Credit Transaction.");
anAccount.Balance += Value;
}
}
class Program
{
static void Main(string[] args)
{
//GS - Let's test the pattern
//GS - Create an account and give it an opening balance
Account theAccount = new Account();
theAccount.Balance = 100.00;
//GS - Show the opening balance
Console.WriteLine("The opening balance is {0}",
theAccount.Balance);
//GS - Create a credit transaction for 50 pounds
CreditTransaction cr = new CreditTransaction();
cr.Value = 50.00;
//GS - Apply the transaction to the account
Console.WriteLine("Account executing the transaction.");
theAccount.ExecuteTransaction(cr);
//GS - Show the new balance
Console.WriteLine("The new balance is {0}",
theAccount.Balance);
//GS - Create a debit transaction for 50 pounds
DebitTransaction dr = new DebitTransaction();
dr.Value = 50;
//GS - Apply the transaction
Console.WriteLine("Account executing the transaction.");
theAccount.ExecuteTransaction(dr);
//GS - Show the new balance
Console.WriteLine("The new balance is {0}",
theAccount.Balance);
//GS - Hang around so the user can see the console
Console.ReadLine();
}
}
}
A couple of days ago I posted that, although some developers use the terms Inversion of Control and Dependency Injection as if they are the same thing, they are in fact not the same. I said that Dependency Injection was a "kind of" Inversion of Control, it wasn't the only kind and they are not the same thing. I then went on to give an explanation of the Dependency Injection pattern.
Well it turns out that all the developers who thought they were the same thing all read my blog (hey, who knew?) and most of them felt the need to email me telling me I was wrong and challenging me to give an example of Inversion of Control that was not Dependency Injection.
Well okay, fair enough. The other kind of Inversion of Control is based on program control flow and to cut a long story short, it is basically the event based programming model. Here's an example.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace IOC2
{
class Program
{
static void Main(string[] args)
{
//GS - Get the User's name
Console.WriteLine("Please enter your name:");
string input = Console.ReadLine();
//GS - Say hello
Console.WriteLine("Hello " + input);
//GS - Wait so the user can see what was written
//to the console
Console.ReadLine();
}
}
}
In the above example the control flow of the program is very simple. A user is asked to input their name and then the program prints out a personalised greeting on the console. Now in Inversion of Control, in relation to control of flow; instead of the programmer specifying a series of events to happen they would register desired responses to particular events and then let some external entity (the user perhaps) take control of the order of these events. In other words, the programmer implements an event driven programming model, as in the following example.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace IOC3
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void btnGreeting_Click(object sender, EventArgs e)
{
//GS - Say hello
MessageBox.Show("Hello " + txtUserName.Text);
}
private void btnClose_Click(object sender, EventArgs e)
{
//GS - Close the window
Close();
}
}
}
In the example above, you can see that the flow of the program is now not obvious, as it was before. The user may write his name in the textbox, he may change it a bunch of times before pressing the "Greeting" button, or he may not press it at all, but instead press the "Close" button. There is now no way for the programmer to know what the control flow is going to be, the contol flow as "suffered" Inversion of Control.
As you can see, the above is an example of Inversion of Control that does not use Dependency Injection. I stand by what I said in my previous post. Dependency Injection is a "kind of " Inversion of Control, but it's not the only kind, and the two things are not the same.
Okay, in this post we are going to look at Dependency Injection. First thing you have to know (and the reason for the title of this post) is that Inversion of Control is not Dependency Injection. You'll hear developers mix the two terms as if they are the same thing, they're not, Dependency Injection is a "kind of" IoC, but its not the only kind and they are not the same thing.
Right, now we have that out of the way this post is actually about Dependency Injection; so what is DI? Well, normally, when an object has to consume a service, it is that object's responsibility to "know" how to gain access to that service. However, sometimes that is not always desirable; take for example, the instance where you wish to use a different service in test and in production, how will you handle that without code changes? Well, the answer is the DI pattern.
In the DI pattern the object consuming the service merely provides a property into which the required service is "injected" at runtime. In the example below, you can see that two services are created (each implementing the IService interface) and the required Service (specified in a config file) is "injected" into the Consumer at runtime. Using another service is just a case of changing the value in the config file. Try it out for yourself! :-)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Reflection;
namespace IOC
{
/// <summary>
/// GS - I am an intereface that describes a "service"
/// </summary>
public interface IService
{
void addNumbers(int first, int second);
}
/// <summary>
/// I am the service used for testing
/// </summary>
public class TestService : IService
{
#region IService Members
void IService.addNumbers(int first, int second)
{
Console.WriteLine("I am the test service");
int sum = first + second;
Console.WriteLine("{0} + {1} = {2}",first,second,sum);
}
#endregion
}
/// <summary>
/// GS - I am the service used in production
/// </summary>
public class ProductionService : IService
{
#region IService Members
void IService.addNumbers(int first, int second)
{
Console.WriteLine("I am the production service");
int sum = first + second;
Console.WriteLine("{0} + {1} = {2}", first,second,sum);
}
#endregion
}
/// <summary>
/// GS - I am the Consumer and I consume the provided Service
/// </summary>
public class Consumer
{
private int _first;
private int _second;
public Consumer(int first, int second)
{
_first = first;
_second = second;
}
public IService Service { get; set; }
public void consumeService()
{
if (Service == null)
{
throw new Exception("The service has not been set!");
}
Console.WriteLine("Consuming service...");
Service.addNumbers(_first, _second);
}
}
/// <summary>
/// GS - Let's test the pattern
/// </summary>
public class Program
{
static void Main(string[] args)
{
//GS - Create a consumer
Consumer aConsumer = new Consumer(3,7);
//GS - Set the service based on configuration
//GS - Get the type of the required Service
Type ServiceType = Type.GetType(
ConfigurationSettings.AppSettings["ServiceName"]);
//GS - Get the constructor for this Service
ConstructorInfo ci =
ServiceType.GetConstructor(Type.EmptyTypes);
//GS - Instantiate the Service
IService theService = ci.Invoke(null) as IService;
//GS - Inject the Service into the Consumer
aConsumer.Service = theService;
//GS - Consume the service
aConsumer.consumeService();
//GS - Hang around so the user can see what's
//written to the console
Console.Read();
}
}
}
Sorry it's been so quiet here of late, but I've been off sick (you all better be saying "awww" right now) but I'm back so normal service will be resumed shortly.
|