Abstract Factory is NOT Loosely Coupling

Posted: April 1, 2013 in Clean Code, Development General
Tags: , , , ,

In the last few years a lot has been written about the benefits of automated testing, unit testing, TDD, and mocking frameworks. So much has been written about it, even demonstrated in the field, that I find quite surprising, and frightening, that there are some blogs out there in which their authors claim that the effort required to write the tests and let the tests drive the design is a huge overhead, not necessary, increases the complexity of both the production code and the tests themselves, a bad approach among other things.

Unfortunately I was unable to find the exact quote, but I believe that it was in his book, Architecting Applications for the Enterprise, that Dino Esposito says, I am paraphrasing, that there is no difference between a working piece of software that is testable and another one that is not, but the one that is designed with testability in mind will have a much better design. Even though I do not agree with the first part of this claim, the reasons and arguments are the subject of another post, I absolutely agree with the second part of it. When we design our software with testability in mind the end result is a much more maintainable and loosely coupled design.

With these ideas in mind, the next series of posts will aim to address some of the, what I consider, misconceptions about designing for testability, loosely coupling, and the YAGNI and KISS principles. I hope that by the end of them I will be able to add a small contribution to the development community in trying to make software development a more robust discipline.

Designing for testability does pose a number of challenges that have been addressed in a number of ways. One of these ways has been the Inversion of Control (IoC) principle, and more specifically by using Dependency Injection. Another way has been by the creation of tools like TypeMock and Microsoft Fakes. In a nutshell these tools are able to reach into unreachable code and replace static methods and tightly coupled classes with stubs so that untestable code can be unit tested. We will discuss this type of tools at another time, what I am more interested right now is to talk about an online argument in the comments section of this blog post between two people about the use of these tools.

The person who was against the use of this tools was concerned about the unmaintainable and tightly coupled software that could be developed since the limitations of replacing those hard coded dependencies were no longer there. The other person, who was not only in favor of these tools but avidly encouraged their use, claimed that his concerns were the result of the idea of “the ability for code to be unit testable in classical terms” and that the use of an abstract factory would reduce the coupling even further. This last comment was what drew the line for me. Don’t get me wrong, I love the abstract factory pattern and I think is an invaluable tool to abstract the creation of objects from the components that will use them. This does not mean that an abstract factory in itself will be loosely coupled to the code that needs to decide which concrete factory to instantiate and have it create the required object.

To be able to demonstrate these ideas, let’s think about a smart phone manufacturer that has different lines of products and these products can be subdivide into sub-products to satisfy the increasing demands of a hungry market. Of course when the manufactured products reach a certain point in the assembly line a certain number of, what do you know, tests will have to be performed to make sure each product comply with the company’s quality policies. In order to do that all products need to have a common interface to be handed to the testing process, which will not be interested in the exact model of the product.

    public interface IPhone
    {
        void DoSomething();
    }

    public class HighEnd16Gig : IPhone
    {
        public void DoSomething(){}
    }

    public class HighEnd32Gig : IPhone
    {
        public void DoSomething(){}
    }

    public class LowEnd16Gig : IPhone
    {
        public void DoSomething(){}
    }

    public class LowEnd32Gig : IPhone
    {
        public void DoSomething(){}
    }

We can see that we have a high end and a low end phone and that each of them comes in 16 and 32 gigs of storage models. Since the testing process should not care about what kind of phone it will receive at any given time, we define an abstract factory to create the concrete product before starting the tests.

    public interface IPhoneFactory
    {
        IPhone Create();
    }

    public class HighEndFactory : IPhoneFactory
    {
        private readonly StorageSize _storageSize;

        public HighEndFactory(StorageSize storageSize)
        {
            _storageSize = storageSize;
        }

        public IPhone Create()
        {
            switch (_storageSize)
            {
                case StorageSize.Gig16:
                    return new HighEnd16Gig();
                case StorageSize.Gig32:
                    return new HighEnd32Gig();
                default:
                    throw new ArgumentException("Unknown high end model.");
            }
        }
    }

    public class LowEndFactory : IPhoneFactory
    {
        private readonly StorageSize _storageSize;

        public LowEndFactory(StorageSize storageSize)
        {
            _storageSize = storageSize;
        }

        public IPhone Create()
        {
            switch (_storageSize)
            {
                case StorageSize.Gig16:
                    return new LowEnd16Gig();
                case StorageSize.Gig32:
                    return new LowEnd32Gig();
                default:
                    throw new ArgumentException("Unknown low end model.");
            }
        }
    }
    public interface IManufacturingProcess
    {
        void ProcessProduct(ProductType productType, StorageSize storageSize);
    }

    public class TestingCell : IManufacturingProcess
    {
        public void ProcessProduct(ProductType productType, StorageSize storageSize)
        {
            //Do some stuff.

            IPhoneFactory factory;
            switch (productType)
            {
                case ProductType.HighEnd:
                    factory = new HighEndFactory(storageSize);
                    break;
                case ProductType.LowEnd:
                    factory = new LowEndFactory(storageSize);
                    break;
                default:
                    throw new ArgumentException("Unknown Product Type");
            }

            var product = factory.Create();
            ExecuteTests(product);
        }

        private void ExecuteTests(IPhone product)
        {
            product.DoSomething();
        }
    }

This is a very good design, we can see that the component that is going to start the testing process is completely abstracted from the creation mechanism of the different types of products, but there is still one problem, this component is tightly coupled not only to the abstraction but to the two concrete implementations as well. This component is deciding which kind of factory it needs to instantiate to ask for the product it will send to the testing process so if new product lines are added, it will have to be modified to include the new concrete factories and this is a big maintenance issue. Even worse, this is just one step of the whole process, other processes will have to implement the same logic to determine which factory to use.

A much better approach would be to inject the factory to this component so it is completely agnostic of the kind of factory it will be calling.

    public interface IManufacturingProcess
    {
        void ProcessProduct();
    }

    public class TestingCell : IManufacturingProcess
    {
        private readonly IPhoneFactory _factory;

        public TestingCell(IPhoneFactory factory)
        {
            if(factory == null) throw new ArgumentNullException("factory");
            _factory = factory;
        }

        public void ProcessProduct()
        {
            //Do some stuff.

            var product = _factory.Create();
            ExecuteTests(product);
        }

        private void ExecuteTests(IPhone product)
        {
            product.DoSomething();
        }
    }

The benefits of this approach are so many that it just cannot be underestimated:

  • These factories are used by a lot of components throughout the manufacturing process so the code to decide which factory to instantiate will not have to be duplicated in each one.
  • If the factories, the products, and their interfaces are each defined in their own assembly the components that use them only need a reference to the one with the interfaces so, even if new factories are constantly added the assemblies that use them will not have to be recompiled making all of your assemblies truly decoupled.
  • The instantiation of the factories can be isolated in a single place of the application, preferably in the Composition Root.
  • The components that require the factories’ adhere more to the Single Responsibility Principle.
  • The component that will perform the tests can be unit tested in complete isolation without any concern of how the factories create the products.
  • Once you have made your peace with the concepts of abstract factory, dependency injection, and composition root, a nice implementation of all of these concepts working together can be used.

Even though abstract factories are a wonderful tool to isolate and abstract the creation of components from their callers, they are not, in their own sense, a way to achieve a loosely coupled design. To this day the best design technique that I have seen to achieve absolute and truly loose coupling, not only from individual classes but from full assemblies, is dependency injection. I will not presume to say that this is the golden hammer of software engineering, but so far it has proven itself to be the best we’ve got.

Advertisements

Does this make sense to you?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s