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

RMAG news

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
.

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”)

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);

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)

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);

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()

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();

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)

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);

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

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: Original