Leveraging Polymorphism with Factory Classes

In this post, I’ll explain the benefits of using the factory pattern, and show you how to code a typical factory implementation to achieve polymorphism in your .NET applications. In my next post, I’ll show you how to optimize the factory method for ultra-high performance by using a static array of delegates.

Working with Different Types

Let’s use a typical POS (Point Of Sale) scenario to illustrate the factory pattern. POS applications run on checkout counters in retail stores, where the customer can pay with a variety of tender types, such as cash, credit, debit, voucher, etc. Each of these types exhibit different behaviors; for example, paying with cash pops open the cash drawer, paying by debit card requires a PIN, paying by credit card requires a signature, etc.

Without using a factory pattern to manage the different tender types, code to determine if a customer signature is required on checkout for a given tender type might look like this:

bool isSignatureRequired;
switch (tenderType)
{
  case TenderType.Cash:
    isSignatureRequired = false;
    break;


  case TenderType.DebitCard:
    isSignatureRequired = false;
    break;


  case TenderType.CreditCard:
    isSignatureRequired = true;   // credit cards require signature
    break;

   //
   // more case statements here
   //

  default:
    throw new Exception("Unsupported tender: " + (int)tenderType);
}

Similarly, to determine if a PIN is required for any given tender type:

bool isPinRequired;
switch (tenderType)
{
  case TenderType.Cash:
    isPinRequired = false;
    break;


  case TenderType.DebitCard:
    isPinRequired = true;   // debit cards require PIN
    break;


  case TenderType.CreditCard:
    isPinRequired = false;
    break;

   //
   // more case statements here
   //

  default:
    throw new Exception("Unsupported tender: " + (int)tenderType);
}

With this approach, the same switch/case “ladder” will appear numerous times scattered throughout your application, each one dealing with a different aspect of each possible tender type. In short order, maintenance will become a nightmare. Changing the behavior of any type means hunting down all the pertinent switch/case blocks and modifying them. Creating a new type means adding a new case to many existing switch/case blocks—wherever they are (good luck finding them all). It’s nearly impossible to gain a clear picture of how the tender types compare to one another, because bits of information about each tender are fragmented in switch/case blocks across the application’s codebase.

Creating a Factory

The factory pattern solves this problem by eliminating all these switch/case blocks from your application, and consolidating the logic for each type in its own class instead. All the classes for each type implement the same interface, so that you can work with an instance of any type without knowing or caring what the concrete type is, and get the information you need.

With a factory pattern in place, you won’t have to search for or modify one line of code in your core application logic when the behavior of an existing tender changes or a tender type is supported in the future. This represents a huge improvement over the scattered switch/case approach.

Here are the steps to implement the pattern:

1) Create the ITender interface:

With this interface, we have defined certain things that we know about every tender type, such as IsSignatureRequired, IsPinRequired, AllowCashBack, etc.

public interface ITender
{
  bool IsSignatureRequired { get; }
  bool IsPinRequired { get; }
  bool AllowCashBack { get; }
  bool PopOpenCashDrawer { get; }
   //
   // more members
   //
}

2) Define enums for each ITender derivative: 

public enum TenderType
{
  Cash,
  DebitCard,
  CreditCard,
   //
   // more tender types
   //
}

3) Create concrete ITender classes. Here are three concrete classes that implement ITender:

public class CashTender : ITender
{
  bool ITender.IsSignatureRequired { get { return false; } }
  bool ITender.IsPinRequired       { get { return false; } }
  bool ITender.AllowCashBack       { get { return false; } }
  bool ITender.PopOpenCashDrawer   { get { return true; } }
}

public class DebitCardTender : ITender
{
  bool ITender.IsSignatureRequired { get { return false; } }
  bool ITender.IsPinRequired       { get { return true; } }
  bool ITender.AllowCashBack       { get { return true; } }
  bool ITender.PopOpenCashDrawer   { get { return false; } }
}

public class CreditCardTender : ITender
{
  bool ITender.IsSignatureRequired { get { return true; } }
  bool ITender.IsPinRequired       { get { return false; } }
  bool ITender.AllowCashBack       { get { return false; } }
  bool ITender.PopOpenCashDrawer   { get { return false; } }
}

4) Create the factory class:

public static class TenderFactory
{
  public static ITender CreateTender(TenderType tenderType)
  {
    switch (tenderType)
    {
      case TenderType.Cash:
        return new CashTender();


      case TenderType.DebitCard:
        return new DebitCardTender();


      case TenderType.CreditCard:
        return new CreditCardTender();


       //
       // more case statements here
       //

      default:
        throw new Exception("Unsupported tender: " + (int)tenderType);
    }
  }
}

With these elements in place, you can handle any ITender derivative throughout your application easily and elegantly. Given an enum for any particular tender type, a single call to the CreateTender factory method will return a new instance of the correct concrete ITender object. You can then work with the properties and methods of the returned instance and get the results expected for the specific tender type, without needing to know the specific tender type or testing for different tender types. This is the essence of polymorphism.

For example, to determine if a signature is required, it’s as simple as:

ITender tender = TenderFactory.CreateTender(tenderType);
bool isSignatureRequired = tender.IsSignatureRequired;

Unlike the code at the top of the post that retrieved this information without using the factory pattern, this code will never change, even as new tenders are added in the future. Adding support for a new tender (for example, food stamps) now involves only adding a new enum, creating a new concrete class that implements all the ITender members for the new tender type, and adding a single case statement to the factory method’s switch/case block. Doing so allows you to instantly plug in new tender types without touching one line of code throughout the body of your core application logic. The code above requires no modifications to determine if a signature is required for a newly added tender.

Enhancing and Optimizing the Factory Method

So now your application calls a method in the factory class to “new up” an ITender instance rather than explicitly instantiating a new ITender object through one of its constructors. You can enhance this pattern by encapsulating all the concrete classes in a separate assembly and scoping their constructors as internal (Friend, in VB) so that clients cannot circumvent the factory method and are instead forced to create new ITender instances by calling the factory method.

Another logical next step would be to create an abstract base class named TenderBase which would house common functionality that all tender types require. TenderBase would implement ITender, and all the concrete tender classes would inherit TenderBase instead of directly implementing ITender (though they will still implement ITender implicitly of course, since TenderBase implements ITender).

It’s important to ensure that your factory methods execute as quickly as possible, especially in scenarios where there is a high-volume demand to create new objects. In my next post, I’ll show you an improved version of the factory method that significantly out-performs the switch/case approach shown here (especially when dealing very a great many entity types), by replacing the switch/case logic with a static array of delegates.

Advertisements

4 Responses to “Leveraging Polymorphism with Factory Classes”

  1. Optimizing Factory Methods with Static Delegate Arrays « Lenni Lobel on .NET and SQL Server Development Says:

    […] Factory Methods with Static Delegate Arrays August 12, 2009 — Leonard Lobel In my last post, I explained the benefits of using factory classes to achieve polymorphism in your business […]

  2. Ken Jones Says:

    Very valuable blog, Lenni!

  3. Mark D. Says:

    Great stuff Leonard. Very helpful to those of us new to .NET. Thanks.


Leave a Reply

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

%d bloggers like this: