Monday, April 30, 2007


DevDefined Ltd. have been working with Screen Architect Ltd and Catch Limited to produce a UI Prototyping add-in for Enterprise Architect (EA) - and we are pleased to see that they made their first public release, with the product now on sale from their website.

For anyone who uses Enterprise Architect, and already does UI modeling within EA, or UI prototyping with another application (or wishes to) - then why not take a look at the Screen Architect’s flagship Add-in (there's a 30 day trial available).
posted @ Sunday, April 29, 2007 10:12:03 PM (New Zealand Standard Time, UTC+12:00)    Comments [0] | Trackback |
 Sunday, April 29, 2007
14_startable_facility.png

So this time we're going to have a look at the startable facility (you can find more info on it here in the wiki) - the concept of the startable facility is to ensure that components are started as soon as possible, which translates to as soon as all dependencies are available. 

Effectively what happens is you create a component, and implement a special interface called IStartable, which looks like this and is found in the Castle.Core assembly.

/// <summary>

/// Interface for components that wish to be started by the container

/// </summary>

public interface IStartable

{

    /// <summary>

    /// Starts this instance.

    /// </summary>

    void Start();

 

    /// <summary>

    /// Stops this instance.

    /// </summary>

    void Stop();

}


And then the startable facility detects any components registered in the container which implement the IStartable interface and determines if they have any dependencies which still need to be satisfied, if not, then the container will Resolve the component, and start it immediately (by invoking the IStartable.Start method on it) otherwise it's added to a list of services awaiting startup.

From then on, as components are registered in the container the startable facility will check the list of services awating startup, to see if any of them have all their dependencies available now, if so it will then Resolve the service and again invoke IStartable.Start on it.

Now at this point it's worth noting that Start is not executed asynchronously - if you block within the Start method then your container will halt loading the configuration etc. till it completes... be careful not to slow down your container startup too much with slow blocking operations with the Start() method.

So let's get into some code... first off I'm going to create a simple http listening service which is capable of serving up a single file... exciting stuff!

Here's the code:

public class LameHttpFileServer : IStartable

{

    private string _path;

    private HttpListener _listener;

    private Thread _listenerThread;

    private ILogger _logger;

 

    public LameHttpFileServer(string prefix, string path)

    {

        _path = path;

        _listener = new HttpListener();

        _listener.Prefixes.Add(prefix);

    }

 

    public ILogger Logger

    {

        get

        {

            if (_logger == null) _logger = NullLogger.Instance;

            return _logger;

        }

        set { _logger = value; }

    }

 

    public void Start()

    {

        Logger.Debug("Starting LameWebService...");

        _listener.Start();

        _listenerThread = new Thread(RequestWorker);

        _listenerThread.Start();

        Logger.Info("LameWebService Started.");

    }

 

    public void Stop()

    {

        Logger.Debug("Stopping LameWebService...");

        _listenerThread.Abort();

        _listener.Stop();

        Logger.Info("Stopped LameWebService.");

    }

 

    private void RequestWorker()

    {

        while (true)

        {

            HttpListenerContext context = null;

 

            try

            {

                context = _listener.GetContext();

            }

            catch (ObjectDisposedException)

            {

                return;

            }

 

            ServeContent(context.Response, _path);

        }

    }

 

    private void ServeContent(HttpListenerResponse response, string path)

    {

        string content = File.ReadAllText(path);

        byte[] buffer = Encoding.UTF8.GetBytes(content);

        response.ContentLength64 = buffer.Length;

 

        Stream output = response.OutputStream;

        output.Write(buffer, 0, buffer.Length);

        output.Close();

    }

}


Now, this class doesn't have any constructor dependencies (beyond a little configuration) but we do have a Setter dependency on an ILogger service - ILogger is actually a generic abstract of a logger for a logging framework (a logger being a named/scoped class supporting a number of generic message writing methods) out of the box Castle provides support for both nlog and log4net, however it also contains some lightweight loggers, such as a console logger, which means you can avoid having to write any logging configuration files... so we're going to use that this time... because the loggers are assigned by the container, there's not need to actually reference the type the logger is logging "for", because the container knows that implicitly.

Now let's have a look at the container configuration:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <facilities>

      <facility id="startable.facility"

        type="Castle.Facilities.Startable.StartableFacility, Castle.MicroKernel" />

 

      <facility id="logging.facility"

        type="Castle.Facilities.Logging.LoggingFacility, Castle.Facilities.Logging"

        loggingApi="console" />                         

 

  </facilities>

    <components>

 

      <component id="serves->lame.html"

                type="IoC.Tutorials.Part14.LameHttpFileServer, IoC.Tutorials.Part14">

        <parameters>

          <prefix>http://*:8089/</prefix>

          <path>lame.html</path>

        </parameters>

      </component>

 

    </components>

  </castle>

 

</configuration>


As you can see we have declared two facilities, one for handling the auto-wiring of Loggers into any components with ILogger constructor parameters or setter dependencies, and secondly the startable facility, which will take care of automatically starting our LameHttpFileServer for us.

Now, notice we're referencing a file called lame.html - heres what that looks like:

<html>

    <body>

        <h1>Lame.html</h1>

        <h2>Welcome to lame.html file</h2>

        <p>This is the contents of the lame.html file, neat huh?</p>

    </body>

</html>


Now done to business, let's look at the program that's using this server...

private static void Main(string[] args)

{

    WindsorContainer container = new WindsorContainer(new XmlInterpreter());

 

    GetPage();

 

    container.Dispose();

 

    GetPage();

 

    Console.Read();

}

 

private static void GetPage()

{           

    Console.WriteLine("\r\nClient: requesting http://localhost:8089/ ...");

    try

    {

        WebClient client = new WebClient();

        string content = client.DownloadString("http://localhost:8089/");

        Console.WriteLine("Client: success, content follows.\r\n\r\n {0}", content);

    }

    catch (WebException ex)

    {

        Console.WriteLine("Client: Exception occured, message: {0}", ex.Message);

    }

}


As you can see what we're doing is:

  1. Constructing the container using the configuration.
  2. Calling the GetPage method, which will attempt to download the lame.html file from our server (which the container should have started automatically for us)
  3. Disposing of the container (any guesses what that will do?)
  4. And finally calling the GetPage method again, to see if our server is still running.
So, running the program gives us these results:

[Debug] 'IoC.Tutorials.Part14.LameHttpFileServer' Starting LameWebService...
[Info] 'IoC.Tutorials.Part14.LameHttpFileServer' LameWebService Started.

Client: requesting http://localhost:8089/ ...
Client: success, content follows.

 <html>
    <body>
        <h1>Lame.html</h1>
        <h2>Welcome to lame.html file</h2>
        <p>This is the contents of the lame.html file, neat huh?</p>
    </body>
</html>

[Debug] 'IoC.Tutorials.Part14.LameHttpFileServer' Stopping LameWebService...
[Info] 'IoC.Tutorials.Part14.LameHttpFileServer' Stopped LameWebService.

Client: requesting http://localhost:8089/ ...
Client: Exception occurred, message: Unable to connect to the remote server

As you can see before we disposed of the container our request for the file worked just fine, but after the container had been disposed, it appears the file service has been stopped, and so our client was unable to connect... what caused this?

Well the way the startable service is implemented, it actually applies a couple of lifecycle concerns - a concern is effectively a bit of code that executes when what it's concerned in occurs, in these case the startable facility adds two concerns to a startable component's model.

The first "concern" is a commission concern, which invokes the Start method upon the component entering the commission step of it's lifecycle, which is immediately after the component instance has been constructed and all it's dependencies and configuration injected. 

The second concern is a "decommission" concern, which occurs upon the component is being decommissioned (ie. disposed of, released or purged from the container) this happens to all components the container is holding onto when it is disposed, or when an individual component is released - however in the case of components with a singleton lifestyle, you can't actually release the component prior to disposing of the container (because that would violate the lifestyle) ... so doing something like this:

LameHttpFileServer server = container.Resolve<LameHttpFileServer>();

container.Release(server);


In place of disposing the container won't stop the service if it's a singleton....

One last though - what would happen if we changed our components lifestyle to [Transient] instead of the default of singleton, let's create a simple class for testing this idea:

[Transient]

public class StartableExperiment : IStartable

{

    private static int _count;

    private int _number;

 

    public StartableExperiment()

    {

        _number = ++_count;

    }

 

    public void Start()

    {

        Console.WriteLine("Started #{0}", _number);

    }

 

    public void Stop()

    {

        Console.WriteLine("Stopped #{0}", _number);

    }

}


Now let's write some code to test it... we'll just register the component in code, to avoid having to change our config...

Here's the program itself...

WindsorContainer container = new WindsorContainer(new XmlInterpreter());

 

container.AddComponent("exp", typeof(StartableExperiment));

 

StartableExperiment exp1 = container.Resolve<StartableExperiment>();

container.Release(exp1);

StartableExperiment exp2 = container.Resolve<StartableExperiment>();

container.Release(exp2);

 

container.Dispose();


And the results:

Started #1
Started #2
Stopped #2
Started #3
Stopped #3
Stopped #1


So what's happened is that immediately upon registration a component is created... and can't be disposed (because we can't get a hold of the instance to release it) until the container is disposed... but for the couple of calls to Resolve and Release you can see the StartableExperiment instances are started & stopped during commission & decommission steps in the components life cycle as expected.

So that brings this look at the startable facility to an end... next time we might have another quick look at life cycles to see how else we can use this concept when build components, and maybe after that we'll have a look at some of other lifestyles which are available "out of the box" and how to write our own.
posted @ Sunday, April 29, 2007 4:51:25 AM (New Zealand Standard Time, UTC+12:00)    Comments [3] | Trackback |
 Saturday, April 28, 2007
Something non-tech related, but I only just noticed wises has revamped their website, lets hope it's not so arse this time ;o)

posted @ Saturday, April 28, 2007 4:11:19 AM (New Zealand Standard Time, UTC+12:00)    Comments [2] | Trackback |
13_injecting_service_arrays.png
So this time we're going to look at injecting service arrays ... if you haven't read part 12, it might pay to have a look at that first - this part follows on from the examples presented there.

So this should be reasonably quick, we're just going to rework the Part 12 solution so we don't need decorators... why would we do this, well in this case I feel that rewiring decorators (ie. changing orders, or disabling one of the decorators temporarily) isn't completely intuitive, especially when you have a long chain, it's easy to make mistakes.

So what's the plan, well first off let's implement an abstract class for our calculators:

public abstract class AbstractCalculator

{

    public abstract decimal Calculate(decimal currentTotal, Order order);

}


Now we can rework the existing decorators and default calculator to be implementations of the abstract caculator, so here goes:

First off the the logic in the DefaultCostCalculator will be moved into a class called TotalCalculator :

public class TotalCalculator : AbstractCalculator

{

    private decimal CalculateTotal(Order order)

    {

        decimal total = 0;

 

        foreach (OrderItem item in order.Items)

        {

            total += (item.Quantity*item.CostPerItem);

        }

 

        return total;

    }

 

    public override decimal Calculate(decimal currentTotal, Order order)

    {

        return currentTotal + CalculateTotal(order);

    }

}


And then for the gst calculator, we do much the same thing:

public class GstCalculator : AbstractCalculator

{

    private decimal _gstRate = 1.125m;

 

    public decimal GstRate

    {

        get { return _gstRate; }

        set { _gstRate = value; }

    }

 

    private bool IsNewZealand(Order order)

    {

        return (order.CountryCode == "NZ");

    }

 

    public override decimal Calculate(decimal currentTotal, Order order)

    {

        if (IsNewZealand(order))

        {

            return (currentTotal*_gstRate);

        }

 

        return currentTotal;

    }

}


And finally the shipping calculator:

public class ShippingCalculator : AbstractCalculator

{

    private decimal _shippingCost = 5.0m;

    private decimal _fragileShippingPremium = 1.5m;

 

    public decimal ShippingCost

    {

        get { return _shippingCost; }

        set { _shippingCost = value; }

    }

 

    public decimal FragileShippingPremium

    {

        get { return _fragileShippingPremium; }

        set { _fragileShippingPremium = value; }

    }

 

    private decimal GetShippingTotal(Order order)

    {

        decimal shippingTotal = 0;

 

        foreach (OrderItem item in order.Items)

        {

            decimal itemShippingCost = ShippingCost*item.Quantity;

            if (item.IsFragile) itemShippingCost *= FragileShippingPremium;

            shippingTotal += itemShippingCost;

        }

 

        return shippingTotal;

    }

 

    public override decimal Calculate(decimal currentTotal, Order order)

    {

        return currentTotal + GetShippingTotal(order);

    }

}


Now, our original DefaultCostCalculator is reworked to use an array of AbstractCalculators to compute the total cost for an order:

public class DefaultCostCalculator : ICostCalculator

{

    private AbstractCalculator[] _calculators;

 

    public DefaultCostCalculator(AbstractCalculator[] calculators)

    {

        _calculators = calculators;

    }

 

    public decimal CalculateTotal(Order order)

    {

        decimal currentTotal = 0;

 

        foreach (AbstractCalculator calculator in _calculators)

        {

            currentTotal = calculator.Calculate(currentTotal, order);

        }

 

        return currentTotal;

    }

}


Notice it takes an array of AbstractCalculator's as a constructor dependency... just like we did for configuration parameters in the early parts of this container tutorial series, we can actually pass arrays of dependencies to a component as well, so last of all let's see the container configuration:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <components>

 

      <component id="calc.gst"

                service="IoC.Tutorials.Part13.AbstractCalculator, IoC.Tutorials.Part13"

                type="IoC.Tutorials.Part13.GstCalculator, IoC.Tutorials.Part13">

        <parameters>

          <GstRate>1.20</GstRate>

          <!-- GST is now 20% -->

        </parameters>

      </component>

 

      <component id="calc.shipping"

          service="IoC.Tutorials.Part13.AbstractCalculator, IoC.Tutorials.Part13"

          type="IoC.Tutorials.Part13.ShippingCalculator, IoC.Tutorials.Part13">

        <parameters>

          <FragileShippingPremium>0.0</FragileShippingPremium>

          <!-- No fragile goods premium any more-->

        </parameters>

      </component>

 

      <component id="calc.total"

        service="IoC.Tutorials.Part13.AbstractCalculator, IoC.Tutorials.Part13"

          type="IoC.Tutorials.Part13.TotalCalculator, IoC.Tutorials.Part13" />

 

      <component id="costCalculator.default"

                service="IoC.Tutorials.Part13.ICostCalculator, IoC.Tutorials.Part13"

                type="IoC.Tutorials.Part13.DefaultCostCalculator, IoC.Tutorials.Part13">

        <parameters>

          <calculators>

            <array>

              <value>${calc.total}</value>

              <value>${calc.shipping}</value>

              <value>${calc.gst}</value>

            </array>

          </calculators>

        </parameters>

      </component>

 

    </components>

  </castle>

 

</configuration>


Notice the default caculator now takes our 3 calculators as members of an array, and if we want to change the order in which they are evaluated, it's trivial to just move them around within the <array /> node, or to comment certain ones out.

And just to demonstrate that nothing has changed, the code for the program in part 13 is identical to part 12:

private static void Main(string[] args)

{

    WindsorContainer container = new WindsorContainer(new XmlInterpreter());

 

    Order order1 = new Order();

    order1.CountryCode = "NZ";

    order1.Items.Add(new OrderItem("water", 10, 1.0m, false));

    order1.Items.Add(new OrderItem("glass", 5, 20.0m, true));

 

    Order order2 = new Order();

    order2.CountryCode = "US";

    order2.Items.Add(new OrderItem("sand", 50, 0.2m, false));

 

    ICostCalculator costCalculator = container.Resolve<ICostCalculator>();

    Console.WriteLine("Cost to deliver Order 1: {0}", costCalculator.CalculateTotal(order1));

    Console.WriteLine("Cost to deliver Order 2: {0}", costCalculator.CalculateTotal(order2));

 

    Console.Read();

}


And the results are the same too:

Cost to deliver Order 1: 192.0000
Cost to deliver Order 2: 260.0


Beautiful :)

The next part will have a quick look at the "Startable" facility, enjoy.





posted @ Saturday, April 28, 2007 1:52:38 AM (New Zealand Standard Time, UTC+12:00)    Comments [0] | Trackback |
12_decorators.png
So... Decorators (for those who don't know, or don't remember) are a classic design pattern - have a look at the wikipedia entry if your curious on doing any background reading.

Up until now we've been creating abstractions and swapping implementations, but here we are going to chain implementations... so for this example we have an Order class which contains a bunch of order items, here's how that looks:

Order.cs

public class Order

{

    private string _countryCode;

    private readonly List<OrderItem> _items = new List<OrderItem>();

 

    public List<OrderItem> Items

    {

        get { return _items; }

    }

 

    public string CountryCode

    {

        get { return _countryCode; }

        set { _countryCode = value; }

    }

}


OrderItem.cs

public class OrderItem

{

    private string _name;

    private bool _isFragile;

    private int _quantity;

    private decimal _costPerItem;

 

    public OrderItem(string name, int quantity, decimal costPerItem, bool isFragile)

    {

        _name = name;

        _quantity = quantity;

        _costPerItem = costPerItem;

        _isFragile = isFragile;

    }

 

    public bool IsFragile

    {

        get { return _isFragile; }

        set { _isFragile = value; }

    }

 

    public int Quantity

    {

        get { return _quantity; }

        set { _quantity = value; }

    }

 

    public decimal CostPerItem

    {

        get { return _costPerItem; }

        set { _costPerItem = value; }

    }

 

    public string Name

    {

        get { return _name; }

        set { _name = value; }

    }

}


And now we have an interface that a class can implement to calculate the cost of an order:

public interface ICostCalculator

{

    decimal CalculateTotal(Order order);

}


And last of all we have an implementation of the cost calculator, which calculates the base cost of an order based on quantities and per-item costs:

public class DefaultCostCalculator : ICostCalculator

{

    public decimal CalculateTotal(Order order)

    {

        decimal total = 0;

 

        foreach (OrderItem item in order.Items)

        {

            total += (item.Quantity*item.CostPerItem);

        }

 

        return total;

    }

}


Pretty simple, so what about the program itself... well here it is, we just have 2 different orders we are calculating the total cost for:

internal class Program

{

    private static void Main(string[] args)

    {

        WindsorContainer container = new WindsorContainer(new XmlInterpreter());

 

        Order order1 = new Order();

        order1.CountryCode = "NZ";

        order1.Items.Add(new OrderItem("water", 10, 1.0m, false));

        order1.Items.Add(new OrderItem("glass", 5, 20.0m, true));

 

        Order order2 = new Order();

        order2.CountryCode = "US";

        order2.Items.Add(new OrderItem("sand", 50, 0.2m, false));

 

        ICostCalculator costCalculator = container.Resolve<ICostCalculator>();

        Console.WriteLine("Cost to deliver Order 1: {0}", costCalculator.CalculateTotal(order1));

        Console.WriteLine("Cost to deliver Order 2: {0}", costCalculator.CalculateTotal(order2));

 

        Console.Read();

    }

}


Our configuration (predictably) just had the default implementation of the cost calculator registered:

<component id="costCalculator.default"

          service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

          type="IoC.Tutorials.Part12.DefaultCostCalculator, IoC.Tutorials.Part12" />


Now lets see the output of running it:

Cost to deliver Order 1: 110.0
Cost to deliver Order 2: 10.0

So that seems OK... But I live in New Zealand, and in New Zealand we have GST (Goods and Services Tax) which means that I need to add another 12.5% to the total value of an order for NZ customers... so I could modify the default shipping calculator... but that's going to get ugly fast as more requirements come in (and more configuration needs to be exposed) - so instead I'll create a decorator for the cost calculator, just for doing GST:

public class GstCostCalcualtorDecoarator : ICostCalculator

{

    private readonly ICostCalculator _innerCalculator;

    private decimal _gstRate = 1.125m;

 

    public GstCostCalcualtorDecoarator(ICostCalculator innerCalculator)

    {

        _innerCalculator = innerCalculator;

    }

 

    public decimal GstRate

    {

        get { return _gstRate; }

        set { _gstRate = value; }

    }

 

    private bool IsNewZealand(Order order)

    {

        return (order.CountryCode == "NZ");

    }

 

    public decimal CalculateTotal(Order order)

    {

        decimal innerTotal = _innerCalculator.CalculateTotal(order);

 

        if (IsNewZealand(order))

        {

            innerTotal = (innerTotal*_gstRate);

        }

 

        return innerTotal;

    }

}


Notice it's dependency on ICostCalculator, and that it actually invokes the inner calculator to get a total and then "decorates" the total (by adding the 12.5% when the country for the order is New Zealand).

Now we can actually wire this up in our container without changing any of our application code... let's look at that:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <components>

 

      <component id="costCalculator.gstDecorator"

                service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

                type="IoC.Tutorials.Part12.GstCostCalcualtorDecoarator, IoC.Tutorials.Part12">

        <parameters>

          <innerCalculator>${costCalculator.default}</innerCalculator>

        </parameters>

      </component>

 

      <component id="costCalculator.default"

                service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

                type="IoC.Tutorials.Part12.DefaultCostCalculator, IoC.Tutorials.Part12" />

 

    </components>

  </castle>

 

</configuration>


Notice that the GSTdecorator is registered first (so it will be the default implementation of the ICostCalculator service) and it's inner calculator is wired to our default cost calculator... but proof is in the pudding, lets see what the results of running the program are now:

Cost to deliver Order 1: 123.7500
Cost to deliver Order 2: 10.0


Great, so our New Zealand order is now ($110 * 1.125 = $123.75) - but our United states order is still at the original $10 dollars... but we forgot to calculate shipping... so let's add another decorator.

public class ShippingCostCalculatorDecorator : ICostCalculator

{

    private readonly ICostCalculator _innerCalculator;

    private decimal _shippingCost = 5.0m;

    private decimal _fragileShippingPremium = 1.5m;

 

    public ShippingCostCalculatorDecorator(ICostCalculator innerCalculator)

    {

        _innerCalculator = innerCalculator;

    }

 

    public decimal ShippingCost

    {

        get { return _shippingCost; }

        set { _shippingCost = value; }

    }

 

    public decimal FragileShippingPremium

    {

        get { return _fragileShippingPremium; }

        set { _fragileShippingPremium = value; }

    }

 

    public decimal CalculateTotal(Order order)

    {

        decimal innerTotal = _innerCalculator.CalculateTotal(order);

        return innerTotal + GetShippingTotal(order);

    }

 

    private decimal GetShippingTotal(Order order)

    {

        decimal shippingTotal = 0;

 

        foreach (OrderItem item in order.Items)

        {

            decimal itemShippingCost = ShippingCost*item.Quantity;

            if (item.IsFragile) itemShippingCost *= FragileShippingPremium;

            shippingTotal += itemShippingCost;

        }

 

        return shippingTotal;

    }

}


And of course, we need to wire that up as well:

<configuration>

 

  <configSections>

    <section name="castle"

        type="Castle.Windsor.Configuration.AppDomain.CastleSectionHandler, Castle.Windsor" />

  </configSections>

 

  <castle>

    <components>

 

      <component id="costCalculator.shippingDecorator"

          service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

          type="IoC.Tutorials.Part12.ShippingCostCalculatorDecorator, IoC.Tutorials.Part12">

        <parameters>

          <innerCalculator>${costCalculator.gstDecorator}</innerCalculator>

        </parameters>

      </component>

 

      <component id="costCalculator.gstDecorator"

                service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

                type="IoC.Tutorials.Part12.GstCostCalcualtorDecoarator, IoC.Tutorials.Part12">

        <parameters>

          <innerCalculator>${costCalculator.default}</innerCalculator>

        </parameters>

      </component>

 

      <component id="costCalculator.default"

                service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

                type="IoC.Tutorials.Part12.DefaultCostCalculator, IoC.Tutorials.Part12" />

 

    </components>

  </castle>

 

</configuration>


Notice the chain, now we have Shipping -> GST -> Default Calculator .. so running the app again we can see the new totals:

Cost to deliver Order 1: 211.2500
Cost to deliver Order 2: 260.0


Now there's a small problem here... we're calculating GST before shipping, this actually should be the other way round... think about what you would have to do to change this in a program normally - ugh!  But of course decorators and IoC containers are a great fit, we'll just change the order of decorators, and while we're at it, lets make GST 20% and no longer charge a premium for shipping fragile goods... easy!

<components>

 

  <component id="costCalculator.gstDecorator"

            service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

            type="IoC.Tutorials.Part12.GstCostCalcualtorDecoarator, IoC.Tutorials.Part12">

    <parameters>

      <innerCalculator>${costCalculator.shippingDecorator}</innerCalculator>

      <GstRate>1.20</GstRate> <!-- GST is now 20% -->

    </parameters>

  </component>

 

  <component id="costCalculator.shippingDecorator"

      service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

      type="IoC.Tutorials.Part12.ShippingCostCalculatorDecorator, IoC.Tutorials.Part12">

    <parameters>

      <innerCalculator>${costCalculator.default}</innerCalculator>         

      <FragileShippingPremium>0.0</FragileShippingPremium> <!-- No fragile goods premium any more-->

    </parameters>

  </component>

 

  <component id="costCalculator.default"

            service="IoC.Tutorials.Part12.ICostCalculator, IoC.Tutorials.Part12"

            type="IoC.Tutorials.Part12.DefaultCostCalculator, IoC.Tutorials.Part12" />

 

</components>


And for anyone sad enough to be checking the results with a calculator (or with the human equivalent ;o) we'll run the program one last time:

Cost to deliver Order 1: 192.0000
Cost to deliver Order 2: 260.0


Now one thing to keep in mind is that so far we've been decorating return values... but you can also decorate methods with logic that occurs before invoking the inner service - a common example of this might be to validate something before it's committed to a repository (and perhaps throwing an exception if it's not OK) - and now suddenly you can turn validation on or off at the configuration level.

Next time we'll have a look at another way of writing this functionality without using decorators, stay tuned.
posted @ Saturday, April 28, 2007 12:27:50 AM (New Zealand Standard Time, UTC+12:00)    Comments [0] | Trackback |
 Wednesday, April 25, 2007
11_factories.png
So most all of us are familiar with factories, and at first glance it seems an IoC container removes the need for having to write our own... but this isn't quite the case - for the container to be able create a component it needs:
  • A publicly accessible constructor.
  • All dependencies to be registered in the container.
  • Mechanisms to allow for configuration and dependencies to be injected via constructors, or properties.
Which is fine for classes you own, but what about those that you don't - well you have a few options, but one of the easy options is to create a factory and get the container to use the factory for creating instances - to do this we'll be using a facility.

Facilities are "add-ins" for the container, they can be registered in the container, and augment what the container is capable of doing - there are a lot of facilities for the container, and many of them are documented (to varying degrees) on the castle project wiki ... we will be using a couple of different facilities in this part, first off will be the factory support facility.

So let's start off with a class that's supplied to us by a 3rd party... I'm just going to make one as an example, to keep things simple, called the Sms Service, for sending simple messages to cellular phones...

public class SmsService : ISmsService

{

    private SmsConfig _config;

 

    public void SetConfig(SmsConfig config)

    {

        _config = config;

    }

 

    public void SendMessage(string number, string message)

    {

        Console.WriteLine("SMS message: {0} sent to: {1} with account: {2}", message, number, _config.UserName);

    }

 

    public class SmsConfig

    {

        private string _userName;

        private string _password;

        private int _retryAttempts;

 

        internal string UserName

        {

            get { return _userName; }

        }

 

        internal string Password

        {

            get { return _password; }

        }

 

        public int RetryAttempts

        {

            get { return _retryAttempts; }

            set { _retryAttempts = value; }

        }

 

        public void SetCredentials(string userName, string password)

        {

            _userName = userName;

            _password = password;

        }

    }

}


It also has a corresponding interface:

public interface ISmsService

{

    void SendMessage(string number, string message);

}


Now the point is that this clas