Abstract Factory Pattern
The Abstract Factory Pattern is another creational design pattern that provides an interface for creating families of related or dependent objects without specifying their concrete classes. Instead of just creating one type of object, like in the Factory Method pattern, the Abstract Factory pattern lets you create related objects that can work together.
Let's build on the ApiClientFactoryMethod example we discussed in the blog on Factory Design pattern and transition into understanding the Abstract Factory Pattern.
Difference from Factory Method
Factory Method: Deals with the creation of a single object type.
Abstract Factory: Deals with the creation of families of related objects, ensuring that objects created by the factory are compatible with each other.
Step 1: Define interfaces for each family of objects
Let us define the interfaces for logger, parser and the api client.
// ApiClient.ts
export interface ApiClient {
fetchData(endpoint: string): Promise<any>;
}
// ApiLogger.ts
export interface ApiLogger {
log(message: string): void;
}
// ApiParser.ts
export interface ApiParser {
parse(data: string): any;
}
Step 2: Create concrete implementations for each API type
//XmlApiClient.ts
import { ApiClient } from "./ApiClient";
import { ApiLogger } from "./ApiLogger";
import { ApiParser } from "./ApiParser";
export class XmlApiClient implements ApiClient {
fetchData(endpoint: string): Promise<any> {
console.log(`Fetching data from XML API: ${endpoint}`);
return Promise.resolve('{ "data": "XML data" }');
}
}
export class XmlApiLogger implements ApiLogger {
log(message: string): void {
console.log(`[XML Logger]: ${message}`);
}
}
export class XmlApiParser implements ApiParser {
parse(data: string): any {
// Mock XML parsing
return { data: "Parsed XML data" };
}
}
//YamlApiClient.ts
import { ApiClient } from "./ApiClient";
import { ApiLogger } from "./ApiLogger";
import { ApiParser } from "./ApiParser";
export class YamlApiClient implements ApiClient {
fetchData(endpoint: string): Promise<any> {
console.log(`Fetching data from YAML API: ${endpoint}`);
return Promise.resolve('{ "data": "YAML data" }');
}
}
export class YamlApiLogger implements ApiLogger {
log(message: string): void {
console.log(`[YAML Logger]: ${message}`);
}
}
export class YamlApiParser implements ApiParser {
parse(data: string): any {
// Mock YAML parsing
return { data: "Parsed YAML data" };
}
}
//JsonApiClient.ts
import { ApiClient } from "./ApiClient";
import { ApiLogger } from "./ApiLogger";
import { ApiParser } from "./ApiParser";
export class JsonApiClient implements ApiClient {
fetchData(endpoint: string): Promise<any> {
console.log(`Fetching data from JSON API: ${endpoint}`);
return Promise.resolve('{ "data": "JSON data" }');
}
}
export class JsonApiLogger implements ApiLogger {
log(message: string): void {
console.log(`[JSON Logger]: ${message}`);
}
}
export class JsonApiParser implements ApiParser {
parse(data: string): any {
console.log(`[JSON parser]: ${data}`);
return JSON.parse(data);
}
}
Step 3: Create the Abstract Factory interface
Define an abstract factory interface that groups these related components together.
//ApiFactory.ts
import { ApiClient } from "./ApiClient";
import { ApiLogger } from "./ApiLogger";
import { ApiParser } from "./ApiParser";
export interface ApiFactory {
createClient(): ApiClient;
createLogger(): ApiLogger;
createParser(): ApiParser;
}
Step 4: Implement Concrete Factories for each API type
Create the concrete factories that will produce appropriate objects for each API type.
//JsonApiFactory.ts
import { ApiClient } from "./ApiClient";
import { ApiFactory } from "./ApiFactory";
import { ApiLogger } from "./ApiLogger";
import { ApiParser } from "./ApiParser";
import { JsonApiClient, JsonApiLogger, JsonApiParser } from "./JsonAPiClient";
export class JsonApiFactory implements ApiFactory {
createClient(): ApiClient {
return new JsonApiClient();
}
createLogger(): ApiLogger {
return new JsonApiLogger();
}
createParser(): ApiParser {
return new JsonApiParser();
}
}
//YamlApiFactory.ts
import { ApiClient } from "./ApiClient";
import { ApiFactory } from "./ApiFactory";
import { ApiLogger } from "./ApiLogger";
import { ApiParser } from "./ApiParser";
import { YamlApiClient, YamlApiLogger, YamlApiParser } from "./YamlApiClient";
export class YamlApiFactory implements ApiFactory {
createClient(): ApiClient {
return new YamlApiClient();
}
createLogger(): ApiLogger {
return new YamlApiLogger();
}
createParser(): ApiParser {
return new YamlApiParser();
}
}
//XmlApiFactory.ts
import { ApiClient } from "./ApiClient";
import { ApiFactory } from "./ApiFactory";
import { ApiLogger } from "./ApiLogger";
import { ApiParser } from "./ApiParser";
import { XmlApiClient, XmlApiLogger, XmlApiParser } from "./XmlApiClient";
export class XmlApiFactory implements ApiFactory {
createClient(): ApiClient {
return new XmlApiClient();
}
createLogger(): ApiLogger {
return new XmlApiLogger();
}
createParser(): ApiParser {
return new XmlApiParser();
}
}
Step 5: Creating A Registry that will register the factories and also newer factories in the future.
import { ApiFactory } from "./ApiFactory";
class ApiClientFactoryRegistry {
private static registry: { [key: string]: ApiFactory } = {};
static registerFactory(type: string, factory: ApiFactory): void {
this.registry[type] = factory;
}
static getFactory(type: string): ApiFactory {
const factory = this.registry[type];
if (!factory) {
throw new Error(`No factory registered for type : {$type}`);
}
return factory;
}
}
The registry class has a method registerFactory which will store the mapping between the API type and its corresponding factory.
Step 6: Working code
//main.ts
//registering the three factories
import { ApiClientFactoryRegistry } from "./ApiClientFactoryRegistry";
import { JsonApiFactory } from "./JsonApiFactory";
import { XmlApiFactory } from "./XmlApiFactory";
import { YamlApiFactory } from "./YamlApiFactory";
function main() {
//register the three factories
ApiClientFactoryRegistry.registerFactory("json", new JsonApiFactory());
ApiClientFactoryRegistry.registerFactory("yaml", new YamlApiFactory());
ApiClientFactoryRegistry.registerFactory("xml", new XmlApiFactory());
//user Input or configuration input
const apiType = "json";
// Retrieving a factory
const apiFactory = ApiClientFactoryRegistry.getFactory(apiType);
const client = apiFactory.createClient();
const logger = apiFactory.createLogger();
const parser = apiFactory.createParser();
client.fetchData("your-https-endpoint").then((data) => {
logger.log("data fetched successfully");
const parsedData = parser.parse(data);
logger.log(parsedData);
});
}
main();
Conclusion:
By using the Abstract Factory pattern, you ensure that all related objects (API client, logger, parser) are created together and are compatible with each other, which can be particularly useful in complex systems where we have multiple interrelated components.
The GITHUB LINK code sample for your reference.
Subscribe to my newsletter
Read articles from Ganesh Rama Hegde directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Ganesh Rama Hegde
Ganesh Rama Hegde
Passionate Developer | Code Whisperer | Innovator Hi there! I'm a senior software developer with a love for all things tech and a knack for turning complex problems into elegant, scalable solutions. Whether I'm diving deep into TypeScript, crafting seamless user experiences in React Native, or exploring the latest in cloud computing, I thrive on the thrill of bringing ideas to life through code. I’m all about creating clean, maintainable, and efficient code, with a strong focus on best practices like the SOLID principles. My work isn’t just about writing code; it’s about crafting digital experiences that resonate with users and drive impact. Beyond the code editor, I’m an advocate for continuous learning, always exploring new tools and technologies to stay ahead in this ever-evolving field. When I'm not coding, you'll find me blogging about my latest discoveries, experimenting with side projects, or contributing to open-source communities. Let's connect, share knowledge, and build something amazing together!