Wednesday, July 4, 2012

Factories

The design pattern for a factory includes an interface for the factory with methods that return interfaces. Here is a diagram that includes the implementation of the factory:



FactoryInterface contains methods that return interfaces for, in this case, ServiceAInterface and ServiceBInterface. The implementation, Factory, is responsible for determining which objects that implement those services should be created.

A factory could be created without the use of interfaces, of course, but you would be limited in how you could use that factory in testing. See Developing Software with BDD  for more on how factories can be used in testing.


Factory Trees

In most business application where factories would be used, you are likely to need factories in each layer (see Layered Design Pattern for Enterprise Applications .) For example, you will need a factory for the Presentation, Service and Data Access Layer. In this scenario, the creation of concrete implementations at each layer will depend on creating the dependant layers, visa-vi Dependency Injection. To accomplish this, each factory must have a dependent property for the factory (or factories) beneath it. The following diagram illustrates this:


Here you see that the Factory class has a property for FactoryB.

You will notice that this property is not a part of the interface. It is not necessary because the fact that the factory is even needed may be considered an implementation detail. For example, suppose you have an interface that defines a service. Naturally, that service needs some data access objects, so the service factory will have a data access factory. However, you might also use the same service in a thick click scenario where the service is really a proxy for a web service. In that case, the data access factory is not required. Since it is the factory itself that will know whether another factory is needed, the interface need not define that dependency. Of course, it won't really hurt if you included it anyway; you'd just not set it for the service proxy factory.

Here is what the example factory tree would look like:



Instantiating Factories


Using a Factory in code is quite simple:

ServiceAInterface servicea = factory.CreateServiceA();

The challenge is when and where to instantiate the factory. If the factory is by itself, you might just create it when you need it, like so:

FactoryInterface factory = new Factory();
ServiceAInterface servicea = factory.CreateServiceA();

Of course, the problem with this approach is that you hardly get any value out of abstracting the factory itself. A better approach is to create the factory early, such as when the application starts, and use that object throughout the application. You might do this in the Main() method of a Windows Forms application or in the Application.Start of an ASP.NET application.

Wherever you instantiate the factory, you will specify the concrete factories that are used in the tree. By doing this in one place in your application, you are able to switch out factories at different layers when you need to, as would be the case for unit tests. Here is an example of how you would instantiate the above factory tree:

PresenterFactory presenterfactory = new PresenterFactory(
     new ServiceFactory(
           new DataAccessFactory()
     )
);

This would be typical for an ASP.NET application. The variable presenterfactory would end up in Application state or you might even invoke this at each post back, assuming these constructors did nothing.

If you were instantiating the same PresenterFactory for a thick client that used a web service proxy, it would look a bit different:

PresenterFactory presenterfactory = new PresenterFactory(
      new ServiceProxyFactory()
           );

No need for the DataAccessFactory here. Note two that both the PresenterFactory and any presenters it creates will have no knowledge of the fact that you used a different service factory. This allows the presentation layer to be entirely independent of the service layer.

Using Factories in Unit Tests

Finally, a word about factories in unit tests. Since unit tests are granular, i.e. you test a single method in isolation, the dependencies that factories create would normally already be created by your test. For example, if I'm writing a test for some service method, and using Moq, I might setup my service like so:

Mock<DataAccessInterface> dataAccessMock = new Mock<DataAccessInterface>();
ServiceInterface service = new Service(dataAccessMock.Object);

I did not use a factory like I would normally in the application. That's perfectly fine as long as I test my factory methods somewhere.

On the other hand, I might gain a benefit by letting the factory do its job even in unit tests. In that case, I would use the factory implementation along with the service implementation, but I would provide mocks for other layers. So, my test setup would then look like this:

Mock<DataAccessInterface> dataAccessMock = new Mock<DataAccessInterface>();
Mock<DataAccessFactoryInterface> dataAccessFactoryMock
     = new Mock<DataAccessFactoryInterface>();
dataAccessFactoryMock.Setup(f=>f.CreateDataAccess())
     .Returns(dataAccessMock.Object);

ServiceFactoryInterface serviceFactory
     = new ServiceFactory(dataAccessFactoryMock.Object);

Notice how I setup a mock for the data access factory that will provide the data access mock for the service. This is useful in a base class for service tests (or whatever layer) where you can setup all the data access factory methods you need to create all the service objects. This allows your setup to consist of a single line, assuming that the base test fixture exposes the service factory.

Note: When doing this, you will probably NOT validate the data access factory mock because you will have setup all the methods when only a few are needed in any circumstance. That will not pose a problem for testing since the absence of the data access mocks will cause the test to fail IF you did not properly implement the factory. To do this with Moq, use MockBehavior.Loose in the constructor and don't call VerifyAll().


No comments:

Post a Comment