Skip to content

adamrichardturner/design-patterns

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

23 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Design Patterns in TypeScript

Design Patterns Banner

This repository contains implementations and explanations of various design patterns organized by category. Each pattern includes detailed explanations, code examples, and practical use cases in TypeScript.

Prerequisites

Before diving into design patterns, make sure you're familiar with core TypeScript concepts:

  • TypeScript Fundamentals - Contains prerequisite TypeScript skills and concepts to help you better understand the pattern implementations.

1. Creational Patterns

Creational patterns focus on object creation mechanisms, trying to create objects in a manner suitable to the situation.

  • Factory Pattern - Encapsulates object creation logic by providing an interface for creating instances of a class
  • Abstract Factory Pattern - Creates families of related objects without specifying their concrete classes
  • Builder Pattern - Separates the construction of complex objects from their representation
  • Prototype Pattern - Creates new objects by copying existing objects
  • Singleton Pattern - Ensures a class has only one instance and provides a global point of access to it

2. Structural Patterns

Structural patterns focus on how classes and objects are composed to form larger structures.

  • Decorator Pattern - Attaches additional responsibilities to objects dynamically
  • Adapter Pattern - Allows incompatible interfaces to work together
  • Facade Pattern - Provides a simplified interface to a complex subsystem
  • Bridge Pattern - Separates an abstraction from its implementation
  • Composite Pattern - Composes objects into tree structures to represent part-whole hierarchies
  • Flyweight Pattern - Minimizes memory usage by sharing common parts of state between multiple objects
  • Proxy Pattern - Provides a surrogate or placeholder for another object to control access to it

3. Behavioral Patterns

Behavioral patterns focus on communication between objects, how objects interact and distribute responsibility.

How to Use This Repository

Each pattern folder contains detailed markdown files explaining:

  1. What the pattern is and its core concepts
  2. When and why to use the pattern
  3. TypeScript implementation examples
  4. Real-world use cases and applications

Browse through the categories and click on specific patterns to learn more about their implementation and usage.

Design Pattern Selection Guide

Below is a quick reference table to help you choose the right design pattern for your specific use case:

Pattern Category Best Use Cases Key Benefits
Factory Creational • Creating objects without specifying exact class
• When subclasses decide which class to instantiate
• When you need to delegate responsibility to helper subclasses
• Decouples client code from concrete classes
• Flexibility in creating different objects
• Centralizes complex object creation
Abstract Factory Creational • Creating families of related objects
• When system needs to be independent of product creation
• When you need to ensure compatibility between various products
• Isolates concrete classes
• Simplifies exchanging product families
• Promotes consistency among products
Builder Creational • Constructing complex objects step by step
• When object construction should be separate from representation
• When you need different representations of an object
• Controls construction process
• Allows fine-grained object creation
• Isolates complex construction code
Prototype Creational • Avoiding expensive object creation
• When classes to instantiate are specified at runtime
• Avoiding building a class hierarchy of factories
• Reduces subclassing
• Hides complexity of creating new instances
• Adds/removes products at runtime
Singleton Creational • Ensuring a class has just a single instance
• When you need stricter control over global variables
• When an object needs to coordinate actions across system
• Controlled access to sole instance
• Reduced namespace pollution
• Can be refined to permit a controlled number of instances
Decorator Structural • Adding responsibilities to objects dynamically
• When extension by subclassing is impractical
• When you need to modify behavior without affecting other objects
• More flexible than static inheritance
• Avoids feature-laden classes
• Combines multiple behaviors
Adapter Structural • Making incompatible interfaces compatible
• When reusing existing class with incompatible interface
• When creating reusable class that cooperates with unrelated classes
• Allows classes to work together that couldn't otherwise
• Improves reusability of legacy code
• Increases cleanliness through separation of interfaces
Facade Structural • Providing a simplified interface to a complex subsystem
• When you need to layer your subsystems
• When you want to reduce tight coupling between clients and subsystems
• Shields clients from subsystem components
• Promotes weak coupling
• Simplifies usage of complex systems
Bridge Structural • Separating an abstraction from its implementation
• When both abstraction and implementation should be extensible
• When implementation changes shouldn't impact client code
• Decouples interface from implementation
• Improves extensibility
• Hides implementation details from clients
Composite Structural • Representing part-whole hierarchies of objects
• When clients should ignore differences between compositions and individual objects
• Building tree structures
• Simplifies client code
• Makes adding new types of components easier
• Provides flexibility in building complex structures
Flyweight Structural • When you need to support large numbers of similar objects efficiently
• Reducing memory usage when application uses many identical objects
• When most object state can be made extrinsic
• Reduces memory usage
• Improves performance
• Allows efficient sharing of state
Proxy Structural • Controlling access to another object
• Adding functionality when simply using an object
• When you need lazy-loading, caching, logging, or access control
• Controls access to original object
• Can perform operations before/after requests
• Can work transparently to the client
Command Behavioral • Parameterizing objects by an action to perform
• Specifying, queuing, and executing requests at different times
• Supporting undoable operations
• Decouples sender from receiver
• Allows queueing of commands
• Supports undo/redo operations
Chain of Responsibility Behavioral • When more than one object may handle a request
• When the handler isn't known a priori
• When you want to issue a request to one of several objects without specifying the receiver explicitly
• Reduces coupling between sender and receivers
• Increases flexibility in assigning responsibilities
• Can dynamically change the chain at runtime
Observer Behavioral • When a change to one object requires changing others
• When an object should notify unknown objects
• When you need a publish-subscribe relationship
• Supports loose coupling
• Allows dynamic relationships
• Enables broadcast communication
Interpreter Behavioral • Implementing a specialized language
• When you need to evaluate sentences in a language
• For simple languages with well-defined grammar
• Easy to change/extend the grammar
• Implements grammar rules as classes
• Adds new expressions easily
Iterator Behavioral • Accessing elements of a collection without exposing its implementation
• Supporting multiple traversal methods
• Providing a uniform interface for traversal
• Simplifies client interface
• Supports variations in traversal
• Simplifies the aggregate interface
Mediator Behavioral • Defining how objects interact with one another
• When components need to interact in complex ways
• Reducing chaotic dependencies between objects
• Reduces coupling between components
• Centralizes communication logic
• Simplifies maintenance
Memento Behavioral • Capturing and restoring an object's internal state
• When direct interface to obtain state would expose implementation
• When checkpoint/restore functionality is needed
• Preserves encapsulation boundaries
• Simplifies originator code
• Provides history management
State Behavioral • When an object's behavior depends on its state
• When operations have large, multipart conditional statements
• When transitioning between well-defined states
• Localizes state-specific behavior
• Makes state transitions explicit
• Simplifies conditional statements
Strategy Behavioral • When you need different variants of an algorithm
• When an algorithm uses data unknown to clients
• When you need to eliminate conditional statements
• Isolates algorithm implementation details
• Enables switching between strategies at runtime
• Replaces inheritance with composition
Template Method Behavioral • Defining the skeleton of an algorithm, deferring some steps to subclasses
• When common behavior among subclasses should be factored
• Controlling subclass extensions
• Reuses common code
• Focuses subclasses on specific steps
• Enforces a defined structure
Visitor Behavioral • When you need to perform operations on all elements of a complex structure
• When classes are rarely changed but operations on them frequently change
• When you need to add new operations without changing elements
• Separates algorithms from objects they operate on
• Makes adding new operations easy
• Gathers related operations into one class

About

Easy to reference design patterns in TypeScript guide

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published