designdev design_patterns, csharp

A few days ago I published a post discussing Factory Method pattern. This article is about the other factory design pattern: Abstract Factory.

Use case: Switching between configuration sources easily

Imagine in a C# application you accessed ConfigurationManager.AppSettings whenever you needed a value from the configuration. This would essentially be hardcoding the configuration source and it would be hard to change if you needed to switch to another configuration source (database, web service, etc). A nicer way would be to “outsource” the creation of configuration source to another class.

What is Abstract Factory?

Here’s the official definition from GoF:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes.

Implementation

The application first composes the main class (ArticleFeedGenerator) with the services it will use and starts the process.

static void Main(string[] args)
{
    IConfigurationFactory configFactory = new AppConfigConfigurationFactory();

    IApiSettings apiSettings = configFactory.GetApiSettings();
    IFeedSettings feedSettings = configFactory.GetFeedSettings();
    IFeedServiceSettings feedServiceSettings = configFactory.GetFeedServiceSettings();
    IS3PublisherSettings s3PublishSettings = configFactory.GetS3PublisherSettings();
    IOfflineClientSettings offlineClientSettings = configFactory.GetOfflineClientSettings();

    var rareburgClient = new OfflineRareburgClient(offlineClientSettings);
    var rareburgArticleFeedService = new RareburgArticleFeedService(feedServiceSettings);
    var publishService = new S3PublishService(s3PublishSettings, feedSettings);
    var feedGenerator = new ArticleFeedGenerator(rareburgClient, rareburgArticleFeedService, publishService, feedSettings);

    feedGenerator.Run();
}

This version uses AppConfigConfigurationFactory to get the values from the App.config. When I need to switch to DynamoDB which I used in this example all I have to do is replace one line of code in the application:

var configFactory = new DynamoDBConfigurationFactory();

With this change alone we are essentially replacing a whole family of related classes.

On the factory floor

The abstract factory and the concrete factories implement it are shown below:

Concrete configuration factories create the classes that deal with specific configuration values (concrete products). For instance AppConfigConfigurationFactory looks like this (simplified for brevity):

public class AppConfigConfigurationFactory : IConfigurationFactory
{
    public IApiSettings GetApiSettings()
    {
        return new AppConfigApiSettings();
    }

    public IFeedServiceSettings GetFeedServiceSettings()
    {
        return new AppConfigFeedServiceSettings();
    }
}

Similarly, DynamoDBConfigurationFactory is responsible for creating concrete classes that access DynamoDB values:

public class DynamoDBConfigurationFactory : IConfigurationFactory
{
    protected Table _configTable;
    
    public DynamoDBConfigurationFactory()
    {
        AmazonDynamoDBClient dynmamoClient = new AmazonDynamoDBClient("accessKey", "secretKey", RegionEndpoint.EUWest1);
        _configTable = Table.LoadTable(dynmamoClient, "tableName");
    }
    
    public IApiSettings GetApiSettings()
    {
        return new DynamoDBApiSettings(_configTable);
    }

    public IFeedServiceSettings GetFeedServiceSettings()
    {
        return new DynamoDBFeedServiceSettings(_configTable);
    }
}

Notice all the “concrete products” implement the same “abstract product” interface and hence they are interchangable. With the product classes in the picture the diagram now looks like this:

Finally let’s have a look at the concrete objects that carry out the actual job. For example the IApiSettings exposes 2 string properties:

public interface IApiSettings
{
    string ApiKey { get; }
    string ApiEndPoint { get; }
}

If we want to read these values from App.config it’s very straightforward:

public class AppConfigApiSettings : IApiSettings
{
    public string ApiKey
    {
        get { return ConfigurationManager.AppSettings["Rareburg.ApiKey"]; }
    }

    public string ApiEndPoint
    {
        get { return ConfigurationManager.AppSettings["Rareburg.ApiEndPoint"]; }
    }
}

The DynamoDB version is fairly more complex but it makes no difference from the consumer’s point of view. Here GetValue is a method in the base class that returns the value from the encapsulated Amazon.DynamoDBv2.DocumentModel.Table object.

public class DynamoDBApiSettings : DynamoDBSettingsBase, IApiSettings
{
    public DynamoDBApiSettings(Table configTable)
        : base (configTable)
    {
    }

    public string ApiKey
    {
        get { return GetValue("Rareburg.ApiKey"); }
    }

    public string ApiEndPoint
    {
        get { return GetValue("Rareburg.ApiEndPoint"); }
    }
}

The concrete factory is responsible for creating the concrete classes it uses. So the client is completely oblivious to the classes such as DynamoDBApiSettings or AppConfigApiSettings. This means we can add a whole new set of configuration classes (i.e. a web service) and all we have to change in the client code will be one line where we instantiate the concrete factory.

This approach also allows us to be more flexible with the concerete class implementations. For example DynamoDB config class family requires a Table object in their constructors. To avoid code repetition I derived them all from a base class and moved the table to the base but the that doesn’t change anything in the client code.

Resources

designdev design_patterns, csharp

I recently developed a toy project called Rareburg article feed generator. It basically gets the articles from Rareburg.com (a marketplace for collectors) and creates an RSS feed. One challenge I had was picking the feed formatter class (RSS vs Atom). I utilised Factory Method design pattern to solve that and this post discusses the usage of the pattern over that use case.

Use case: Creating RSS feed formatter

I wanted my feed generator to support both RSS and Atom feeds based on the value specified in the configuration. In order to pass the XML validations they needed to modified a bit and I wanted these details hidden from the client code.

What is Factory Method?

Here’s the official definition from GoF:

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses

Factory Method pattern is one of the creational patterns which make it easy to separate object creation from the actual system. The actual business logic doesn’t need to care about these object creation details.

Implementation

To see the implementation at a glance let’s have a look at the class diagram:

The base abstract class is called ArticleFeedGenerator (it corresponds to Creator in the GoF book). Here’s a shortened version:

public abstract class ArticleFeedGenerator
{
    private SyndicationFeedFormatter _feedFormatter;

    // Factory method
    public abstract SyndicationFeedFormatter CreateFeedFormatter();

    public void Run()
    {
        var allArticles = _feedDataClient.GetAllArticles();
        _feed = _feedService.GetFeed(allArticles);

        _feedFormatter = CreateFeedFormatter();

        _publishService.Publish(_feedFormatter);
    }
}

ArticleFeedGenerator does all the work except creating a conccrete implementation of SyndicationFeedFormatter. It delegates it to the derived class who provide the implementation for CreateFeedFormatter abstract method.

And here comes the concrete implementations (which correspond to ConcreteCreator)

public class AtomFeedGenerator : ArticleFeedGenerator
{
    public AtomFeedGenerator(
        IFeedDataClient feedDataClient,
        IFeedService feedService,
        IPublishService publishService,
        IFeedSettings feedSettings)
        : base (feedDataClient, feedService, publishService, feedSettings)
    {
    }

    public override SyndicationFeedFormatter CreateFeedFormatter()
    {
        return new Atom10FeedFormatter(_feed);
    }
}

In order to pass RSS validations RSS formatter needed more “love” than the Atom version above:

public class RssFeedGenerator : ArticleFeedGenerator
{
    public RssFeedGenerator(
        IFeedDataClient feedDataClient,
        IFeedService feedService,
        IPublishService publishService,
        IFeedSettings feedSettings)
        : base (feedDataClient, feedService, publishService, feedSettings)
    {
    }
    
    public override SyndicationFeedFormatter CreateFeedFormatter()
    {
        var formatter = new Rss20FeedFormatter(_feed);
        formatter.SerializeExtensionsAsAtom = false;
        XNamespace atom = "http://www.w3.org/2005/Atom";
        _feed.AttributeExtensions.Add(new XmlQualifiedName("atom", XNamespace.Xmlns.NamespaceName), atom.NamespaceName);
        _feed.ElementExtensions.Add(new XElement(atom + "link", new XAttribute("href", _feedSettings.FeedUrl), new XAttribute("rel", "self"), new XAttribute("type", "application/rss+xml")));
        return formatter;
    }
}

Concerete creators create concrete products but the factory method returns the abstract product which ArticleFeedGenerator (abstract creator) works with.

To avoid hard-coding the class name, a helper method called CreateFeedGenerator is added. The client code calls this method to get either a AtomFeedGenerator or a RssFeedGenerator based on configuration value.

class Program
{
    static void Main(string[] args)
    {
        var feedSettings = new AppConfigFeedSettings();
        ArticleFeedGenerator feedGenerator = CreateFeedGenerator(feedSettings);
        feedGenerator.Run();
    }
    
    private static ArticleFeedGenerator CreateFeedGenerator(IFeedSettings feedSettings)
    {
        string feedFormat = feedSettings.FeedFormat;
        switch (feedFormat.ToLower())
        {
            case "atom": return new AtomFeedGenerator(new RareburgClient(), new RareburgArticleFeedService(), new S3PublishService(), feedSettings);
            case "rss": return new RssFeedGenerator(new RareburgClient(), new RareburgArticleFeedService(), new S3PublishService(), feedSettings);
            default: throw new ArgumentException("Unknown feed format");
        }
    }
}

Resources

designdev design_patterns, csharp

Often times we need to change some algorithm with another without changing the client code that’s consuming it. In this post I want to show a use case I came across and utilised Strategy pattern.

What is it?

Here’s the official definition of the pattern from GoF book:

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

Use case: Get external IP address

In an application I was working on I needed to get the external IP address of the computer the application is running on. There are various ways to achieve that. This looked like a good opprtunity to use the Strategy pattern as I wanted to be able switch between the different methods easily.

Implementation

The interface is quite simple:

public interface IIpCheckStrategy
{
    string GetExternalIp();
}

Some services return their data in JSON format, some have extra text in it. But encapsulating the algorithms in their own classes this way, the clint code doesn’t have to worry about parsing these various return values. It’s handled in each class. If one service changes it’s output and breaks the implementation I can recover just by changing the code instantiates the class.

The concrete implementations of the interface are below. They implement the IIpCheckStrategy and are responsible for getting the data and return parsed IP address as string.

AWS IP Checker:

public class AwsIPCheckStrategy : IIpCheckStrategy
{
    public string GetExternalIp()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://checkip.amazonaws.com/");
            string result = client.GetStringAsync("").Result;
            return result.TrimEnd('\n');
        }
    }
}

DynDns IP Checker:

public class DynDnsIPCheckStrategy : IIpCheckStrategy
{
    public string GetExternalIp()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://checkip.dyndns.org/");
            HttpResponseMessage response = client.GetAsync("").Result;
            return HelperMethods.ExtractIPAddress(response.Content.ReadAsStringAsync().Result);
        }
    }
}

Custom IP Checker:

public class CustomIpCheckStrategy : IIpCheckStrategy
{
    public string GetExternalIp()
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri("http://check-ip.herokuapp.com/");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            HttpResponseMessage response = client.GetAsync("").Result;
            string json = response.Content.ReadAsStringAsync().Result;
            dynamic ip = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
            string result = ip.ipAddress;
            return result;
        }
    }
}

Choosing the algorithm

The consumer of the algorithm can pick any class that implements IIpCheckStrategy and switch between them. For example:

class StrategyClient1
{
    public void Execute()
    {
        IIpCheckStrategy ipChecker;

        ipChecker = new DynDnsIPCheckStrategy();
        Console.WriteLine(ipChecker.GetExternalIp());

        ipChecker = new AwsIPCheckStrategy();
        Console.WriteLine(ipChecker.GetExternalIp());

        ipChecker = new CustomIpCheckStrategy();
        Console.WriteLine(ipChecker.GetExternalIp());

        Console.ReadKey();
    }
}

Also the class name to be used can be stored in the configuration in some cases so that it can be changed at runtime without recompiling the application. For instance:

class StrategyClient2
{
    public void Execute()
    {
        string ipcheckerTypeName = ConfigurationManager.AppSettings["IPChecker"];

        IIpCheckStrategy ipchecker = Assembly.GetExecutingAssembly().CreateInstance(ipcheckerTypeName) as IIpCheckStrategy;

        Console.WriteLine(ipchecker.GetExternalIp());
    }
}

and the appSettings in the configuration would look like this:

<appSettings>
	<add key="IPChecker" value="Strategy.AwsIPCheckStrategy"/>
</appSettings>

Resources