Skip to content

Instantly share code, notes, and snippets.

@chrisribe
Last active June 23, 2023 18:11
Show Gist options
  • Save chrisribe/89bb5b9e5e04acc84e922cc46366c377 to your computer and use it in GitHub Desktop.
Save chrisribe/89bb5b9e5e04acc84e922cc46366c377 to your computer and use it in GitHub Desktop.
A cheat sheet outlining some popular design patterns, their pros and cons, and guidance on when to use them

Here's a cheat sheet outlining some popular design patterns, their pros and cons, and guidance on when to use them:

  1. Singleton Pattern:

    • Pros: Ensures only one instance of a class exists, provides a global point of access.
    • Cons: Can introduce tight coupling and make unit testing difficult.
    • Use when: You need to limit the number of instances of a class, such as for managing shared resources or global configurations.
  2. Factory Pattern:

    • Pros: Provides a centralized place to create objects, encapsulates object creation logic.
    • Cons: Can become complex as the number of product types increases.
    • Use when: You want to create objects without exposing the instantiation logic and provide a flexible way to create different types of objects.
  3. Abstract Factory Pattern:

    • Pros: Allows the creation of families of related objects, promotes loose coupling.
    • Cons: Adding new product types can be challenging.
    • Use when: You need to create families of related objects without specifying their concrete classes, and you want to ensure the compatibility of the created objects.
  4. Builder Pattern:

    • Pros: Provides a step-by-step process to construct complex objects, allows different representations.
    • Cons: Requires creating a separate builder class for each type of object.
    • Use when: You need to create complex objects with many optional parameters and want to separate the construction logic from the object itself.
  5. Prototype Pattern:

    • Pros: Allows object creation by cloning an existing object, reduces the need for subclassing.
    • Cons: Cloning complex objects can be challenging.
    • Use when: Creating new objects by cloning existing ones is more convenient than creating them from scratch, or when subclassing is not desirable.
  6. Observer Pattern:

    • Pros: Supports loose coupling between objects, provides a one-to-many dependency between objects.
    • Cons: Observers may receive unnecessary notifications.
    • Use when: You need to establish a notification mechanism where multiple objects (observers) are interested in the state changes of another object (subject).
  7. Decorator Pattern: JS

    • Pros: Allows adding behavior to objects dynamically, avoids subclass explosion.
    • Cons: Can result in many small objects and complex code.
    • Use when: You want to add additional responsibilities or behaviors to objects at runtime without modifying their source code directly.
  8. Strategy Pattern:

    • Pros: Enables selecting an algorithm dynamically, promotes interchangeable components.
    • Cons: Can result in increased complexity due to additional classes and interfaces.
    • Use when: You have a family of algorithms and want to encapsulate each one separately and make them interchangeable.
  9. Facade Pattern: JS

    • Pros: Provides a simplified interface to a complex subsystem, decouples clients from subsystems.
    • Cons: May hide important details or make the code harder to maintain if misused.
    • Use when: You want to provide a unified interface to a set of interfaces in a subsystem, or when you need to simplify a complex system for client usage.
  10. Adapter Pattern: JS

    • Pros: Allows objects with incompatible interfaces to work together, enables code reuse.
    • Cons: Can result in a more complex code structure.
    • Use when: You need to make two incompatible interfaces work together or convert the interface of a class into another interface that clients expect.

Remember, these patterns are tools to help address common software design problems. The suitability of a pattern depends on the specific context and requirements of your project. Choose patterns that align with your project's goals, maintainability, and extensibility needs.

/**
* AdaptorPattern => When you need to adapt one interface to another.
*
* In this example, we have an `OldCalculator` class that performs calculations
* using a different interface than the one expected by our client.
* To adapt it, we create an `Adapter` class called `NewCalculatorAdapter` that
* extends the `Calculator` interface. The `NewCalculatorAdapter` internally uses
* an instance of the `OldCalculator` and adapts its methods to match the `Calculator` interface.
**/
class OldCalculator {
constructor() {
console.log("Old Calculator is created.");
}
operations(a, b, operation) {
switch (operation) {
case "add":
return a + b;
case "subtract":
return a - b;
default:
throw new Error("Unsupported operation");
}
}
}
// Target: The interface that our client expects.
class Calculator {
add(a, b) {
throw new Error("This method should be implemented by the adapter.");
}
subtract(a, b) {
throw new Error("This method should be implemented by the adapter.");
}
}
// Adapter: Implements the Target interface and adapts it to the Adaptee.
class NewCalculatorAdapter extends Calculator {
constructor() {
super();
this.oldCalculator = new OldCalculator();
console.log("New Calculator Adapter is created.");
}
add(a, b) {
return this.oldCalculator.operations(a, b, "add");
}
subtract(a, b) {
return this.oldCalculator.operations(a, b, "subtract");
}
}
// Usage
const calculator = new NewCalculatorAdapter();
console.log(calculator.add(5, 3)); // Output: 8
console.log(calculator.subtract(8, 4)); // Output: 4
/**
* Decorator Pattern => Adds behaviors to objects at runtime without modifying their source code directly.
*/
function MacBook() {
this.cost = function() {
return 997;
};
this.screenSize = function() {
return 11.6;
};
}
// Decorator 1
function Memory(macbook) {
var v = macbook.cost();
macbook.cost = function() {
return v + 75;
};
}
// Decorator 2
function Engraving(macbook) {
var v = macbook.cost();
macbook.cost = function(){
return v + 200;
};
}
// Decorator 3
function Insurance(macbook) {
var v = macbook.cost();
macbook.cost = function(){
return v + 250;
};
}
var mb = new MacBook();
Memory(mb);
Engraving(mb);
Insurance(mb);
// Outputs: 1522
console.log(mb.cost());
// Outputs: 11.6
console.log(mb.screenSize());
/**
* Facade Pattern => Simplifies complex system
*
* This serves as a concrete implementation of the abstract
* Beverage, hiding its complexity behind a straightforward interface. The Facade
* associates the user of the API with this interface and hides its complexity.
*/
class CoffeMaker {
coffeeIngredients() {
return "Coffee";
}
brew() {
return "Brewing with hot water";
}
addCondiments() {
return "Add milk and sugar";
}
serve() {
drink();
}
drink() {
return "Give your coffee, enjoy !";
}
}
class DeliveryService {
constructor() {
this.address = "";
}
setDeliveryAddress(address) {
this.address = address;
}
deliver(item) {
return `Delivering ${item} to ${this.address}`;
}
}
/**
* The Facade pattern simplifies complex interface by providing a façade class that is
* easier to work with.
*/
class CoffeMakerFacade {
constructor() {
this.coffeMaker = new CoffeMaker();
this.deliveryService = new DeliveryService();
}
getCoffeeWithMilkAndDeliver(address) {
const coffee = `${this.coffeMaker.coffeeIngredients()} + ${this.coffeMaker.brew()} + ${this.coffeMaker.addCondiments()} + ${this.coffeMaker.serve()}`;
this.deliveryService.setDeliveryAddress(address);
const deliveryStatus = this.deliveryService.deliver(coffee);
return `${coffee}\n${deliveryStatus}`;
}
}
/**
* Working with the CoffeMaker class now becomes much easier. Users work with the
* CoffeMakerFacade interface, which is very simple and intuitive compared to the one the
* CoffeMaker class exposes. And if the implementation of makeCoffee changes, it becomes
* trivial to keep its facade in sync.
*/
const coffeeFacade = new CoffeMakerFacade();
const coffee = coffeeFacade.getCoffeeWithMilkAndDeliver("123 Main St");
console.log(coffee);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment