๐ญ What Is the Abstract Factory Pattern?
๐ฏ In simple terms
The Abstract Factory pattern helps you create groups of related objects without knowing the exact classes that will be used.
Think of it like building a theme system for an app — you might use a Light Theme or a Dark Theme. Each theme has its own Button, TextBox, and Checkbox.
The Abstract Factory pattern gives you a factory for each theme that knows how to create all the UI parts that match. You don’t care how the components are created — you just ask the factory to give them to you.
๐งฑ Why Use It?
- You want to create families of related objects (matching buttons and inputs).
- You want to enforce consistency across object groups (Light Theme components work together).
- You want to avoid writing
if/switchlogic everywhere just to choose versions.
๐งช A Real-World Analogy
Imagine a furniture factory.
- You can order a full set of Victorian-style furniture: chair, bed, and sofa.
- Or a full set of Modern-style furniture: chair, bed, and sofa.
You don’t build the chair yourself — you tell the factory:
“Give me a Victorian chair.”
“Give me a Modern bed.”
Each style has its own factory, and each factory knows how to build its own version of each piece.
๐ง Summary
| Concept | Explanation |
|---|---|
| Abstract Factory | A factory that creates related products as a matching family. |
| Purpose | Produce related objects that should be used together. |
| Benefit | Keeps object creation consistent, organized, and clean. |
| Client code | Doesn’t care about exact classes — it uses interfaces. |
UML / ORM
๐ Abstract Factory — Participants (for JavaScript Students)
AbstractFactory
- Defines functions for creating a family of related objects.
- Only describes method names, not real object creation.
- Helps ConcreteFactory classes follow the same structure.
ConcreteFactory
- Implements the creation functions from AbstractFactory.
- Returns real JavaScript objects for one product family.
- Ensures its created objects are compatible together.
AbstractProduct
- Describes required behavior of a specific product type.
- Acts like an interface JavaScript developers follow.
- Contains no real working code inside.
ConcreteProduct
- Real JavaScript objects created by a ConcreteFactory.
- Implements all behavior defined by its AbstractProduct.
- Matches the product family of its factory.
Client
- Works only with AbstractFactory and AbstractProduct types.
- Never depends on ConcreteProduct classes directly.
- Can switch product families by changing factories.
Project Overview
This project demonstrates the Abstract Factory design pattern, following the class structure and naming conventions from pages 84–85 of the GoF book Design Patterns: Elements of Reusable Object-Oriented Software.
Each product family consists of related products (A and B). The client uses the abstract factory and product interfaces, remaining completely decoupled from concrete implementations.
๐ File Structure (Creation Order)
AbstractFactory.js
AbstractProductA.js
AbstractProductB.js
ProductA1.js
ProductA2.js
ProductB1.js
ProductB2.js
ConcreteFactory1.js
ConcreteFactory2.js
Client.js
index.js
๐งฑ Abstract Factory & Products
AbstractFactory.js
// Abstract class declaring creation methods for abstract product families
class AbstractFactory {
// Method to create a product of type A
createProductA() {
throw new Error("createProductA() must be implemented."); // Forces subclasses to override
}
// Method to create a product of type B
createProductB() {
throw new Error("createProductB() must be implemented."); // Forces subclasses to override
}
}
module.exports = AbstractFactory;
AbstractProductA.js
// Abstract base class for product family A
class AbstractProductA {
// Abstract method that all ProductA variants must implement
usefulFunctionA() {
throw new Error("usefulFunctionA() must be implemented.");
}
}
module.exports = AbstractProductA;
AbstractProductB.js
// Abstract base class for product family B
class AbstractProductB {
// Abstract method that must be implemented by ProductB variants
usefulFunctionB() {
throw new Error("usefulFunctionB() must be implemented.");
}
// Another abstract method that collaborates with ProductA
anotherUsefulFunctionB(collaborator) {
throw new Error("anotherUsefulFunctionB() must be implemented.");
}
}
module.exports = AbstractProductB;
๐งฑ Concrete Products
ProductA1.js
const AbstractProductA = require('./AbstractProductA');
// Concrete implementation of AbstractProductA
class ProductA1 extends AbstractProductA {
usefulFunctionA() {
return 'ProductA1: The result of the product A1.'; // Simulate behavior
}
}
module.exports = ProductA1;
ProductA2.js
const AbstractProductA = require('./AbstractProductA');
// Concrete implementation of AbstractProductA
class ProductA2 extends AbstractProductA {
usefulFunctionA() {
return 'ProductA2: The result of the product A2.'; // Simulate behavior
}
}
module.exports = ProductA2;
ProductB1.js
const AbstractProductB = require('./AbstractProductB');
// Concrete implementation of AbstractProductB
class ProductB1 extends AbstractProductB {
usefulFunctionB() {
return 'ProductB1: The result of the product B1.';
}
anotherUsefulFunctionB(collaborator) {
return `ProductB1: Collaborating with (${collaborator.usefulFunctionA()})`;
}
}
module.exports = ProductB1;
ProductB2.js
const AbstractProductB = require('./AbstractProductB');
// Concrete implementation of AbstractProductB
class ProductB2 extends AbstractProductB {
usefulFunctionB() {
return 'ProductB2: The result of the product B2.';
}
anotherUsefulFunctionB(collaborator) {
return `ProductB2: Collaborating with (${collaborator.usefulFunctionA()})`;
}
}
module.exports = ProductB2;
๐️ Concrete Factories
ConcreteFactory1.js
const AbstractFactory = require('./AbstractFactory');
const ProductA1 = require('./ProductA1');
const ProductB1 = require('./ProductB1');
// ConcreteFactory1 creates ProductA1 and ProductB1
class ConcreteFactory1 extends AbstractFactory {
createProductA() {
return new ProductA1();
}
createProductB() {
return new ProductB1();
}
}
module.exports = ConcreteFactory1;
ConcreteFactory2.js
const AbstractFactory = require('./AbstractFactory');
const ProductA2 = require('./ProductA2');
const ProductB2 = require('./ProductB2');
// ConcreteFactory2 creates ProductA2 and ProductB2
class ConcreteFactory2 extends AbstractFactory {
createProductA() {
return new ProductA2();
}
createProductB() {
return new ProductB2();
}
}
module.exports = ConcreteFactory2;
๐จ๐ป Client
Client.js
// Client works with products only via their abstract interfaces
class Client {
constructor(factory) {
this.productA = factory.createProductA();
this.productB = factory.createProductB();
}
run() {
console.log(this.productB.usefulFunctionB());
console.log(this.productB.anotherUsefulFunctionB(this.productA));
}
}
module.exports = Client;
๐ index.js (Runner)
index.js
const ConcreteFactory1 = require('./ConcreteFactory1');
const ConcreteFactory2 = require('./ConcreteFactory2');
const Client = require('./Client');
console.log('Client: Testing client code with the first factory type...');
const client1 = new Client(new ConcreteFactory1());
client1.run();
console.log('\nClient: Testing the same client code with the second factory type...');
const client2 = new Client(new ConcreteFactory2());
client2.run();
๐งช Expected Output
Client: Testing client code with the first factory type...
ProductB1: The result of the product B1.
ProductB1: Collaborating with (ProductA1: The result of the product A1.)
Client: Testing the same client code with the second factory type...
ProductB2: The result of the product B2.
ProductB2: Collaborating with (ProductA2: The result of the product A2.)
๐ References
- Design Patterns: Elements of Reusable Object-Oriented Software, GoF
- Pages 84–85 — Abstract Factory Pattern
✅ To Run
node index.js
Ensure all files are in the same directory or adjust module paths accordingly.
SWOT
Strengths
- Consistent Families: Ensures related objects work together properly by producing them from the same family.
- Flexible Creation: Swap entire sets of products without changing the code that uses them.
- Organized Code: Encourages modular thinking and clearer architecture.
Weaknesses
- Many Classes: Can overwhelm beginners due to multiple factory and product files.
- Hard Setup: Looks bulky and abstract before the payoff becomes visible.
- Abstract Thinking: Requires comfort with polymorphism and interface-style coding.
Opportunities
- Cross-Platform Skills: Shows how apps adapt to web/mobile/game environments with matching families.
- Design Practice: Builds strong understanding of abstraction and architecture.
- Team Projects: Helps students learn consistent structure across modules.
Threats
- Over-Engineering: Using factories when simple constructors would do can reduce clarity.
- Learning Fatigue: Too many layers can frustrate beginners.
- Integration Conflicts: Mixing creational patterns can confuse which one to use.

No comments:
Post a Comment