DEV Community

Cover image for Design patterns: Part One - A brief explanation of creational pattern
andres paladines
andres paladines

Posted on • Edited on

5

Design patterns: Part One - A brief explanation of creational pattern

The picture was taken from boredpanda.

Hello everyone!

Starting my first post with one of the most basic but never unnecessary topics; Design patterns.

Let's start listing them:

.
├── Creational Patterns
│   ├── Singleton
│   ├── Factory Method
│   ├── Builder
│   ├── Abstract Factory
│   ├── Prototype
├── Structural Patterns
│   ├── Adapter
│   ├── Decorator
│   ├── Facade
│   ├── Composite
│   ├── Proxy
├── Behavioral Patterns
│   ├── Observer
│   ├── Strategy
│   ├── Command
│   ├── Chain of Responsibility
│   ├── Mediator
│   ├── State
│   ├── Template Method
.
Enter fullscreen mode Exit fullscreen mode

Creational Patterns

This post will focus on Creational Patterns and how to construct them with swift and javascript. So, Let's start!

Singleton

Ensures a class has only one instance and provides a global point of access to it.
Use case: A central configuration manager for app settings.

Swift:

class ConfigurationManager {
    static let shared = ConfigurationManager()

    private init() { }

    var settings: [String: Any] = [:]

    func updateSetting(key: String, value: Any) {
        settings[key] = value
    }

    func getSetting(key: String) -> Any? {
        return settings[key]
    }
}

// Usage
ConfigurationManager.shared.updateSetting(key: "theme", value: "dark")
let theme = ConfigurationManager.shared.getSetting(key: "theme")
Enter fullscreen mode Exit fullscreen mode

JavaScript:

class ConfigurationManager {
    constructor() {
        if (ConfigurationManager.instance == null) {
            this.settings = {};
            ConfigurationManager.instance = this;
        }
        return ConfigurationManager.instance;
    }

    updateSetting(key, value) {
        this.settings[key] = value;
    }

    getSetting(key) {
        return this.settings[key];
    }
}

const instance = new ConfigurationManager();
Object.freeze(instance);

// Usage
instance.updateSetting("theme", "dark");
const theme = instance.getSetting("theme");

Enter fullscreen mode Exit fullscreen mode

Factory Method

Defines an interface for creating different objects from a superclass or protocol from another object.

Use Case: Handling different types of payment gateways (e.g., PayPal, Stripe, Apple Pay).

Swift:

enum PaymentProvider {
    case paypal
    case stripe
}

protocol PaymentGateway {
    func processPayment(amount: Double)
}

class PayPal: PaymentGateway {
    func processPayment(amount: Double) {
        print("Processing payment through PayPal: \(amount)")
    }
}

class Stripe: PaymentGateway {
    func processPayment(amount: Double) {
        print("Processing payment through Stripe: \(amount)")
    }
}

class PaymentGatewayFactory {
    static func createPaymentGateway(type: PaymentProvider) -> PaymentGateway? {
        switch type {
        case .paypal:
            return PayPal()
        case .stripe:
            return Stripe()
        default:
            return nil
        }
    }
}

// Usage
let gateway = PaymentGatewayFactory.createPaymentGateway(type: "PayPal")
gateway?.processPayment(amount: 100.0)
Enter fullscreen mode Exit fullscreen mode

JavaScript:

class PayPal {
    processPayment(amount) {
        console.log(`Processing payment through PayPal: ${amount}`);
    }
}

class Stripe {
    processPayment(amount) {
        console.log(`Processing payment through Stripe: ${amount}`);
    }
}

class PaymentGatewayFactory {
    static createPaymentGateway(type) {
        switch (type) {
            case "PayPal":
                return new PayPal();
            case "Stripe":
                return new Stripe();
            default:
                return null;
        }
    }
}

// Usage
const gateway = PaymentGatewayFactory.createPaymentGateway("PayPal");
gateway.processPayment(100.0);
Enter fullscreen mode Exit fullscreen mode

Builder

Separates the whole construction process of a complex object, allowing to create different representations of it.
Use Case: Creating complex UI components such as custom dialog boxes.

Swift

class Dialog {
    var title: String = ""
    var message: String = ""
    var buttons: [String] = []

    func show() {
        print("Title: \(title)")
        print("Message: \(message)")
        print("Buttons: \(buttons.joined(separator: ", "))")
    }
}

class DialogBuilder {
    private var dialog = Dialog()

    func setTitle(_ title: String) -> DialogBuilder {
        dialog.title = title
        return self
    }

    func setMessage(_ message: String) -> DialogBuilder {
        dialog.message = message
        return self
    }

    func addButton(_ button: String) -> DialogBuilder {
        dialog.buttons.append(button)
        return self
    }

    func build() -> Dialog {
        return dialog
    }
}

// Usage
let dialog = DialogBuilder()
    .setTitle("Warning")
    .setMessage("Are you sure you want to delete this file?")
    .addButton("Cancel")
    .addButton("Delete")
    .build()
dialog.show()
Enter fullscreen mode Exit fullscreen mode

JavaScript:

class Dialog {
    constructor() {
        this.title = "";
        this.message = "";
        this.buttons = [];
    }

    show() {
        console.log(`Title: ${this.title}`);
        console.log(`Message: ${this.message}`);
        console.log(`Buttons: ${this.buttons.join(", ")}`);
    }
}

class DialogBuilder {
    constructor() {
        this.dialog = new Dialog();
    }

    setTitle(title) {
        this.dialog.title = title;
        return this;
    }

    setMessage(message) {
        this.dialog.message = message;
        return this;
    }

    addButton(button) {
        this.dialog.buttons.push(button);
        return this;
    }

    build() {
        return this.dialog;
    }
}

// Usage
const dialog = new DialogBuilder()
    .setTitle("Warning")
    .setMessage("Are you sure you want to delete this file?")
    .addButton("Cancel")
    .addButton("Delete")
    .build();
dialog.show();
Enter fullscreen mode Exit fullscreen mode

Abstract Factory

Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
Use Case: Creating families of related or dependent objects without specifying their concrete classes.

Swift

protocol Button {
    func render()
}

class WindowsButton: Button {
    func render() {
        print("Render a button in Windows style")
    }
}

class MacOSButton: Button {
    func render() {
        print("Render a button in MacOS style")
    }
}

protocol GUIFactory {
    func createButton() -> Button
}

class WindowsFactory: GUIFactory {
    func createButton() -> Button {
        return WindowsButton()
    }
}

class MacOSFactory: GUIFactory {
    func createButton() -> Button {
        return MacOSButton()
    }
}

// Usage
func createUI(factory: GUIFactory) {
    let button = factory.createButton()
    button.render()
}

let windowsFactory = WindowsFactory()
createUI(factory: windowsFactory)

let macFactory = MacOSFactory()
createUI(factory: macFactory)
Enter fullscreen mode Exit fullscreen mode

JavaScript:

class WindowsButton {
    render() {
        console.log("Render a button in Windows style");
    }
}

class MacOSButton {
    render() {
        console.log("Render a button in MacOS style");
    }
}

class WindowsFactory {
    createButton() {
        return new WindowsButton();
    }
}

class MacOSFactory {
    createButton() {
        return new MacOSButton();
    }
}

// Usage
function createUI(factory) {
    const button = factory.createButton();
    button.render();
}

const windowsFactory = new WindowsFactory();
createUI(windowsFactory);

const macFactory = new MacOSFactory();
createUI(macFactory);
Enter fullscreen mode Exit fullscreen mode

Prototype

Creates new objects by copying an existing object, known as the prototype.
Use Case: Cloning objects to avoid the cost of creating new instances from scratch.

Swift

protocol Clonable {
    func clone() -> Self
}

class Prototype: Clonable {
    var name: String

    required init(name: String) {
        self.name = name
    }

    func clone() -> Self {
        return type(of: self).init(name: self.name)
    }
}

// Usage
let original = Prototype(name: "Original")
let copy = original.clone()
copy.name = "Changed"
print(original.name) // Output: Original
print(copy.name) // Output: Changed
Enter fullscreen mode Exit fullscreen mode

JavaScript:

class Prototype {
    constructor(name) {
        this.name = name;
    }

    clone() {
        return new Prototype(this.name);
    }
}

// Usage
const original = new Prototype("Original");
const copy = original.clone();
copy.name = "Changed";
console.log(original.name); // Output: Original
console.log(copy.name); // Output: Changed
Enter fullscreen mode Exit fullscreen mode

Billboard image

Deploy and scale your apps on AWS and GCP with a world class developer experience

Coherence makes it easy to set up and maintain cloud infrastructure. Harness the extensibility, compliance and cost efficiency of the cloud.

Learn more

Top comments (3)

Collapse
 
namix profile image
Nick

There is a typo in your last code comment
console.log(copy.name); // Output: Original the output here is "Changed".

Collapse
 
andres_paladines profile image
andres paladines

Thanks for reading it till the last line <3

Collapse
 
andres_paladines profile image
andres paladines • Edited

Thanks Nick!
Changed! xD

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more