The Fibonacci Blog Post Formatter

Sarah suggested Tuesday that I write a blog PostFormatter that only changed the format of blog posts created on days that were Fibonacci Sequence days (e.g. 1, 2, 3, 5, 8…).  I’d hoped to code something like that up during my Ann Arbor talk last night but ended up not having the time, so just to show how easily this could be done, I threw something together today.

First, this is for a demo application that I used in my talks this week – if you want to follow along, grab the code from my last post.

I already had a well-tested but not at all optimized Fib generator/tester class from some Euler problems I’d worked on as part of a coding kata, so I just reused that for the formatter.  The complete code for the new IPostFormatter implementation is shown here:

public class FibPostFormatter : IPostFormatter
{
    public Post FormatPost(Post post)
    {
        var fibChecker = new FibonacciChecker();
        if (fibChecker.IsFib(post.DatePosted.Day))
        {
            post.Title = "[FibDate!] " + post.Title;
        }
        post.EncodedTitle = Utility.BuildFriendlyUrl(post.Title);
        post.Abstract = Utility.GetAbstract(post.Contents);
        return post;
    }
 
    private class FibonacciChecker
    {
        private List<int> fibNumbers;
 
        public FibonacciChecker()
        {
            fibNumbers = new List<int>();
        }
        public bool IsFib(int numberToTest)
        {
            var fibNumbers = new List<int>();
            fibNumbers = GenerateFibs(numberToTest);
            return fibNumbers.Contains(numberToTest);
        }
 
        public List<int> GenerateFibs(int upperBound)
        {
            fibNumbers = new List<int>();
            if (fibNumbers.Count == 0)
            {
                fibNumbers.Add(1);
                fibNumbers.Add(2);
            }
            int index = 2;
            int term = 0;
            while (term <= upperBound)
            {
                term = fibNumbers[index - 2] + fibNumbers[index - 1];
                fibNumbers.Add(term);
                index++;
            }
            return fibNumbers;
        }
    }
 
}

 

Again, it could benefit from some optimization – it was the result of a timeboxed Euler exercise and it certainly is adequate for testing of days up to and including 31.

Naturally I wanted to be sure this worked, so I added the following test class:

using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SolidBlog.Controllers;
using SolidBlog.Models;
 
namespace SolidBlog.Tests.Formatters
{
    [TestClass]
    public class PostFormatterTester
    {
        private const string TEST_TITLE = "test title";
        private FibPostFormatter _fibPostFormatter;
        private const string EXPECTED_PREFIX = "[FibDate!] ";
 
        [TestInitialize]
        public void Setup()
        {
            _fibPostFormatter = new FibPostFormatter();
        }
 
        [TestMethod]
        public void FibDaysShouldBeFormattedAsFib()
        {
            var fibDays = new List<int>() {1, 2, 3, 5, 8, 13, 21};
            foreach (var day in fibDays)
            {
                Post myPost = _fibPostFormatter.FormatPost(GetTestPostForDayOfJanuary(day));
                Assert.AreEqual(EXPECTED_PREFIX + TEST_TITLE, myPost.Title, 
                    "Didn't format title correctly for fib day " + day);
            }
        }
 
        [TestMethod]
        public void NonFibDaysShouldNotBeFormattedAsFib()
        {
            var fibDays = new List<int>() { 4, 6, 7, 9, 10, 11, 12, 14, 15, 16, 17, 18, 19, 
                20, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31 };
            foreach (var day in fibDays)
            {
                Post myPost = _fibPostFormatter.FormatPost(GetTestPostForDayOfJanuary(day));
                Assert.AreEqual(TEST_TITLE, myPost.Title, 
                    "Formatted title incorrectly for nonfib day " + day);
            }
        }
 
 
        private Post GetTestPostForDayOfJanuary(int day)
        {
            DateTime testPostDate = new DateTime(2009, 1, day);
            var myPost = new Post() { DatePosted = testPostDate, Title = TEST_TITLE, 
                Abstract = "", Contents = "" };
 
            return myPost;
        }
 
    }
}

 

Both tests pass.  Finally, to wire this up in my blog application, I simply need to turn on the IoC stuff in global.asax (uncomment line 35) and be sure to use the new Create() action of my PostController (line 175 in PostController needs renamed to Create and the other Create() renamed to something else).  Finally, change the IPostFormatter line in ConfigureIoC() in global.asax (line 51/52) to the following:

StructureMapConfiguration.BuildInstancesOf<IPostFormatter>().TheDefaultIsConcreteType
    <FibPostFormatter>();

With that you can run the app and if you happen to create a new post on one of the Fib sequence days of the month, you’ll see that it makes a note of this by prefixing the title with “[FibDate!] “.

Note that to achieve this (assuming that the application we were changing already was using IoC and the refactored version of Create()) we didn’t have to change any existing code (well, ok, one line of IoC bootstrapping code).  That’s the Open/Closed Principle.  Adding additional features or changing the system’s behavior should not require that you change existing code, but rather that you encapsulate new behavior into new classes that can be swapped out via the interfaces used to refer to them.

a2chrisn4u

blog comments powered by Disqus