The Singleton pattern ensures that a class has only one instance and provides a global point of access to that instance.
class Singleton {
private static instance: Singleton;
private constructor() {}
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}
const instance1 = Singleton.getInstance();
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true
The Factory Method pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate.
interface Button {
render(): void;
}
class WindowsButton implements Button {
render(): void {
console.log("Render Windows button");
}
}
class MacButton implements Button {
render(): void {
console.log("Render Mac button");
}
}
abstract class GUIFactory {
abstract createButton(): Button;
renderButton(): void {
const button = this.createButton();
button.render();
}
}
class WindowsGUIFactory extends GUIFactory {
createButton(): Button {
return new WindowsButton();
}
}
class MacGUIFactory extends GUIFactory {
createButton(): Button {
return new MacButton();
}
}
const factory = new WindowsGUIFactory();
factory.renderButton(); // Render Windows button
The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
interface Button {
render(): void;
}
class WindowsButton implements Button {
render(): void {
console.log("Render Windows button");
}
}
class MacButton implements Button {
render(): void {
console.log("Render Mac button");
}
}
interface GUIFactory {
createButton(): Button;
}
class WindowsFactory implements GUIFactory {
createButton(): Button {
return new WindowsButton();
}
}
class MacFactory implements GUIFactory {
createButton(): Button {
return new MacButton();
}
}
function renderUI(factory: GUIFactory): void {
const button = factory.createButton();
button.render();
}
const windowsFactory = new WindowsFactory();
renderUI(windowsFactory); // Render Windows button
const macFactory = new MacFactory();
renderUI(macFactory); // Render Mac button
The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
interface Builder {
buildPartA(): void;
buildPartB(): void;
getProduct(): Product;
}
class ConcreteBuilder implements Builder {
private product: Product = new Product();
buildPartA(): void {
this.product.add("Part A");
}
buildPartB(): void {
this.product.add("Part B");
}
getProduct(): Product {
return this.product;
}
}
class Product {
private parts: string[] = [];
add(part: string): void {
this.parts.push(part);
}
show(): void {
console.log("Product parts: " + this.parts.join(", "));
}
}
class Director {
construct(builder: Builder): void {
builder.buildPartA();
builder.buildPartB();
}
}
const builder = new ConcreteBuilder();
const director = new Director();
director.construct(builder);
const product = builder.getProduct();
product.show(); // Product parts: Part A, Part B
The Prototype pattern allows creating new objects by cloning an existing object, called the prototype, rather than using a constructor.
interface Clonable {
clone(): Clonable;
}
class Prototype implements Clonable {
private field: number;
constructor(field: number) {
this.field = field;
}
clone(): Prototype {
return new Prototype(this.field);
}
show(): void {
console.log("Field value: " + this.field);
}
}
const prototype = new Prototype(42);
const clone = prototype.clone();
prototype.show(); // Field value: 42
clone.show(); // Field value: 42
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
interface Observer {
update(subject: Subject): void;
}
class ConcreteObserver implements Observer {
private subject: Subject;
private state: number;
constructor(subject: Subject) {
this.subject = subject;
this.subject.attach(this);
}
update(subject: Subject): void {
if (subject === this.subject) {
this.state = subject.getState();
console.log("Observer updated: " + this.state);
}
}
}
class Subject {
private state: number;
private observers: Observer[] = [];
getState(): number {
return this.state;
}
setState(state: number): void {
this.state = state;
this.notify();
}
attach(observer: Observer): void {
this.observers.push(observer);
}
private notify(): void {
for (const observer of this.observers) {
observer.update(this);
}
}
}
const subject = new Subject();
const observer1 = new ConcreteObserver(subject);
const observer2 = new ConcreteObserver(subject);
subject.setState(42); // Observer updated: 42 (x2)
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the algorithm to be selected at runtime.
interface Strategy {
execute(a: number, b: number): number;
}
class AddStrategy implements Strategy {
execute(a: number, b: number): number {
return a + b;
}
}
class SubtractStrategy implements Strategy {
execute(a: number, b: number): number {
return a - b;
}
}
class Context {
private strategy: Strategy;
constructor(strategy: Strategy) {
this.strategy = strategy;
}
setStrategy(strategy: Strategy): void {
this.strategy = strategy;
}
executeStrategy(a: number, b: number): number {
return this.strategy.execute(a, b);
}
}
const context = new Context(new AddStrategy());
console.log(context.executeStrategy(3, 4)); // 7
context.setStrategy(new SubtractStrategy());
console.log(context.executeStrategy(3, 4)); // -1
The Command pattern encapsulates a request as an object, allowing you to parameterize clients with different requests, queue or log requests, and support undoable operations.
interface Command {
execute(): void;
}
class ConcreteCommand implements Command {
private receiver: Receiver;
constructor(receiver: Receiver) {
this.receiver = receiver;
}
execute(): void {
this.receiver.action();
}
}
class Receiver {
action(): void {
console.log("Receiver action called");
}
}
class Invoker {
private command: Command;
setCommand(command: Command): void {
this.command = command;
}
executeCommand(): void {
this.command.execute();
}
}
const receiver = new Receiver();
const command = new ConcreteCommand(receiver);
const invoker = new Invoker();
invoker.setCommand(command);
invoker.executeCommand(); // Receiver action called
The Decorator pattern attaches additional responsibilities to an object dynamically. It provides a flexible alternative to subclassing for extending functionality.
interface Component {
operation(): string;
}
class ConcreteComponent implements Component {
operation(): string {
return "ConcreteComponent";
}
}
class Decorator implements Component {
protected component: Component;
constructor(component: Component) {
this.component = component;
}
operation(): string {
return this.component.operation();
}
}
class ConcreteDecoratorA extends Decorator {
operation(): string {
return `ConcreteDecoratorA(${super.operation()})`;
}
}
class ConcreteDecoratorB extends Decorator {
operation(): string {
return `ConcreteDecoratorB(${super.operation()})`;
}
}
const concreteComponent = new ConcreteComponent();
const decoratorA = new ConcreteDecoratorA(concreteComponent);
const decoratorB = new ConcreteDecoratorB(decoratorA);
console.log(decoratorB.operation()); // ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))
The Adapter pattern converts the interface of a class into another interface clients expect. It allows classes with incompatible interfaces to work together.
interface Target {
request(): string;
}
class Adaptee {
specificRequest(): string {
return "Adaptee";
}
}
class Adapter implements Target {
private adaptee: Adaptee;
constructor(adaptee: Adaptee) {
this.adaptee = adaptee;
}
request(): string {
return `Adapter(${this.adaptee.specificRequest()})`;
}
}
const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
console.log(adapter.request()); // Adapter(Adaptee)
The Facade pattern provides a unified interface to a set of interfaces in a subsystem. It defines a higher-level interface that makes the subsystem easier to use.
class SubsystemA {
operationA(): string {
return "SubsystemA";
}
}
class SubsystemB {
operationB(): string {
return "SubsystemB";
}
}
class Facade {
private subsystemA: SubsystemA;
private subsystemB: SubsystemB;
constructor(subsystemA: SubsystemA, subsystemB: SubsystemB) {
this.subsystemA = subsystemA;
this.subsystemB = subsystemB;
}
operation(): string {
return `${this.subsystemA.operationA()} + ${this.subsystemB.operationB()}`;
}
}
const subsystemA = new SubsystemA();
const subsystemB = new SubsystemB();
const facade = new Facade(subsystemA, subsystemB);
console.log(facade.operation()); // SubsystemA + SubsystemB
The Bridge pattern decouples an abstraction from its implementation, enabling them to vary independently.
interface Implementor {
operationImp(): string;
}
class Abstraction {
protected implementor: Implementor;
constructor(implementor: Implementor) {
this.implementor = implementor;
}
operation(): string {
return this.implementor.operationImp();
}
}
class ConcreteImplementorA implements Implementor {
operationImp(): string {
return "ConcreteImplementorA";
}
}
class ConcreteImplementorB implements Implementor {
operationImp(): string {
return "ConcreteImplementorB";
}
}
const implementorA = new ConcreteImplementorA();
const abstractionA = new Abstraction(implementorA);
console.log(abstractionA.operation()); // ConcreteImplementorA
const implementorB = new ConcreteImplementorB();
const abstractionB = new Abstraction(implementorB);
console.log(abstractionB.operation()); // ConcreteImplementorB
The Composite pattern composes objects into tree structures to represent part-whole hierarchies. It lets clients treat individual objects and compositions of objects uniformly.
interface Component {
operation(): string;
}
class Leaf implements Component {
operation(): string {
return "Leaf";
}
}
class Composite implements Component {
private children: Component[] = [];
add(component: Component): void {
this.children.push(component);
}
operation(): string {
return `Composite(${this.children.map(child => child.operation()).join(", ")})`;
}
}
const composite = new Composite();
const leaf1 = new Leaf();
const leaf2 = new Leaf();
composite.add(leaf1);
composite.add(leaf2);
console.log(composite.operation()); // Composite(Leaf, Leaf)
The Flyweight pattern uses sharing to support large numbers of fine-grained objects efficiently.
interface Flyweight {
operation(extrinsicState: string): string;
}
class ConcreteFlyweight implements Flyweight {
operation(extrinsicState: string): string {
return `ConcreteFlyweight(${extrinsicState})`;
}
}
class FlyweightFactory {
private flyweights: { [key: string]: Flyweight } = {};
getFlyweight(key: string): Flyweight {
if (!this.flyweights[key]) {
this.flyweights[key] = new ConcreteFlyweight();
}
return this.flyweights[key];
}
}
const factory = new FlyweightFactory();
const flyweightA = factory.getFlyweight("A");
const flyweightB = factory.getFlyweight("B");
console.log(flyweightA.operation("X")); // ConcreteFlyweight(X)
console.log(flyweightB.operation("Y")); // ConcreteFlyweight(Y)
The Proxy pattern provides a surrogate or placeholder for another object to control access to it.
interface Subject {
request(): string;
}
class RealSubject implements Subject {
request(): string {
return "RealSubject";
}
}
class Proxy implements Subject {
private realSubject: RealSubject;
request(): string {
if (!this.realSubject) {
this.realSubject = new RealSubject();
}
return `Proxy(${this.realSubject.request()})`;
}
}
const proxy = new Proxy();
console.log(proxy.request()); // Proxy(RealSubject)
The Chain of Responsibility pattern avoids coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. The objects are chained together, and the request is passed along the chain until an object handles it.
interface Handler {
setNext(handler: Handler): Handler;
handle(request: string): string | null;
}
abstract class AbstractHandler implements Handler {
private nextHandler: Handler | null = null;
setNext(handler: Handler): Handler {
this.nextHandler = handler;
return handler;
}
handle(request: string): string | null {
if (this.nextHandler) {
return this.nextHandler.handle(request);
}
return null;
}
}
class ConcreteHandlerA extends AbstractHandler {
handle(request: string): string | null {
if (request === "A") {
return "Handled by ConcreteHandlerA";
}
return super.handle(request);
}
}
class ConcreteHandlerB extends AbstractHandler {
handle(request: string): string | null {
if (request === "B") {
return "Handled by ConcreteHandlerB";
}
return super.handle(request);
}
}
const handlerA = new ConcreteHandlerA();
const handlerB = new ConcreteHandlerB();
handlerA.setNext(handlerB);
console.log(handlerA.handle("A")); // Handled by ConcreteHandlerA
console.log(handlerA.handle("B")); // Handled by ConcreteHandlerB
The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
interface Iterator {
next(): T | null;
hasNext(): boolean;
}
interface Iterable {
createIterator(): Iterator;
}
class ConcreteIterator implements Iterator {
private collection: number[];
private position: number = 0;
constructor(collection: number[]) {
this.collection = collection;
}
next(): number | null {
if (this.hasNext()) {
const result = this.collection[this.position];
this.position += 1;
return result;
}
return null;
}
hasNext(): boolean {
return this.position < this.collection.length;
}
}
class ConcreteIterable implements Iterable {
private collection: number[] = [1, 2, 3, 4, 5];
createIterator(): Iterator {
return new ConcreteIterator(this.collection);
}
}
const iterable = new ConcreteIterable();
const iterator = iterable.createIterator();
while (iterator.hasNext()) {
console.log(iterator.next()); // 1, 2, 3, 4, 5
}
The Mediator pattern defines an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly and allows their interaction to vary independently.
interface Mediator {
notify(sender: Colleague, event: string): void;
}
class Colleague {
protected mediator: Mediator;
constructor(mediator: Mediator) {
this.mediator = mediator;
}
sendEvent(event: string): void {
this.mediator.notify(this, event);
}
}
class ConcreteColleagueA extends Colleague {
actionA(): void {
console.log("ConcreteColleagueA handles actionA.");
}
actionB(): void {
console.log("ConcreteColleagueA handles actionB.");
}
}
class ConcreteColleagueB extends Colleague {
actionC(): void {
console.log("ConcreteColleagueB handles actionC.");
}
}
class ConcreteMediator implements Mediator {
private colleagueA: ConcreteColleagueA;
private colleagueB: ConcreteColleagueB;
constructor(colleagueA: ConcreteColleagueA, colleagueB: ConcreteColleagueB) {
this.colleagueA = colleagueA;
this.colleagueB = colleagueB;
}
notify(sender: Colleague, event: string): void {
if (sender instanceof ConcreteColleagueA) {
if (event === "eventA") {
this.colleagueA.actionA();
} else if (event === "eventB") {
this.colleagueA.actionB();
}
} else if (sender instanceof ConcreteColleagueB) {
if (event === "eventC") {
this.colleagueB.actionC();
}
}
}
}
const mediator = new ConcreteMediator(null, null);
const colleagueA = new ConcreteColleagueA(mediator);
const colleagueB = new ConcreteColleagueB(mediator);
mediator["colleagueA"] = colleagueA;
mediator["colleagueB"] = colleagueB;
colleagueA.sendEvent("eventA"); // ConcreteColleagueA handles actionA.
colleagueA.sendEvent("eventB"); // ConcreteColleagueA handles actionB.
colleagueB.sendEvent("eventC"); // ConcreteColleagueB handles actionC.
The Memento pattern captures and externalizes an object's internal state so that it can be restored later without violating encapsulation.
class Originator {
private state: string;
setState(state: string): void {
this.state = state;
}
createMemento(): Memento {
return new Memento(this.state);
}
restore(memento: Memento): void {
this.state = memento.getState();
}
}
class Memento {
private state: string;
constructor(state: string) {
this.state = state;
}
getState(): string {
return this.state;
}
}
const originator = new Originator();
originator.setState("State1");
const memento = originator.createMemento();
originator.setState("State2");
originator.restore(memento);
console.log(originator["state"]); // State1
The State pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.
interface State {
handle(context: Context): void;
}
class ConcreteStateA implements State {
handle(context: Context): void {
console.log("ConcreteStateA handles.");
context.setState(new ConcreteStateB());
}
}
class ConcreteStateB implements State {
handle(context: Context): void {
console.log("ConcreteStateB handles.");
context.setState(new ConcreteStateA());
}
}
class Context {
private state: State;
constructor(state: State) {
this.state = state;
}
setState(state: State): void {
this.state = state;
}
request(): void {
this.state.handle(this);
}
}
const context = new Context(new ConcreteStateA());
context.request(); // ConcreteStateA handles.
context.request(); // ConcreteStateB handles.
The Template Method pattern defines the skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure.
abstract class AbstractClass {
templateMethod(): void {
this.primitiveOperation1();
this.primitiveOperation2();
}
abstract primitiveOperation1(): void;
abstract primitiveOperation2(): void;
}
class ConcreteClassA extends AbstractClass {
primitiveOperation1(): void {
console.log("ConcreteClassA: PrimitiveOperation1");
}
primitiveOperation2(): void {
console.log("ConcreteClassA: PrimitiveOperation2");
}
}
class ConcreteClassB extends AbstractClass {
primitiveOperation1(): void {
console.log("ConcreteClassB: PrimitiveOperation1");
}
primitiveOperation2(): void {
console.log("ConcreteClassB: PrimitiveOperation2");
}
}
const concreteA = new ConcreteClassA();
concreteA.templateMethod();
// ConcreteClassA: PrimitiveOperation1
// ConcreteClassA: PrimitiveOperation2
const concreteB = new ConcreteClassB();
concreteB.templateMethod();
// ConcreteClassB: PrimitiveOperation1
// ConcreteClassB: PrimitiveOperation2
The Visitor pattern represents an operation to be performed on the elements of an object structure without changing the classes on which it operates. It allows defining new operations without modifying existing classes.
interface Element {
accept(visitor: Visitor): void;
}
class ConcreteElementA implements Element {
accept(visitor: Visitor): void {
visitor.visitConcreteElementA(this);
}
operationA(): string {
return "ConcreteElementA";
}
}
class ConcreteElementB implements Element {
accept(visitor: Visitor): void {
visitor.visitConcreteElementB(this);
}
operationB(): string {
return "ConcreteElementB";
}
}
interface Visitor {
visitConcreteElementA(element: ConcreteElementA): void;
visitConcreteElementB(element: ConcreteElementB): void;
}
class ConcreteVisitor implements Visitor {
visitConcreteElementA(element: ConcreteElementA): void {
console.log(`Visited ${element.operationA()}`);
}
visitConcreteElementB(element: ConcreteElementB): void {
console.log(`Visited ${element.operationB()}`);
}
}
const elements: Element[] = [new ConcreteElementA(), new ConcreteElementB()];
const visitor = new ConcreteVisitor();
elements.forEach((element) => element.accept(visitor));
// Visited ConcreteElementA
// Visited ConcreteElementB
The Interpreter pattern specifies how to evaluate sentences in a language. It involves building an abstract syntax tree (AST) and defining an interpreter to evaluate it. This pattern can be useful for designing compilers, parsers, or calculators.
interface Expression {
interpret(): number;
}
class NumberExpression implements Expression {
private value: number;
constructor(value: number) {
this.value = value;
}
interpret(): number {
return this.value;
}
}
class AddExpression implements Expression {
private left: Expression;
private right: Expression;
constructor(left: Expression, right: Expression) {
this.left = left;
this.right = right;
}
interpret(): number {
return this.left.interpret() + this.right.interpret();
}
}
const num1 = new NumberExpression(3);
const num2 = new NumberExpression(5);
const addExpression = new AddExpression(num1, num2);
console.log(addExpression.interpret()); // 8