diff --git a/README.md b/README.md index e865ae0..3b6045d 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,8 @@ Each page follows a basic structure to ensure that a user can easily find metada 1. Real world examples of application of the pattern. 8. Pros and cons 1. Pros and cons of the pattern. -9. Your turn +9. Applicability + 1. Explain when, and HOW the pattern is applicable to a situation. 10. How can the user see a potential implementation plan. 11. Code samples 12. Snippets in various programming languages. @@ -53,6 +54,7 @@ Some extras, common elements may include; 8. Code compiles and tests pass. 9. Directories follow the correct pattern structure. 10. Each page is built using the [page structure](#page-structure). +11. Custom components should not use a title, they should use the correct heading level in the page itself to ensure proper url based linking. ## Installation diff --git a/blog/2019-05-28-first-blog-post.md b/blog/2019-05-28-first-blog-post.md deleted file mode 100644 index d3032ef..0000000 --- a/blog/2019-05-28-first-blog-post.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -slug: first-blog-post -title: First Blog Post -authors: [slorber, yangshun] -tags: [hola, docusaurus] ---- - -Lorem ipsum dolor sit amet... - - - -...consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet diff --git a/blog/2019-05-29-long-blog-post.md b/blog/2019-05-29-long-blog-post.md deleted file mode 100644 index eb4435d..0000000 --- a/blog/2019-05-29-long-blog-post.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -slug: long-blog-post -title: Long Blog Post -authors: yangshun -tags: [hello, docusaurus] ---- - -This is the summary of a very long blog post, - -Use a `` comment to limit blog post size in the list view. - - - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet diff --git a/blog/2021-08-01-mdx-blog-post.mdx b/blog/2021-08-01-mdx-blog-post.mdx deleted file mode 100644 index 0c4b4a4..0000000 --- a/blog/2021-08-01-mdx-blog-post.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -slug: mdx-blog-post -title: MDX Blog Post -authors: [slorber] -tags: [docusaurus] ---- - -Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/). - -:::tip - -Use the power of React to create interactive blog posts. - -::: - -{/* truncate */} - -For example, use JSX to create an interactive button: - -```js - -``` - - diff --git a/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg b/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg deleted file mode 100644 index 11bda09..0000000 Binary files a/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg and /dev/null differ diff --git a/blog/2021-08-26-welcome/index.md b/blog/2021-08-26-welcome/index.md deleted file mode 100644 index 349ea07..0000000 --- a/blog/2021-08-26-welcome/index.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -slug: welcome -title: Welcome -authors: [slorber, yangshun] -tags: [facebook, hello, docusaurus] ---- - -[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). - -Here are a few tips you might find useful. - - - -Simply add Markdown files (or folders) to the `blog` directory. - -Regular blog authors can be added to `authors.yml`. - -The blog post date can be extracted from filenames, such as: - -- `2019-05-30-welcome.md` -- `2019-05-30-welcome/index.md` - -A blog post folder can be convenient to co-locate blog post images: - -![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) - -The blog supports tags as well! - -**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. diff --git a/blog/authors.yml b/blog/authors.yml deleted file mode 100644 index 0fd3987..0000000 --- a/blog/authors.yml +++ /dev/null @@ -1,25 +0,0 @@ -yangshun: - name: Yangshun Tay - title: Ex-Meta Staff Engineer, Co-founder GreatFrontEnd - url: https://linkedin.com/in/yangshun - image_url: https://github.com/yangshun.png - page: true - socials: - x: yangshunz - linkedin: yangshun - github: yangshun - newsletter: https://www.greatfrontend.com - -slorber: - name: Sébastien Lorber - title: Docusaurus maintainer - url: https://sebastienlorber.com - image_url: https://github.com/slorber.png - page: - # customize the url of the author page at /blog/authors/ - permalink: '/all-sebastien-lorber-articles' - socials: - x: sebastienlorber - linkedin: sebastienlorber - github: slorber - newsletter: https://thisweekinreact.com diff --git a/blog/tags.yml b/blog/tags.yml deleted file mode 100644 index bfaa778..0000000 --- a/blog/tags.yml +++ /dev/null @@ -1,19 +0,0 @@ -facebook: - label: Facebook - permalink: /facebook - description: Facebook tag description - -hello: - label: Hello - permalink: /hello - description: Hello tag description - -docusaurus: - label: Docusaurus - permalink: /docusaurus - description: Docusaurus tag description - -hola: - label: Hola - permalink: /hola - description: Hola tag description diff --git a/docs/Creational/builder.mdx b/docs/Creational/builder.mdx index 53ab04e..9e1e39b 100644 --- a/docs/Creational/builder.mdx +++ b/docs/Creational/builder.mdx @@ -1,5 +1,424 @@ --- title: Builder +hide_title: true --- -This is Builder Pattern page +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 Tabs from "@theme/Tabs"; +import TabItem from "@theme/TabItem"; +import Applicability from "@site/src/components/pattern-page-features/applicability"; + + + +--- + +The **Builder** pattern helps to break large, complex constructors and constructive logic into smaller sections. This allows you to build complex objects step by step, whilst using the same construction code. This is particularly useful when an object can be created in multiple ways or requires numerous configurations. + +## 🧩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. + +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 + +The builder pattern addresses this problem by separating the construction of a complex object from its representation. It involves creating a `Builder` class that provides methods to configure and create the different parts of the object step by step. The builder class has methods for setting each part of the object, and a final method to construct and return the complete object. The important part is that you are not longer required to call each step individually nor pass each parameter on its own. You only call those steps which are relevant and necessary for your new configuration. + +### 🎬 Director + +This design pattern is often used with something called a **Director**. The `Director` class is responsible for managing the construction process. It defines the order in which to call the builder's methods, ensuring that the final product is built correctly. The client code interacts with the director instead of the builder directly, allowing for a higher level of abstraction and encapsulation. + +### 🏛️Metaphors + +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. +- Constructing a complex UI component step by step. + +## ⚖️Pros and Cons + + + + +## 🔍Applicability + + + +## 🧭Implementation Plan + +To implement a Builder manually: + +1. Gather a clear list of instructions, configurations and steps required to build the complex object. +2. Declare these steps in a builder interface. +3. Create a concrete builder for each configuration or representation of the object. +4. Optionally, create a Director class to manage the construction process. +5. Use the builder (and director if applicable) in your client code to create the complex object step by step. +6. Fetch the final object from the builder (or director if used and the interfaces match) once all necessary steps are completed. + +### 💻Code samples + + + + +```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'}`); + } + +} + +// 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; +} + +// 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); + } +} + +// 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(); + +gamingComputer.displaySpecs(); +// Output: CPU: Intel i9, RAM: 32GB, Storage: 1TB SSD, Graphics Card: NVIDIA RTX 3080, Peripherals: Mechanical Keyboard, Gaming Mouse +``` + + + + +```python +# Product class - the complex object to be built +class Computer: + def __init__(self, cpu, ram, storage, graphics_card=None, peripherals=None): + self.cpu = cpu + self.ram = ram + self.storage = storage + self.graphics_card = graphics_card + self.peripherals = peripherals if peripherals else [] + + def display_specs(self): + print(f"CPU: {self.cpu}, RAM: {self.ram}, Storage: {self.storage}, Graphics Card: {self.graphics_card or 'None'}, Peripherals: {', '.join(self.peripherals) if self.peripherals else 'None'}") + +# Builder interface - defines the steps to build the product +from abc import ABC, abstractmethod +class ComputerBuilder(ABC): + @abstractmethod + def set_cpu(self, cpu): + pass + + @abstractmethod + def set_ram(self, ram): + pass + + @abstractmethod + def set_storage(self, storage): + pass + + @abstractmethod + def set_graphics_card(self, graphics_card): + pass + + @abstractmethod + def add_peripheral(self, peripheral): + pass + + @abstractmethod + def build(self): + pass + +# Concrete Builder - implements the builder interface +class GamingComputerBuilder(ComputerBuilder): + def __init__(self): + self.cpu = None + self.ram = None + self.storage = None + self.graphics_card = None + self.peripherals = [] + + def set_cpu(self, cpu): + self.cpu = cpu + return self + + def set_ram(self, ram): + self.ram = ram + return self + + def set_storage(self, storage): + self.storage = storage + return self + + def set_graphics_card(self, graphics_card): + self.graphics_card = graphics_card + return self + + def add_peripheral(self, peripheral): + self.peripherals.append(peripheral) + return self + + def build(self): + return Computer(self.cpu, self.ram, self.storage, self.graphics_card, self.peripherals) + +# Client code - using the builder to create a complex object +gaming_computer = (GamingComputerBuilder() + .set_cpu("Intel i9") + .set_ram("32GB") + .set_storage("1TB SSD") + .set_graphics_card("NVIDIA RTX 3080") + .add_peripheral("Mechanical Keyboard") + .add_peripheral("Gaming Mouse") + .build()) + +gaming_computer.display_specs() +# Output: CPU: Intel i9, RAM: 32GB, Storage: 1TB SSD, Graphics Card: NVIDIA RTX 3080, Peripherals: Mechanical Keyboard, Gaming Mouse +``` + + + +### 🎮Playground + +:::note + +This sample is to get a 'feel' for the pattern. The code itself may not reflect a correct implementation of the pattern. + +::: + +```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'}`; + } + } + + // 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). + +--- + +## 📚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 diff --git a/docs/Creational/singleton.mdx b/docs/Creational/singleton.mdx index be8b2d4..ac3b758 100644 --- a/docs/Creational/singleton.mdx +++ b/docs/Creational/singleton.mdx @@ -5,7 +5,7 @@ hide_title: true 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 Applicability from "@site/src/components/pattern-page-features/applicability"; @@ -14,6 +14,7 @@ import TOCInline from '@theme/TOCInline'; The **Singleton** pattern ensures that a class has only one instance throughout the lifetime of an application and provides a single, well-known global access point to that instance. It’s one of the simplest creational design patterns, often used to control access to shared resources or global states, such as configuration objects, loggers, or connection pools. ## 🧩The problem + Some components in a system are meant to exist only once. For example, imagine a logging service that writes messages to a single log file. If each part of an application created its own logger instance, messages could become disorganized, @@ -31,13 +32,6 @@ The Singleton pattern introduces a way to manage this shared instance explicitly In essence, Singleton turns a regular class into both a **factory** and a **unique data holder**. - -