Abstract Factory Pattern

Posted by Dustin Boston in .

The Abstract Factory Pattern is a creational design pattern that enables the creation of families of related objects without specifying their concrete classes, promoting consistency and flexibility.


The Abstract Factory Pattern is a creational design pattern that provides a way to create families of related objects without specifying their concrete classes. This pattern ensures consistency among objects in a family and makes it easier
to add new variants or configurations, promoting flexibility in software design.

Source Code Listing

classes.ts

// AbstractProductA
export abstract class Modal {
  constructor(public argument: string) {
    console.log(argument);
  }
}

// AbstractProductB
export abstract class Button {
  constructor(public argument: string) {
    console.log(argument);
  }
}

// ConcreteProductA1
export class DarkModal extends Modal {}
// ConcreteProductA2
export class DarkButton extends Button {}
// ConcreteProductB1
export class LightModal extends Modal {}
// ConcreteProductB2
export class LightButton extends Button {}

// AbstractFactory
export abstract class ThemedComponent {
  abstract createButton(): Button;
  abstract createModal(): Modal;
}

// ConcreteFactory1
export class DarkThemedComponent extends ThemedComponent {
  createModal(): Modal {
    return new DarkModal("DarkModal");
  }

  createButton(): Button {
    return new DarkButton("DarkButton");
  }
}

// ConcreteFactory2
export class LightThemedComponent extends ThemedComponent {
  createModal(): Modal {
    return new LightModal("LightModal");
  }

  createButton(): Button {
    return new LightButton("LightButton");
  }
}

export class Client {
  modal: Modal;
  button: Button;

  constructor(factory: ThemedComponent) {
    this.modal = factory.createModal();
    this.button = factory.createButton();
  }
}

components.jsx

import React from "react"; // eslint-disable-line no-unused-vars
import ReactDOM from "react-dom/client";

export const Button = ({background, color, children}) => {
  return (
    <>
      <button type="button" style={{background, color}}>
        {children}
      </button>
    </>
  );
};

export const Modal = ({background, color, children}) => {
  return (
    <>
      <dialog style={{background, color}} open>
        {children}
      </dialog>
    </>
  );
};

export const DarkButton = ({onClick}) => {
  return (
    <Button background="gray" color="white" onClick={onClick}>
      Dark Button
    </Button>
  );
};

export const DarkModal = ({children}) => (
  <Modal background="black" color="white">
    {children}
  </Modal>
);

export const DarkThemedComponent = ({type, ...parameters}) =>
  type === "modal" ? (
    <DarkModal {...parameters} />
  ) : (
    <DarkButton {...parameters} />
  );

export const LightButton = ({onClick}) => {
  return (
    <Button background="white" color="black" onClick={onClick}>
      Light Button
    </Button>
  );
};

export const LightModal = ({children}) => (
  <Modal background="white" color="black">
    {children}
  </Modal>
);

export const LightThemedComponent = ({type, ...parameters}) =>
  type === "modal" ? (
    <LightModal {...parameters} />
  ) : (
    <LightButton {...parameters} />
  );

export const ThemedComponent = ({type, theme, children}) => {
  return (
    <div>
      {theme === "light" && (
        <LightThemedComponent type={type}>{children}</LightThemedComponent>
      )}
      {theme === "dark" && (
        <DarkThemedComponent type={type}>{children}</DarkThemedComponent>
      )}
    </div>
  );
};

export const Client = () => (
  <>
    <ThemedComponent theme="dark" type="modal">
      <ThemedComponent theme="dark" type="button" />
    </ThemedComponent>
  </>
);

// eslint-disable-next-line no-undef
ReactDOM.createRoot(document.querySelector("#root")).render(<Client />);