Factory Pattern

The Factory Pattern is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. It promotes loose coupling by eliminating the need for classes to explicitly instantiate objects.

A method quite often a static method, for initializing objects, and it gives you some benefits, you are not only explicit about your naming and intents, but also additional benefits and control on what you are returning.

Example

Suppose we have a simple application that generates reports in different formats such as PDF or CSV. We want to decouple the report generation logic from the client code and allow for easy addition of new report formats in the future.

enum ReportType {
  PDF = 'pdf',
  WORD = 'word',
  CSV = 'csv',
}
 
abstract class Report {
  abstract generate(): void;
}
 
class PdfReport implements Report {
  generate() {
    // Logic tp generate a pdf document
    return 'PDF Generated...';
  }
}
class CsvReport implements Report {
  generate() {
    return 'CSV Generated...';
  }
}
class WordReport implements Report {
  generate() {
    return 'Word Generated...';
  }
}
 
class ReportFactory {
  static createReport(reportType: ReportType) {
    if (reportType == ReportType.PDF) return new PdfReport();
    if (reportType == ReportType.CSV) return new CsvReport();
    if (reportType == ReportType.WORD) return new WordReport();
    throw new Error('Invalid report type');
  }
}
 
const pdf = ReportFactory.createReport(ReportType.PDF);
// Generate our pdf
pdf.generate()
 

⭐ We have a Report abstract class with a generate method representing a report generation operation. ⭐ Concrete implementations of the Report interface are provided for PDF ,Doc and CSV report formats. ⭐ The ReportFactory class contains a static createReport method that takes a type parameter and returns an instance of the appropriate report format. ⭐ Client code uses the ReportFactory to create reports without needing to know the specific implementation classes, promoting flexibility and decoupling.

This example demonstrates how the Factory Pattern can be used to encapsulate object creation logic and promote flexibility and maintainability in software design.

🔥 Creating Objects with Complex Initialization: ✅ When creating objects with complex initialization logic or multiple steps involved, the Factory Pattern can encapsulate this logic within factory methods, making the code cleaner and more maintainable.

🔥 Decoupling Object Creation from Client Code: ✅ By using factory methods, client code does not need to know the specific class of objects being created. This promotes flexibility and allows for easier modification and extension of the codebase.

🔥 Implementing Dependency Injection: ✅ Factories can be used to centralize the creation and management of dependencies in an application, making it easier to implement dependency injection principles.

While the Factory Pattern promotes loose coupling and flexibility, it can introduce additional complexity, especially in systems with a large number of concrete classes or variations.

Care should be taken to ensure that factory classes remain cohesive and do not violate the Single Responsibility Principle.

The idea of an abstract factory does not neatly map to JavaScript or Ts but it’s still worth discussing just in the general sense.