designdevelopmentdesign patternscsharp

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

designdevelopmentdesign patterns

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

developmentawslambda

Just two days ago I was jumping through a lot of hoops (maintain state on S3, subscribe to public SNS topics) just to schedule a Lambda function, as explained here. If I knew all these problems were to be solved in a day, I would just wait! In re-Invent 2015 event AWS announced scheduled event support for Lambda functions.

After tweaking around a little bit with the sample I set my schedule like this which will run first day of every month at 10:00:

cron(0 10 1 * ? *)

That’s all there is to it for a monthly schedule. Reliable and no need for my state management workarounds. So the final code becomes as simple as sending an email when invoked:

Resources