Abstract Factory

Abstract Factory Pattern

Introduction

The Abstract Factory pattern is a creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. This pattern is especially useful for systems that need to be independent of how their objects are created, composed, and represented.

Purpose

  • To provide an interface for creating families of related or dependent objects without specifying their concrete classes.
  • To encapsulate a group of individual factories with a common goal.

Use Cases

  • When the system needs to be independent of how its products are created, composed, and represented.
  • When families of related products are designed to be used together, and you need to enforce this constraint.

Advantages/Disadvantages

Advantages:

  • Isolates concrete classes: The client code is separated from the implementation details of derived products.
  • Makes exchanging product families easy: The class of a concrete factory appears only once in an application.

Disadvantages:

  • Can be more complex to implement due to the multiple layers of abstraction.
  • Adding new kinds of products can be challenging as it might require extending the interface, affecting all concrete factories.

Implementation in JavaScript

javascript
	class AbstractFactory {
	  createProductA() {}
	  createProductB() {}
	}
	
	class ConcreteFactory1 extends AbstractFactory {
	  createProductA() {
	    return new ProductA1();
	  }
	
	  createProductB() {
	    return new ProductB1();
	  }
	}
	
	class ConcreteFactory2 extends AbstractFactory {
	  createProductA() {
	    return new ProductA2();
	  }
	
	  createProductB() {
	    return new ProductB2();
	  }
	}
	
	class ProductA1 {}
	class ProductB1 {}
	class ProductA2 {}
	class ProductB2 {}
	
	// Usage
	const factory1 = new ConcreteFactory1();
	const productA1 = factory1.createProductA();
	const productB1 = factory1.createProductB();
	
	const factory2 = new ConcreteFactory2();
	const productA2 = factory2.createProductA();
	const productB2 = factory2.createProductB();

Practical Example

javascript
	class GUIFactory {
	  createButton() {}
	  createCheckbox() {}
	}
	
	class WinFactory extends GUIFactory {
	  createButton() {
	    return new WinButton();
	  }
	
	  createCheckbox() {
	    return new WinCheckbox();
	  }
	}
	
	class MacFactory extends GUIFactory {
	  createButton() {
	    return new MacButton();
	  }
	
	  createCheckbox() {
	    return new MacCheckbox();
	  }
	}
	
	class WinButton {
	  render() {
	    return 'Windows Button';
	  }
	}
	
	class WinCheckbox {
	  render() {
	    return 'Windows Checkbox';
	  }
	}
	
	class MacButton {
	  render() {
	    return 'Mac Button';
	  }
	}
	
	class MacCheckbox {
	  render() {
	    return 'Mac Checkbox';
	  }
	}
	
	// Usage
	function createUI(factory) {
	  const button = factory.createButton();
	  const checkbox = factory.createCheckbox();
	  console.log(button.render());
	  console.log(checkbox.render());
	}
	
	createUI(new WinFactory()); // Windows Button, Windows Checkbox
	createUI(new MacFactory()); // Mac Button, Mac Checkbox

In this practical example, GUIFactory serves as an abstract factory for creating a family of GUI elements. WinFactory and MacFactory are concrete factories that produce Windows and Mac styled GUI elements, respectively. This pattern allows for the creation of consistent-looking GUIs across different operating systems.