Published on

Mastering the Factory Method Design Pattern

Authors

The Factory Method is a key creational design pattern in software engineering, offering an elegant solution for object creation. It encapsulates the creation logic, thereby reducing complexity in client code and enhancing flexibility.

Understanding Factory Method

This pattern is particularly useful when there are different types of objects to create and a single method is desired to simplify the instantiation process. It allows specifying the type of object to be created through a generic interface, streamlining the creation process.

Example Use Case

Consider a scenario where we need to create a 'button' object for different operating systems. Instead of having the client choose the correct constructor, a generalized method can be used to instantiate the appropriate button object.

Factory Method Analogy

The Factory Method is akin to a hiring agency. Just as a hiring agency takes care of the recruitment details, allowing clients to simply specify their needs, the Factory Method handles object creation, letting consumers specify what type of object they need without worrying about the creation details.

When to Use Factory Method

  • When object creation is complex.
  • To create different instances sharing similar properties with ease.
  • In scenarios dependent on external resources, where the exact type of resource isn't known beforehand.
  • When constructing an instance depends on already constructed instances.

Advantages

  • Promotes reusability and a single point of control for object creation.
  • Enhances extensibility and simplifies testing.

Disadvantages

  • Can lead to a high number of classes.
  • Extending the application might become elaborate and complex.
  • In conclusion, the Factory Method design pattern plays a crucial role in managing object creation in software development, making it an essential tool for developers to master.

Structure and Implementation

The Factory Method simplifies the creation of complex objects. For illustration, let's consider a simplified example of creating bank account objects.

// Constructor for defining new CurrentAccounts
function CurrentAccount(options){
  this.minBalance = options.minBalance || 1000;
  this.interest = options.interest || 0;
}

// Constructor for defining new SavingAccounts
function SavingAccount(options){
  this.minBalance = options.minBalance || 5000;
  this.interest = options.interest || 0.1;
}

// Define a skeleton AccountFactory
function AccountFactory(){}

// Default accountClass is CurrentAccount
AccountFactory.prototype.AccountClass = CurrentAccount;

// Factory method for creating new Account instances
AccountFactory.prototype.createAccount = function(options = {}){
  switch(options.accountType){
    case 'current':
      this.AccountClass = CurrentAccount;
      break;
    case 'saving':
      this.AccountClass = SavingAccount;
      break;
    // Defaults to CurrentAccount
  }
  return new this.AccountClass(options);
}

function run(){
  const accountMaker = new AccountFactory();
  const account1 = accountMaker.createAccount();
  const account2 = accountMaker.createAccount({
    accountType: 'current',
    minBalance: 2000,
    interest: 0,
  });
  const account3 = accountMaker.createAccount({
    accountType: 'saving',
    minBalance: 20000,
  });
  console.log(account1);
  console.log(account2);
  console.log(account3);
}

run();