From 3d3ed0f12138e08e640dca69a36dadb392b69c4d Mon Sep 17 00:00:00 2001 From: theartcher Date: Tue, 11 Nov 2025 15:00:58 +0100 Subject: [PATCH 1/2] Applied Prettier code formatter to existing pages --- docs/creational/abstract-factory.mdx | 155 +++++--- docs/creational/builder.mdx | 534 ++++++++++++++++----------- docs/creational/factory-method.mdx | 161 +++++--- docs/creational/prototype.mdx | 70 ++-- docs/creational/singleton.mdx | 100 +++-- docs/structural/adapter.mdx | 89 +++-- 6 files changed, 671 insertions(+), 438 deletions(-) diff --git a/docs/creational/abstract-factory.mdx b/docs/creational/abstract-factory.mdx index 08f92a3..04841b3 100644 --- a/docs/creational/abstract-factory.mdx +++ b/docs/creational/abstract-factory.mdx @@ -2,26 +2,25 @@ title: Abstract Factory hide_title: true description: > - It's not a pyramid scheme, it's a factory scheme. + It's not a pyramid scheme, it's a factory scheme. --- import { CreationalPatternTag } from "@site/src/components/pattern-page-features/pattern-alias-and-tag"; import TwoColumnProsCons from "@site/src/components/pattern-page-features/pros-and-cons"; -import TOCInline from '@theme/TOCInline'; +import TOCInline from "@theme/TOCInline"; import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; import Applicability from "@site/src/components/pattern-page-features/applicability"; - - + --- -The **abstract factory** pattern is a creational pattern that can help create groups of related products without specifying their concrete implementations. +The **abstract factory** pattern is a creational pattern that can help create groups of related products without specifying their concrete implementations. ## 🧩The problem -Let's say you are creating a armour shop, and you want to offer different styles of armour (e.g. medieval, modern and futuristic). Each style includes a set of related products like gloves, a chest plate and a helmet. You want to ensure that when a customer selects a particular style, all the armour pieces they receive are consistent in design and aesthetics. You will need a way to create families of related armour products without tightly coupling your code to specific implementations. +Let's say you are creating a armour shop, and you want to offer different styles of armour (e.g. medieval, modern and futuristic). Each style includes a set of related products like gloves, a chest plate and a helmet. You want to ensure that when a customer selects a particular style, all the armour pieces they receive are consistent in design and aesthetics. You will need a way to create families of related armour products without tightly coupling your code to specific implementations. ## 🛠️Solutions @@ -38,6 +37,7 @@ By applying this, the client can work with the factories and products via their An abstract factory is like a car manufacturer that produces different models of cars (e.g., sedan, SUV, truck) without specifying the exact details of each model. The manufacturer provides a common interface for creating cars, and each model implements its own specific features and characteristics. This allows customers to choose a car model based on their preferences without needing to know the intricate details of how each car is built. ### 💡Real-world examples + Common practical scenarios for applying the Abstract Factory pattern include: - GUI frameworks: Different operating systems have distinct UI components. An abstract factory can create families of related UI elements (buttons, text fields, menus) for different platforms (Windows, macOS, Linux) without specifying their concrete implementations. @@ -45,21 +45,30 @@ Common practical scenarios for applying the Abstract Factory pattern include: ## ⚖️ Pros and Cons - - ## 🔍Applicability - - + ## 🧭Implementation Plan @@ -236,11 +245,11 @@ class ArmourFactory(ABC): @abstractmethod def create_helmet(self) -> Helmet: pass - + @abstractmethod def create_chest_plate(self) -> ChestPlate: pass - + @abstractmethod def create_gloves(self) -> Gloves: pass @@ -249,20 +258,20 @@ class ArmourFactory(ABC): class MedievalArmourFactory(ArmourFactory): def create_helmet(self) -> Helmet: return MedievalHelmet() - + def create_chest_plate(self) -> ChestPlate: return MedievalChestPlate() - + def create_gloves(self) -> Gloves: return MedievalGloves() class FuturisticArmourFactory(ArmourFactory): def create_helmet(self) -> Helmet: return FuturisticHelmet() - + def create_chest_plate(self) -> ChestPlate: return FuturisticChestPlate() - + def create_gloves(self) -> Gloves: return FuturisticGloves() @@ -271,7 +280,7 @@ def equip_warrior(factory: ArmourFactory) -> None: helmet = factory.create_helmet() chest_plate = factory.create_chest_plate() gloves = factory.create_gloves() - + print(helmet.get_protection()) print(chest_plate.get_protection()) print(gloves.get_protection()) @@ -279,7 +288,7 @@ def equip_warrior(factory: ArmourFactory) -> None: # Usage if __name__ == "__main__": equip_warrior(MedievalArmourFactory()) - + equip_warrior(FuturisticArmourFactory()) ``` @@ -303,25 +312,61 @@ function AbstractFactoryDemo() { const factories = { Medieval: { name: "Medieval Armoury", - createHelmet: () => ({ piece: "Helmet", description: "Iron helmet with nose guard", defense: 15 }), - createChestPlate: () => ({ piece: "Chest Plate", description: "Heavy chainmail armor", defense: 30 }), - createGloves: () => ({ piece: "Gloves", description: "Leather gauntlets", defense: 8 }), - emoji: "⚔️" + createHelmet: () => ({ + piece: "Helmet", + description: "Iron helmet with nose guard", + defense: 15, + }), + createChestPlate: () => ({ + piece: "Chest Plate", + description: "Heavy chainmail armor", + defense: 30, + }), + createGloves: () => ({ + piece: "Gloves", + description: "Leather gauntlets", + defense: 8, + }), + emoji: "⚔️", }, Futuristic: { name: "Futuristic Tech Lab", - createHelmet: () => ({ piece: "Helmet", description: "Energy shield with HUD", defense: 40 }), - createChestPlate: () => ({ piece: "Chest Plate", description: "Nano-fiber combat suit", defense: 55 }), - createGloves: () => ({ piece: "Gloves", description: "Smart-tech grip enhancers", defense: 20 }), - emoji: "🚀" + createHelmet: () => ({ + piece: "Helmet", + description: "Energy shield with HUD", + defense: 40, + }), + createChestPlate: () => ({ + piece: "Chest Plate", + description: "Nano-fiber combat suit", + defense: 55, + }), + createGloves: () => ({ + piece: "Gloves", + description: "Smart-tech grip enhancers", + defense: 20, + }), + emoji: "🚀", }, Elven: { name: "Elven Forge", - createHelmet: () => ({ piece: "Helmet", description: "Enchanted mithril circlet", defense: 25 }), - createChestPlate: () => ({ piece: "Chest Plate", description: "Blessed elven plate", defense: 45 }), - createGloves: () => ({ piece: "Gloves", description: "Silken battle gloves", defense: 12 }), - emoji: "🧝" - } + createHelmet: () => ({ + piece: "Helmet", + description: "Enchanted mithril circlet", + defense: 25, + }), + createChestPlate: () => ({ + piece: "Chest Plate", + description: "Blessed elven plate", + defense: 45, + }), + createGloves: () => ({ + piece: "Gloves", + description: "Silken battle gloves", + defense: 12, + }), + emoji: "🧝", + }, }; // Equip a full set of armour from selected factory @@ -330,34 +375,37 @@ function AbstractFactoryDemo() { const armourSet = [ factory.createHelmet(), factory.createChestPlate(), - factory.createGloves() + factory.createGloves(), ]; setEquippedArmour(armourSet); }; // Calculate total defense - const totalDefense = equippedArmour.reduce((sum, item) => sum + item.defense, 0); + const totalDefense = equippedArmour.reduce( + (sum, item) => sum + item.defense, + 0 + ); const currentFactory = factories[factoryType]; return (

Abstract Factory — Armour Shop {currentFactory.emoji}

- + {/* Factory Selection */}
- - +
{/* Equipped Armour Display */} @@ -367,7 +415,8 @@ function AbstractFactoryDemo() {
    {equippedArmour.map((item, i) => (
  • - {item.piece}: {item.description} (+{item.defense} defense) + {item.piece}: {item.description} (+ + {item.defense} defense)
  • ))}
@@ -379,8 +428,9 @@ function AbstractFactoryDemo() { {/* Explanation */}
- Each factory creates a family of related products (helmet, chest plate, gloves) - that belong together. Try switching factories to see how the pattern ensures all pieces match! + Each factory creates a family of related products{" "} + (helmet, chest plate, gloves) that belong together. Try switching + factories to see how the pattern ensures all pieces match!
); @@ -398,7 +448,8 @@ function AbstractFactoryDemo() { --- ## 📚Sources + Information used in this page was collected from various reliable sources: - [Refactoring Guru - Abstract Factory](https://refactoring.guru/design-patterns/abstract-factory) -- [Geeks for Geeks - Abstract Factory pattern](https://www.geeksforgeeks.org/system-design/abstract-factory-pattern/) \ No newline at end of file +- [Geeks for Geeks - Abstract Factory pattern](https://www.geeksforgeeks.org/system-design/abstract-factory-pattern/) diff --git a/docs/creational/builder.mdx b/docs/creational/builder.mdx index 3703e10..19e51ab 100644 --- a/docs/creational/builder.mdx +++ b/docs/creational/builder.mdx @@ -19,9 +19,9 @@ The **Builder** pattern helps to break large, complex constructors and construct ## 🧩The problem -Imagine an object or class that is getting increasingly more complex to create. This can include initializing many properties, fields, nested objects and core logic. Such initialization logic is often buried deep within a gigantic constructor with many parameters. This can lead to code that is hard to read, maintain and understand. Additionally, if the object can be created in different ways (e.g., with different configurations), the constructor can become overloaded with multiple parameters, leading to confusion and errors. +Imagine an object or class that is getting increasingly more complex to create. This can include initializing many properties, fields, nested objects and core logic. Such initialization logic is often buried deep within a gigantic constructor with many parameters. This can lead to code that is hard to read, maintain and understand. Additionally, if the object can be created in different ways (e.g., with different configurations), the constructor can become overloaded with multiple parameters, leading to confusion and errors. -For example, consider a `Computer` class that can be built with various components like CPU, RAM, storage, graphics card, and peripherals. A constructor for such a class could easily become unwieldy and difficult to manage, as it has a lot of parameters you need to manage for each computer. Although they all might need storage, RAM or a graphics card, the specific types and configurations can vary widely per use-case. +For example, consider a `Computer` class that can be built with various components like CPU, RAM, storage, graphics card, and peripherals. A constructor for such a class could easily become unwieldy and difficult to manage, as it has a lot of parameters you need to manage for each computer. Although they all might need storage, RAM or a graphics card, the specific types and configurations can vary widely per use-case. ## 🛠️Solutions @@ -36,6 +36,7 @@ This design pattern is often used with something called a **Director**. The `Dir Think of building a house. You have an architect (the Director) who designs the house and a construction team (the Builder) that follows the architect's plans to build it. The architect specifies what kind of house to build, while the construction team handles the actual building process step by step, such as laying the foundation, erecting walls, installing windows, and so on. The end result is a complete house built according to the architect's specifications, but the construction team takes care of the details. ### 💡Real-world examples + Common practical scenarios for applying the Builder pattern include: - Creating a configuration object with numerous optional parameters. @@ -43,20 +44,30 @@ Common practical scenarios for applying the Builder pattern include: ## ⚖️Pros and Cons - - ## 🔍Applicability - + ## 🧭Implementation Plan @@ -77,77 +88,88 @@ To implement a Builder manually: ```ts // Product class - the complex object to be built class Computer { - constructor( - public cpu: string, - public ram: string, - public storage: string, - public graphicsCard?: string, - public peripherals?: string[] - ) {} - - displaySpecs() { - console.log(`CPU: ${this.cpu}, RAM: ${this.ram}, Storage: ${this.storage}, Graphics Card: ${this.graphicsCard || 'None'}, Peripherals: ${this.peripherals?.join(', ') || 'None'}`); - } - + constructor( + public cpu: string, + public ram: string, + public storage: string, + public graphicsCard?: string, + public peripherals?: string[] + ) {} + + displaySpecs() { + console.log( + `CPU: ${this.cpu}, RAM: ${this.ram}, Storage: ${ + this.storage + }, Graphics Card: ${this.graphicsCard || "None"}, Peripherals: ${ + this.peripherals?.join(", ") || "None" + }` + ); + } } // Builder interface - defines the steps to build the product interface ComputerBuilder { - setCPU(cpu: string): ComputerBuilder; - setRAM(ram: string): ComputerBuilder; - setStorage(storage: string): ComputerBuilder; - setGraphicsCard(graphicsCard: string): ComputerBuilder; - addPeripheral(peripheral: string): ComputerBuilder; - build(): Computer; + setCPU(cpu: string): ComputerBuilder; + setRAM(ram: string): ComputerBuilder; + setStorage(storage: string): ComputerBuilder; + setGraphicsCard(graphicsCard: string): ComputerBuilder; + addPeripheral(peripheral: string): ComputerBuilder; + build(): Computer; } // Concrete Builder - implements the builder interface class GamingComputerBuilder implements ComputerBuilder { - private cpu!: string; - private ram!: string; - private storage!: string; - private graphicsCard!: string; - private peripherals: string[] = []; - - setCPU(cpu: string): ComputerBuilder { - this.cpu = cpu; - return this; - } - - setRAM(ram: string): ComputerBuilder { - this.ram = ram; - return this; - } - - setStorage(storage: string): ComputerBuilder { - this.storage = storage; - return this; - } - - setGraphicsCard(graphicsCard: string): ComputerBuilder { - this.graphicsCard = graphicsCard; - return this; - } - - addPeripheral(peripheral: string): ComputerBuilder { - this.peripherals.push(peripheral); - return this; - } - - build(): Computer { - return new Computer(this.cpu, this.ram, this.storage, this.graphicsCard, this.peripherals); - } + private cpu!: string; + private ram!: string; + private storage!: string; + private graphicsCard!: string; + private peripherals: string[] = []; + + setCPU(cpu: string): ComputerBuilder { + this.cpu = cpu; + return this; + } + + setRAM(ram: string): ComputerBuilder { + this.ram = ram; + return this; + } + + setStorage(storage: string): ComputerBuilder { + this.storage = storage; + return this; + } + + setGraphicsCard(graphicsCard: string): ComputerBuilder { + this.graphicsCard = graphicsCard; + return this; + } + + addPeripheral(peripheral: string): ComputerBuilder { + this.peripherals.push(peripheral); + return this; + } + + build(): Computer { + return new Computer( + this.cpu, + this.ram, + this.storage, + this.graphicsCard, + this.peripherals + ); + } } // Client code - using the builder to create a complex object const gamingComputer = new GamingComputerBuilder() - .setCPU("Intel i9") - .setRAM("32GB") - .setStorage("1TB SSD") - .setGraphicsCard("NVIDIA RTX 3080") - .addPeripheral("Mechanical Keyboard") - .addPeripheral("Gaming Mouse") - .build(); + .setCPU("Intel i9") + .setRAM("32GB") + .setStorage("1TB SSD") + .setGraphicsCard("NVIDIA RTX 3080") + .addPeripheral("Mechanical Keyboard") + .addPeripheral("Gaming Mouse") + .build(); gamingComputer.displaySpecs(); // Output: CPU: Intel i9, RAM: 32GB, Storage: 1TB SSD, Graphics Card: NVIDIA RTX 3080, Peripherals: Mechanical Keyboard, Gaming Mouse @@ -254,165 +276,232 @@ This sample is to get a 'feel' for the pattern. The code itself may not reflect ```jsx live function BuilderDemo() { - // Simple Product class - class Computer { - constructor(cpu, ram, storage, graphicsCard, peripherals = []) { - this.cpu = cpu; - this.ram = ram; - this.storage = storage; - this.graphicsCard = graphicsCard; - this.peripherals = peripherals; - } - - specs() { - return `CPU: ${this.cpu}, RAM: ${this.ram}, Storage: ${this.storage}, Graphics: ${this.graphicsCard || 'None'}, Peripherals: ${this.peripherals.length ? this.peripherals.join(', ') : 'None'}`; - } + // Simple Product class + class Computer { + constructor(cpu, ram, storage, graphicsCard, peripherals = []) { + this.cpu = cpu; + this.ram = ram; + this.storage = storage; + this.graphicsCard = graphicsCard; + this.peripherals = peripherals; } - // Base fluent builder - class ComputerBuilder { - constructor() { - this.reset(); - } - - reset() { - this._cpu = 'Intel i5'; - this._ram = '8GB'; - this._storage = '256GB SSD'; - this._graphicsCard = ''; - this._peripherals = []; - return this; - } - - setCPU(cpu) { this._cpu = cpu; return this; } - setRAM(ram) { this._ram = ram; return this; } - setStorage(storage) { this._storage = storage; return this; } - setGraphicsCard(graphics) { this._graphicsCard = graphics; return this; } - addPeripheral(p) { this._peripherals.push(p); return this; } - - build() { - const product = new Computer(this._cpu, this._ram, this._storage, this._graphicsCard, [...this._peripherals]); - this.reset(); // optional: make builder reusable - return product; - } + specs() { + return `CPU: ${this.cpu}, RAM: ${this.ram}, Storage: ${ + this.storage + }, Graphics: ${this.graphicsCard || "None"}, Peripherals: ${ + this.peripherals.length ? this.peripherals.join(", ") : "None" + }`; } + } - // React UI to interact with the builder (manual-only) - const [cpu, setCpu] = React.useState('Intel i5'); - const [ram, setRam] = React.useState('8GB'); - const [storage, setStorage] = React.useState('256GB SSD'); - const [graphics, setGraphics] = React.useState(''); - const [peripheralInput, setPeripheralInput] = React.useState(''); - const [peripherals, setPeripherals] = React.useState([]); - const [built, setBuilt] = React.useState(null); - // log entries are objects {type, message} so we can render colored badges reliably - const [log, setLog] = React.useState([]); - - const typeColor = { - manual: '#1e88e5', - info: '#43a047', - warn: '#f57c00', - error: '#e53935' - }; - - function addLog(type, message) { - setLog(l => [...l, { type, message }]); - } - - function addPeripheral() { - if (!peripheralInput) return; - setPeripherals(p => [...p, peripheralInput]); - addLog('info', `Peripheral added: ${peripheralInput}`); - setPeripheralInput(''); - } - - function buildManual() { - const b = new ComputerBuilder(); - b.setCPU(cpu).setRAM(ram).setStorage(storage).setGraphicsCard(graphics || undefined); - peripherals.forEach(p => b.addPeripheral(p)); - const c = b.build(); - setBuilt(c); - addLog('manual', `Manual build → ${c.specs()}`); - } - - // Clear log and reset the manual form and last-built product - function clearAndReset() { - setLog([]); - setBuilt(null); - setPeripherals([]); - // reset fields to defaults - setCpu('Intel i5'); - setRam('8GB'); - setStorage('256GB SSD'); - setGraphics(''); - } - - return ( -
-

Builder Pattern — Playground

- -
-
-

Manual Builder

-
- - setCpu(e.target.value)} /> -
-
- - setRam(e.target.value)} /> -
-
- - setStorage(e.target.value)} /> -
-
- - setGraphics(e.target.value)} placeholder="optional" /> -
-
- setPeripheralInput(e.target.value)} /> - -
-
- Peripherals: {peripherals.length ? peripherals.join(', ') : 'None'} -
-
- - -
-
- -
-

Last Built

-
- {built ? built.specs() : No build yet} -
-
-

This playground demonstrates manual construction using a Builder. There are no presets or Director in this simplified demo — try changing fields and finalizing a manual build.

-
-
-
- -
-

Action Log

-
-
    - {log.length ? log.map((entry, i) => ( -
  1. - {entry.type} - {entry.message} -
  2. - )) :
  3. No actions yet
  4. } -
-
-
+ // Base fluent builder + class ComputerBuilder { + constructor() { + this.reset(); + } + + reset() { + this._cpu = "Intel i5"; + this._ram = "8GB"; + this._storage = "256GB SSD"; + this._graphicsCard = ""; + this._peripherals = []; + return this; + } + + setCPU(cpu) { + this._cpu = cpu; + return this; + } + setRAM(ram) { + this._ram = ram; + return this; + } + setStorage(storage) { + this._storage = storage; + return this; + } + setGraphicsCard(graphics) { + this._graphicsCard = graphics; + return this; + } + addPeripheral(p) { + this._peripherals.push(p); + return this; + } + + build() { + const product = new Computer( + this._cpu, + this._ram, + this._storage, + this._graphicsCard, + [...this._peripherals] + ); + this.reset(); // optional: make builder reusable + return product; + } + } + + // React UI to interact with the builder (manual-only) + const [cpu, setCpu] = React.useState("Intel i5"); + const [ram, setRam] = React.useState("8GB"); + const [storage, setStorage] = React.useState("256GB SSD"); + const [graphics, setGraphics] = React.useState(""); + const [peripheralInput, setPeripheralInput] = React.useState(""); + const [peripherals, setPeripherals] = React.useState([]); + const [built, setBuilt] = React.useState(null); + // log entries are objects {type, message} so we can render colored badges reliably + const [log, setLog] = React.useState([]); + + const typeColor = { + manual: "#1e88e5", + info: "#43a047", + warn: "#f57c00", + error: "#e53935", + }; + + function addLog(type, message) { + setLog((l) => [...l, { type, message }]); + } + + function addPeripheral() { + if (!peripheralInput) return; + setPeripherals((p) => [...p, peripheralInput]); + addLog("info", `Peripheral added: ${peripheralInput}`); + setPeripheralInput(""); + } + + function buildManual() { + const b = new ComputerBuilder(); + b.setCPU(cpu) + .setRAM(ram) + .setStorage(storage) + .setGraphicsCard(graphics || undefined); + peripherals.forEach((p) => b.addPeripheral(p)); + const c = b.build(); + setBuilt(c); + addLog("manual", `Manual build → ${c.specs()}`); + } + + // Clear log and reset the manual form and last-built product + function clearAndReset() { + setLog([]); + setBuilt(null); + setPeripherals([]); + // reset fields to defaults + setCpu("Intel i5"); + setRam("8GB"); + setStorage("256GB SSD"); + setGraphics(""); + } + + return ( +
+

Builder Pattern — Playground

+ +
+
+

Manual Builder

+
+ + setCpu(e.target.value)} /> +
+
+ + setRam(e.target.value)} /> +
+
+ + setStorage(e.target.value)} + /> +
+
+ + setGraphics(e.target.value)} + placeholder="optional" + /> +
+
+ setPeripheralInput(e.target.value)} + /> + +
+
+ Peripherals:{" "} + {peripherals.length ? peripherals.join(", ") : "None"} +
+
+ + +
- ); + +
+

Last Built

+
+ {built ? built.specs() : No build yet} +
+
+

+ This playground demonstrates manual construction using a Builder. + There are no presets or Director in this simplified demo — try + changing fields and finalizing a manual build. +

+
+
+
+ +
+

Action Log

+
+
    + {log.length ? ( + log.map((entry, i) => ( +
  1. + + {entry.type} + + {entry.message} +
  2. + )) + ) : ( +
  3. + No actions yet +
  4. + )} +
+
+
+
+ ); } ``` ## 🔗Relations to other patterns + - The Builder pattern is often used in conjunction with the [Factory Method](/docs/creational/factory-method) pattern, where the factory method creates a builder instance. - You can combine [Builder](/docs/creational/builder) with [Bridge](/docs/structural/bridge): the director class plays the role of the abstraction, while different builders act as implementations. - [Abstract Factories](/docs/creational/abstract-factory), [Builders](/docs/creational/builder), and [Prototype](/docs/creational/prototype) can be implemented as [Singleton](/docs/creational/singleton). @@ -420,8 +509,9 @@ function BuilderDemo() { --- ## 📚Sources + Information used in this page was collected from various reliable sources: - [Refactoring Guru - Builder](https://refactoring.guru/design-patterns/builder) - [Geeks for Geeks - Builder Design Pattern](https://www.geeksforgeeks.org/system-design/builder-design-pattern/) -- [Wikipedia - Builder Pattern](https://en.wikipedia.org/wiki/Builder_pattern) \ No newline at end of file +- [Wikipedia - Builder Pattern](https://en.wikipedia.org/wiki/Builder_pattern) diff --git a/docs/creational/factory-method.mdx b/docs/creational/factory-method.mdx index 715816b..67ffc39 100644 --- a/docs/creational/factory-method.mdx +++ b/docs/creational/factory-method.mdx @@ -2,17 +2,24 @@ title: Factory Method hide_title: true description: > - Give the constructor a break... + Give the constructor a break... --- -import { CreationalPatternTag, BehavioralPatternTag, StructuralPatternTag } from "@site/src/components/pattern-page-features/pattern-alias-and-tag"; +import { + CreationalPatternTag, + BehavioralPatternTag, + StructuralPatternTag, +} from "@site/src/components/pattern-page-features/pattern-alias-and-tag"; import TwoColumnProsCons from "@site/src/components/pattern-page-features/pros-and-cons"; -import TOCInline from '@theme/TOCInline'; +import TOCInline from "@theme/TOCInline"; import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; import Applicability from "@site/src/components/pattern-page-features/applicability"; - + --- @@ -31,6 +38,7 @@ The factory method pattern suggests that you create a separate method (the facto Imagine a toy factory that produces different types of toys. Instead of having a single assembly line that creates all toys, the factory has multiple specialized assembly lines for different toy types (e.g., action figures, dolls, puzzles). Each assembly line is responsible for creating its specific type of toy. When a customer places an order, the factory uses the appropriate assembly line to produce the requested toy type. This way, the factory can easily add new toy types in the future by simply introducing new assembly lines without disrupting the existing ones. ### 💡Real-world examples + Common practical scenarios for applying the Factory Method pattern include: - A logistics application that needs to create different types of transport vehicles (e.g., trucks, ships, planes) based on the cargo being shipped. Here all vehicles adhere to a common 'vehicle' (interface) type. @@ -38,26 +46,36 @@ Common practical scenarios for applying the Factory Method pattern include: ## ⚖️ Pros and Cons - - ## 🔍Applicability - - + ]} +/> ## 🧭Implementation Plan @@ -68,7 +86,6 @@ To implement a Factory Method manually: 3. Create the concrete creator (sub-) classes, e.g. `BoxShipping` and `EnvelopeShipping`, that override the factory method to instantiate and return specific types of objects that implement the common interface. 4. Client code should call the factory method to create objects, instead of directly instantiating them. This way the client code remains decoupled from the specific classes being created. - ### 💻Code samples @@ -100,7 +117,8 @@ class BoxShipping implements ShippingMethod { // Concrete Products - different shipping implementations class EnvelopeShipping implements ShippingMethod { calculateCost(weight: number): number { - return Math.max(5, weight * 1.2); kg + return Math.max(5, weight * 1.2); + kg; } package(item: string): string { @@ -135,7 +153,7 @@ abstract class ShippingService { // Template method - uses the factory method processOrder(item: string, weight: number): void { const shipping = this.createShippingMethod(); - + console.log(`📦 ${shipping.package(item)}`); console.log(`💰 Cost: $${shipping.calculateCost(weight).toFixed(2)}`); console.log(`🚚 ${shipping.deliver()}`); @@ -162,7 +180,11 @@ class PremiumShippingService extends ShippingService { } // Client code - works with any shipping service without knowing the details -function processCustomerOrder(service: ShippingService, item: string, weight: number): void { +function processCustomerOrder( + service: ShippingService, + item: string, + weight: number +): void { service.processOrder(item, weight); } @@ -173,7 +195,6 @@ const weight = 0.5; // 0.5 kg processCustomerOrder(new StandardShippingService(), toyOrder, weight); processCustomerOrder(new ExpressShippingService(), toyOrder, weight); processCustomerOrder(new PremiumShippingService(), toyOrder, weight); - ``` @@ -187,11 +208,11 @@ class ShippingMethod(ABC): @abstractmethod def calculate_cost(self, weight: float) -> float: pass - + @abstractmethod def package(self, item: str) -> str: pass - + @abstractmethod def deliver(self) -> str: pass @@ -200,30 +221,30 @@ class ShippingMethod(ABC): class BoxShipping(ShippingMethod): def calculate_cost(self, weight: float) -> float: return weight * 2.5 # $2.50 per kg for box shipping - + def package(self, item: str) -> str: return f"{item} securely packed in a protective box with bubble wrap" - + def deliver(self) -> str: return "Delivered via ground transport (3-5 business days)" class EnvelopeShipping(ShippingMethod): def calculate_cost(self, weight: float) -> float: return max(5, weight * 1.2) # Minimum $5 or $1.20 per kg - + def package(self, item: str) -> str: return f"{item} placed in a padded envelope" - + def deliver(self) -> str: return "Delivered via express mail (1-2 business days)" class InsuredShipping(ShippingMethod): def calculate_cost(self, weight: float) -> float: return weight * 3.5 + 15 # Higher cost + insurance fee - + def package(self, item: str) -> str: return f"{item} packed in reinforced box with tracking and insurance" - + def deliver(self) -> str: return "Delivered with signature required and full insurance coverage" @@ -233,11 +254,11 @@ class ShippingService(ABC): @abstractmethod def create_shipping_method(self) -> ShippingMethod: pass - + # Template method - uses the factory method def process_order(self, item: str, weight: float) -> None: shipping = self.create_shipping_method() - + print(f"📦 {shipping.package(item)}") print(f"💰 Cost: ${shipping.calculate_cost(weight):.2f}") print(f"🚚 {shipping.deliver()}") @@ -263,7 +284,7 @@ def process_customer_order(service: ShippingService, item: str, weight: float) - if __name__ == "__main__": toy_order = "Limited Edition Action Figure" weight = 0.5 # 0.5 kg - + process_customer_order(StandardShippingService(), toy_order, weight) process_customer_order(ExpressShippingService(), toy_order, weight) process_customer_order(PremiumShippingService(), toy_order, weight) @@ -292,30 +313,34 @@ function FactoryMethodDemo() { type: "Standard Box", calculateCost: (w) => w * 2.5, deliveryTime: "3-5 business days", - icon: "📦" + icon: "📦", }); const createExpressShipping = () => ({ type: "Express Envelope", calculateCost: (w) => Math.max(5, w * 1.2), deliveryTime: "1-2 business days", - icon: "✉️" + icon: "✉️", }); const createPremiumShipping = () => ({ type: "Premium Insured", calculateCost: (w) => w * 3.5 + 15, deliveryTime: "Next day + signature", - icon: "🎁" + icon: "🎁", }); // Factory Method - returns the appropriate shipping method const getShippingMethod = (type) => { - switch(type) { - case "standard": return createStandardShipping(); - case "express": return createExpressShipping(); - case "premium": return createPremiumShipping(); - default: return createStandardShipping(); + switch (type) { + case "standard": + return createStandardShipping(); + case "express": + return createExpressShipping(); + case "premium": + return createPremiumShipping(); + default: + return createStandardShipping(); } }; @@ -323,7 +348,7 @@ function FactoryMethodDemo() { const handleProcessOrder = () => { const shipping = getShippingMethod(shippingType); const cost = shipping.calculateCost(weight); - + const order = { id: Date.now(), item, @@ -331,9 +356,9 @@ function FactoryMethodDemo() { shipping: shipping.type, cost: cost.toFixed(2), delivery: shipping.deliveryTime, - icon: shipping.icon + icon: shipping.icon, }; - + setOrders([order, ...orders]); }; @@ -342,9 +367,16 @@ function FactoryMethodDemo() { return (

Factory Method — Shipping Demo

- + {/* Order Form */} -
+
- +
- +
- + - +
{/* Orders Display */} @@ -409,15 +439,17 @@ function FactoryMethodDemo() { padding: 12, border: "1px solid #ddd", borderRadius: 4, - backgroundColor: "#f9f9f9" + backgroundColor: "#f9f9f9", }} >
{order.icon} {order.item} ({order.weight} kg)
- Method: {order.shipping}
- Cost: ${order.cost}
+ Method: {order.shipping} +
+ Cost: ${order.cost} +
Delivery: {order.delivery}
@@ -425,11 +457,20 @@ function FactoryMethodDemo() { )}
- -
- 💡 Factory Method in Action: Each shipping type is created by a - different factory function. The getShippingMethod() decides which factory - to use, keeping the order processing code decoupled from specific shipping implementations. + +
+ 💡 Factory Method in Action: Each shipping type is + created by a different factory function. The{" "} + getShippingMethod() decides which factory to use, keeping + the order processing code decoupled from specific shipping + implementations.
); @@ -437,6 +478,7 @@ function FactoryMethodDemo() { ``` ## 🔗Relations to other patterns + - Lots of people start with the [Factory Method](/docs/creational/factory-method) pattern prior to implementing something such as [Abstract Factory](/docs/creational/abstract-factory), [Builder](/docs/creational/builder) or [Prototype](/docs/creational/prototype) patterns. The latter patterns are more flexible, but also come with increased complexity. - [Prototype](/docs/creational/prototype) isn't based on inheritance, so it does not have its drawbacks. On the other hand, [Prototype](/docs/creational/prototype) requires a complicated initialization of the cloned object. [Factory Method](/docs/creational/factory-method) is based on inheritance but doesn't require an initialization step. - [Factory Method](/docs/creational/factory-method) is a specialization of [Template Method](/docs/behavioral/template-method). At the same time, a Factory Method may serve as a step in a large Template Method. @@ -444,6 +486,7 @@ function FactoryMethodDemo() { --- ## 📚Sources + Information used in this page was collected from various reliable sources: - [Refactoring Guru - Factory Method](https://refactoring.guru/design-patterns/factory-method) diff --git a/docs/creational/prototype.mdx b/docs/creational/prototype.mdx index 4cf64eb..bb5f639 100644 --- a/docs/creational/prototype.mdx +++ b/docs/creational/prototype.mdx @@ -1,19 +1,19 @@ ---- +s--- title: Prototype hide_title: true description: > - Double the copies, double the fun. +Double the copies, double the fun. + --- import { CreationalPatternTag } from "@site/src/components/pattern-page-features/pattern-alias-and-tag"; import TwoColumnProsCons from "@site/src/components/pattern-page-features/pros-and-cons"; -import TOCInline from '@theme/TOCInline'; +import TOCInline from "@theme/TOCInline"; import Tabs from "@theme/Tabs"; import TabItem from "@theme/TabItem"; import Applicability from "@site/src/components/pattern-page-features/applicability"; - ---- +## **Prototype** is a pattern that lets you copy existing objects without making your code dependant on their actual implementations or classes. @@ -23,14 +23,14 @@ Let's say you want to create a copy or clone of an existing object. However, the ## 🛠️Solutions -The Prototype pattern addresses these challenges by allowing you to create new objects by cloning existing ones, known as **prototypes**. Instead of initializing objects directly, you copy an existing object, which serves as a prototype. This is typically achieved through a method like `clone()`, which creates a copy of the object. An object which supports cloning or copying is referred to as a *prototype*. When you have lots of configurations or dozens of fields, prototyping may serve as a good alternative to subclassing. - +The Prototype pattern addresses these challenges by allowing you to create new objects by cloning existing ones, known as **prototypes**. Instead of initializing objects directly, you copy an existing object, which serves as a prototype. This is typically achieved through a method like `clone()`, which creates a copy of the object. An object which supports cloning or copying is referred to as a _prototype_. When you have lots of configurations or dozens of fields, prototyping may serve as a good alternative to subclassing. ### 🏛️Metaphors Imagine a sculptor who has created a beautiful statue. Instead of sculpting a new statue from scratch each time, the sculptor makes a mold of the original statue. Whenever a new statue is needed, the sculptor simply pours material into the mold to create an exact replica. In this scenario, the original statue is the prototype, and the mold allows for easy duplication without needing to recreate the entire sculpture process. ### 💡Real-world examples + Common practical scenarios for applying the Prototype pattern include: - Cloning complex objects in a game (e.g., characters, items) without knowing their exact class. @@ -39,25 +39,33 @@ Common practical scenarios for applying the Prototype pattern include: ## ⚖️ Pros and Cons - - ## 🔍Applicability - - + ## 🧭Implementation Plan @@ -69,7 +77,6 @@ To implement a Prototype manually: 4. [OPTIONAL] Create a Prototype Registry, a centralized place to store and manage prototype instances, or use a Factory that utilizes prototypes for object creation. 5. Replace the direct call to the constructor with calls to the clone method. - ### 💻Code samples @@ -100,7 +107,6 @@ class Person implements Prototype { const original = new Person("Alice", 30); const copy = original.clone(); console.log(copy); // Person { name: 'Alice', age: 30 } - ``` @@ -141,7 +147,7 @@ function PrototypeDemo() { const animalPrototype = { speak() { return `Hi, I'm a ${this.type} called ${this.name}`; - } + }, }; // Function to clone the prototype and create a new animal @@ -170,7 +176,7 @@ function PrototypeDemo() { Type:{" "} setType(e.target.value)} + onChange={(e) => setType(e.target.value)} style={{ marginRight: 8 }} /> @@ -178,7 +184,7 @@ function PrototypeDemo() { Name:{" "} setName(e.target.value)} + onChange={(e) => setName(e.target.value)} style={{ marginRight: 8 }} /> @@ -189,17 +195,18 @@ function PrototypeDemo() {
  • {animal.speak()}
  • ))} -
    - Each new animal is cloned from animalPrototype. Try changing the - fields and creating multiple animals to see prototype sharing in action! +
    + Each new animal is cloned from animalPrototype. Try + changing the fields and creating multiple animals to see prototype + sharing in action!
    ); } - ``` ## 🔗Relations to other patterns + - Designs that make heavy use of [Composite](/docs/structural/composite.mdx) and [Decorator](/docs/structural/decorator.mdx) can often benefit from using [Prototype](/docs/creational/prototype). Applying the pattern lets you clone complex structures instead of re-constructing them from scratch. - [Prototypes](/docs/creational/prototype) can help when you need to save copies of [Commands](/docs/behavioral/command.mdx) into history. - [Abstract Factories](/docs/creational/abstract-factory) classes are often based on a set of [Factory Methods](/docs/creational/factory-method.mdx), but you can also use [Prototype](/docs/creational/prototype) to compose the methods on these classes. @@ -208,8 +215,9 @@ function PrototypeDemo() { --- ## 📚Sources + Information used in this page was collected from various reliable sources: - [Refactoring Guru - Prototype](https://refactoring.guru/design-patterns/prototype) - [Geeks for Geeks - Prototype Design Pattern](https://www.geeksforgeeks.org/system-design/prototype-design-pattern/) -- [Wikipedia - Prototype Pattern](https://en.wikipedia.org/wiki/Prototype_pattern) \ No newline at end of file +- [Wikipedia - Prototype Pattern](https://en.wikipedia.org/wiki/Prototype_pattern) diff --git a/docs/creational/singleton.mdx b/docs/creational/singleton.mdx index 39232ee..0b33b03 100644 --- a/docs/creational/singleton.mdx +++ b/docs/creational/singleton.mdx @@ -9,7 +9,7 @@ import { CreationalPatternTag } from "@site/src/components/pattern-page-features import TwoColumnProsCons from "@site/src/components/pattern-page-features/pros-and-cons"; import Applicability from "@site/src/components/pattern-page-features/applicability"; - + --- @@ -43,6 +43,7 @@ one controlling authority, the government. In this example, the government is considered a Singleton. ### 💡Real-world examples + Common practical scenarios for applying the Singleton pattern include: - Application configuration managers, ensuring consistent settings across the app. @@ -52,36 +53,47 @@ Common practical scenarios for applying the Singleton pattern include: ## ⚖️ Pros and Cons -