29. Factory

image

© Jennifer M. Kohnke

The man who builds a factory builds a temple.

—Calvin Coolidge (1872–1933)

The Dependency-Inversion Principle (DIP) (Chapter 11) tells us that we should prefer dependencies on abstract classes and avoid dependencies on concrete classes, especially when those classes are volatile. Therefore, the following snippet of code violates this principle:

Circle c = new Circle(origin, 1);

Circle is a concrete class. Therefore, those modules that create instances of Circle must violate DIP. Indeed, any line of code that uses the new keyword violates DIP.

There are times when violating DIP is mostly harmless, which is pretty good coverage. The more likely a concrete class is to change, the more likely that depending on it will lead to trouble. But if the concrete class is not volatile, depending on it is not worrisome. For example, creating instances of string does not bother me. Depending on string is very safe because string is not likely to change any time soon.

On the other hand, when we are actively developing an application, many concrete classes are very volatile, so depending on them is problematic. We’d rather depend on an abstract interface to shield us from the majority of the changes.

The FACTORY pattern allows us to create instances of concrete objects while depending only on abstract interfaces. Therefore, it can be of great assistance during active development when those concrete classes are highly volatile.

Figure 29-1 shows the problematic scenario. We have a class named SomeApp that depends on the interface Shape. SomeApp uses instances of Shape solely through the Shape interface and does not use any of the specific methods of Square or Circle. Unfortunately, SomeApp also creates instances of Square and Circle and thus has to depend on the concrete classes.

Figure 29-1. An app that violates DIP to create concrete classes

image

We can fix this by applying the FACTORY pattern to SomeApp, as in Figure 29-2. Here, we see the ShapeFactory interface, which has two methods: MakeSquare and MakeCircle. The MakeSquare method returns an instance of a Square, and the MakeCircle method returns an instance of a Circle. However, the return type of both functions is Shape.

Figure 29-2. Application of FACTORY to SomeApp

image

Listing 29-1 shows what the ShapeFactory code looks like. Listing 29-2 shows ShapeFactoryImplementation.


Listing 29-1. ShapeFactory.cs

public interface ShapeFactory
{
  Shape MakeCircle();
  Shape MakeSquare();
}



Listing 29-2. ShapeFactoryImplementation.cs

public class ShapeFactoryImplementation : ShapeFactory
{
  public Shape MakeCircle()
  {
    return new Circle();
  }

  public Shape MakeSquare()
  {
    return new Square();
  }
}


Note that this completely solves the problem of depending on concrete classes. The application code no longer depends on Circle or Square, yet it still manages to create instances of them. It manipulates those instances through the Shape interface and never invokes methods that are specific to Square or Circle.

The problem of depending on a concrete class has been moved. Someone must create ShapeFactoryImplementation, but nobody else ever needs to create Square or Circle. ShapeFactoryImplementation will most likely be created by Main or an initialization function attached to Main.

A Dependency Problem

Astute readers will recognize a problem with this form of the FACTORY pattern. The class ShapeFactory has a method for each of the derivatives of Shape. This results in a name-only dependency that makes it difficult to add new derivatives to Shape. Every time we add a new Shape derivative, we have to add a new method to the ShapeFactory interface. In most cases, this means that we’ll have to recompile and redeploy all the users of ShapeFactory.1

We can get rid of this dependency problem by sacrificing a little type safety. Instead of giving ShapeFactory one method for every Shape derivative, we can give it only one make function that takes a string. For example, look at Listing 29-3. This technique requires that ShapeFactoryImplementation use an if/else chain on the incoming argument to select which derivative of Shape to instantiate. This is shown in Listings 29-4 and 29-5.


Listing 29-3. A snippet that creates a circle

[Test]
public void TestCreateCircle()
{
  Shape s = factory.Make("Circle");
  Assert.IsTrue(s is Circle);
}



Listing 29-4. ShapeFactory.cs

public interface ShapeFactory
{
  Shape Make(string name);
}



Listing 29-5. ShapeFactoryImplementation.cs

public class ShapeFactoryImplementation : ShapeFactory
{
  public Shape Make(string name)

  {
    if(name.Equals("Circle"))
      return new Circle();
    else if(name.Equals("Square"))
      return new Square();
    else
      throw new Exception(
        "ShapeFactory cannot create: {0}", name);
  }
}


One might argue that this is dangerous, because callers who misspell the name of a shape will get a runtime error instead of a compile-time error. This is true. However, if you are writing the appropriate unit tests and are applying test-driven development, you’ll catch these runtime errors long before they become problems.

Static versus Dynamic Typing

The trade-off that we’ve just witnessed between type safety and flexibility typifies the ongoing debate over language styles. On the one side are statically typed languages, such as C#, C++, and Java, which check types at compile time and emit compilation errors if the declared types aren’t consistent. On the other hand, dynamically typed languages, such as Python, Ruby, Groovy, and Smalltalk, do their type checking at runtime. The compiler does not insist on type consistence; nor, indeed, does the syntax of these languages permit such checking.

As we saw in the FACTORY example, static typing can lead to dependency knots that force modifications to source files for the sole purpose of maintaining type consistency. In our case, we have to change the ShapeFactory interface whenever a new derivative of Shape is added. These changes can force rebuilds and redeployments that would otherwise be unecessary. We solved that problem when we relaxed type safety and depended on our unit tests to catch type errors; we gained the flexibility to add new derivatives of Shape without changing ShapeFactory.

Proponents of statically typed languages contend that the compile-time safety is worth the minor dependency issues, the increased rate of source code modification, and the increased rate of rebuild and redeployment. The other side argues that unit tests will find most of the problems that static typing would find and that the burden of source code modification, rebuild, and redeployment is therefore unecessary.

I find it interesting that the rise in popularity of dynamically typed languages is, so far tracking the rise in adoption of test-driven development (TTD). Perhaps programmers who adopt TDD are finding that it changes the safety versus flexibility equation. Perhaps those programmers are gradually becoming convinced that the flexibility of dynamically typed languages outweighs the benefits of static type checking.

Perhaps we are at the crest of popularity of the statically typed languages. If the current trend continues, we may find that the next major industrial language is more related to Smalltalk than to C++.

Substitutable Factories

One of the great benefits of using factories is the ability to substitute one implementation of a factory for another. In this way, you can substitute families of objects within an application.

For example, imagine an application that had to adapt to many different database implementations. In our example, let’s assume that the users can either use flat files or purchase an Oracle adapter. We might use the PROXY pattern to isolate the application from the database implementation.2 We might also use factories to instantiate the proxies. Figure 29-3 shows the structure.

Figure 29-3. Substitutable factory

image

Note the two implementations of EmployeeFactory. One creates proxies that work with flat files, and the other creates proxies that work with Oracle. Note also that the application does not know or care which implementation is being used.

Using Factories for Test Fixtures

When writing unit tests, we often want to test the behavior of a module in isolation from the modules it uses. For example, we might have a Payroll application that uses a database (see Figure 29-4). We may wish to test the function of the Payroll module without using the database at all.

Figure 29-4. Payroll uses database

image

We can accomplish this by using an abstract interface for the database. One implementation of this abstract interface uses the real database. Another implementation is test code written to simulate the behavior of the database and to check that the database calls are being made correctly. Figure 29-5 shows the structure. The PayrollTest module tests the PayrollModule by making calls to it and also implements the Database interface so that it can trap the calls that Payroll makes to the database. This allows PayrollTest to ensure that Payroll is behaving properly. It also allows PayrollTest to simulate many kinds of database failures and problems that are otherwise difficult to create. This is a testing pattern known as SELF-SHUNT, also sometimes known as mocking or spoofing.

Figure 29-5. PayrollTest SELF-SHUNTs database

image

How does Payroll get the instance of PayrollTest it uses as the Database? Certainly, Payroll isn’t going to do the creation of PayrollTest. Just as clearly, Payroll must somehow get a reference to the Database implementation it’s going to use.

In some cases, it is perfectly natural for PayrollTest to pass the Database reference to Payroll. In other cases, it may be that PayrollTest must set a global variable to refer to the Database. In still others, Payroll may be fully expecting to create the Database instance. In that last case, we can use a Factory to fool Payroll into creating the test version of the Database by passing an alternative factory to Payroll.

Figure 29-6 shows a possible structure. The Payroll module acquires the factory through a global variable—or a static variable in a global class—named Gdatabase-Factory. The PayrollTest module implements DatabaseFactory and sets a reference to itself into that GdatabaseFactory. When Payroll uses the factory to create a Database, the PayrollTest module traps the call and passes back a reference to itself. Thus, Payroll is convinced that it has created the PayrollDatabase, and yet the PayrollTest module can fully spoof the Payroll module and trap all database calls.

Figure 29-6. Spoofing the factory

image

Importance of Factories

A strict interpretation of DIP would insist on using factories for every volatile class in the system. What’s more, the power of the FACTORY pattern is seductive. These two factors can sometimes lure developers into using factories by default. This is an extreme that I don’t recommend.

I don’t start out using factories. I put them into the system only when the need for them becomes great enough. For example, if it becomes necessary to use the PROXY pattern, it will probably become necessary to use a factory to create the persistent objects. Or, if through unit testing, I come across situations in which I must spoof the creator of an object, I will likely use a factory. But I don’t start out assuming that factories will be necessary.

Factories are a complexity that can often be avoided, especially in the early phases of an evolving design. When they are used by default, factories dramatically increase the difficulty of extending the design. In order to create a new class, one may have to create as many as four new classes: the two interface classes that represent the new class and its factory and the two concrete classes that implement those interfaces.

Conclusion

Factories are powerful tools. They can be of great benefit in conforming to DIP. They allow high-level policy modules to create instances of objects without depending on the concrete implementations of those objects. Factories also make it possible to swap in completely different families of implementations for a group of classes. However, factories are a complexity that can often be avoided. Using them by default is seldom the best course of action.

Bibliography

[GOF95] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1995.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
34.238.242.168