From 266b685d8c3543135444d94cda0d29ad3811a1bf Mon Sep 17 00:00:00 2001 From: shashank Date: Thu, 16 Jul 2026 19:31:40 +0530 Subject: [PATCH] readme --- README.md | 610 ++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 567 insertions(+), 43 deletions(-) diff --git a/README.md b/README.md index 603393e..1f7ba40 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,569 @@ # JavaScript Design Patterns -## Creational - -- Abstract Factory -- Builder -- Factory Method -- Object Pool -- Prototype -- [Singleton](http://robdodson.me/blog/2012/08/08/javascript-design-patterns-singleton/) - -## Structural - -- Adapter -- Bridge -- Composite -- [Decorator](http://robdodson.me/blog/2012/08/27/javascript-design-patterns-decorator/) -- Facade -- Flyweight -- Private Class Data -- Proxy - -## Behavioral - -- Chain of Responsibility -- Command -- Interpreter -- [Iterator](http://robdodson.me/blog/2012/08/10/javascript-design-patterns-iterator/) -- Mediator -- Memento -- Null Object -- [Observer](http://robdodson.me/blog/2012/08/16/javascript-design-patterns-observer/) -- State -- [Strategy](http://robdodson.me/blog/2012/08/03/javascript-design-patterns-strategy/) -- Template Method -- Visitor -- Monad Pattern / Promises - -It occurred to me that I’ve always wanted to go through the Gang of Four book and just write my own interpretation of each pattern. Since I’m currently working primarily in JavaScript I thought it might be an interesting challenge to convert their examples, often in strongly typed languages, to something as dynamic and loosey-goosey as JS. - -I know there are a lot of people out there who aren’t too keen on design patterns but that’s not to say that they shouldn’t be used or studied. There’s a lot of code out there that starts with jQuery.click() or addEventListener or .on() and all of them are implementations of the Observer pattern. Finding this reusable approach is the main point of patterns and along with it comes a shared vocabulary that can be passed on to other developers. Rather than saying “Let’s defer the methods of our object that are subject to change to well encapsulated algorithms.” We can just say “A Strategy pattern might be nice here.” - -Patterns should be used with caution as not everything fits so neatly into their paradigms. It’s often said that a beginner never met a pattern he didn’t like. In my experiences I’ve been burned by pattern overuse and at other times they have legitimately saved my ass. It’s also true that many patterns don’t really work or aren’t appropriate for particular languages. For instance, the GoF book was written primarily for languages which shared features of C++ and SmallTalk. I totally agree with this sentiment but I feel like along the way we’ll discover what does and doesn’t make sense in a dynamic language like JS and hopefully we can toss in some new patterns of our own. Already to the list I’ve added Promises which I use quite frequently and find to be a wonderful alternative to JavaScript’s oft seen pyramid of callbacks. Again, this is all about learning and experimenting. In my opinion a good understanding of design patterns is a threshold that needs to be crossed at some point in your career. I’m committed to doing this twice a week for the next several weeks so hopefully by the end of it we’ll have a useful resource that others can benefit from. Stay tuned! - -[http://robdodson.me/blog/2012/08/03/javascript-design-patterns/](http://robdodson.me/blog/2012/08/03/javascript-design-patterns/) \ No newline at end of file +This repository contains small, browser-based examples of common software design patterns implemented in JavaScript. + +The examples use older ES5-style JavaScript—constructor functions, prototypes, global variables, and `var`. The syntax may be old, but the design ideas are still useful in modern JavaScript applications. + +## How to use this repository + +No installation or build step is required. + +1. Choose an example directory. +2. Open its `index.html` file in a browser. +3. Open the browser developer tools and select the **Console** tab. +4. Read `main.js` and predict its output. +5. Refresh the page to check your prediction. +6. Modify the example and observe how its behavior changes. + +For the Strategy painter example, interact with the canvas instead of looking for console output. + +## Recommended learning order + +1. Simple Factory +2. Strategy +3. Observer +4. Iterator +5. Decorator +6. Factory Method +7. Abstract Factory +8. Singleton/Namespace + +## Pattern categories + +Design patterns are commonly grouped into three categories: + +- **Creational patterns** control how objects are created. +- **Structural patterns** organize relationships between objects. +- **Behavioral patterns** organize communication and responsibilities. + +This repository implements examples from all three categories. + +--- + +## 1. Simple Factory + +**Category:** Creational +**Location:** `factory/simple-factory` + +### Purpose + +A Simple Factory puts object-creation decisions in one place. The calling code asks for a type of object without directly invoking its constructor. + +### Repository example + +The factory creates either an `Admin` or a `Customer`: + +```js +function Admin() { + console.log("Admin created!"); +} + +function Customer() { + console.log("Customer created!"); +} + +var UserFactory = {}; + +UserFactory.createUser = function (type) { + if (type === "admin") { + return new Admin(); + } else if (type === "customer") { + return new Customer(); + } +}; + +var user = UserFactory.createUser("admin"); +``` + +Console output: + +```text +Admin created! +Customer created! +``` + +### When it is useful + +Use a factory when object construction depends on input, configuration, environment, or runtime state. Examples include creating users, parsers, database adapters, or UI controls. + +### Exercise + +Add a `Guest` constructor and support `UserFactory.createUser("guest")`. Also throw an error when the requested type is unknown. + +--- + +## 2. Factory Method + +**Category:** Creational +**Location:** `factory/factory-method` + +### Purpose + +The Factory Method pattern defines a creation operation that specialized objects can override. Client code uses the same method while different implementations decide which concrete product to return. + +### Repository example + +Every pizza store has a `createPizza` method, but each regional store creates a different style: + +```js +var PizzaStore = { + createPizza: function (type) { + // Create a generic pizza. + } +}; + +var ChicagoPizzaStore = fromPrototype(PizzaStore, { + createPizza: function (type) { + if (type === "veggie") { + return fromPrototype(Pizza, { + description: "Veggie, Deep-dish Chicago Pizza" + }); + } + } +}); +``` + +Console output: + +```text +Veggie, Deep-dish Chicago Pizza +``` + +### Simple Factory vs Factory Method + +- A **Simple Factory** usually has one function containing all creation decisions. +- A **Factory Method** lets specialized implementations override the creation operation. + +### When it is useful + +Use it when a base workflow should remain consistent but subclasses or specialized objects need to choose the object being created. + +### Exercise + +Create a `NewYorkPizzaStore` that supports cheese and veggie pizzas with thin crust. + +--- + +## 3. Abstract Factory + +**Category:** Creational +**Location:** `factory/abstract-factory` + +### Purpose + +An Abstract Factory creates a related family of objects without exposing their concrete implementations. + +### Repository example + +The example creates compatible groups of pizza ingredients: + +```js +var Ingredients = { + createDough: function () { + return "generic dough"; + }, + createSauce: function () { + return "generic sauce"; + }, + createCrust: function () { + return "generic crust"; + } +}; + +Ingredients.createCaliforniaStyle = function () { + return fromPrototype(Ingredients, { + createDough: function () { + return "light, fluffy dough"; + }, + createSauce: function () { + return "tangy red sauce"; + }, + createCrust: function () { + return "thin and crispy"; + } + }); +}; +``` + +Console output: + +```text +thin and crispy +``` + +### Factory Method vs Abstract Factory + +- A **Factory Method** generally creates one product through an overridable method. +- An **Abstract Factory** creates a family of related products that should work together. + +### When it is useful + +Use it for themes, operating-system-specific controls, database provider families, or related application services that must remain compatible. + +### Exercise + +Add a New York ingredient family with thin dough, tomato sauce, and a crispy crust. + +--- + +## 4. Singleton and Namespace + +**Category:** Creational +**Location:** `singleton/namespace` + +### Purpose + +A Singleton normally ensures that only one instance of something exists and provides a shared access point to it. + +The repository example mainly demonstrates the related **namespace pattern**. It creates one global container to avoid placing many separate names in the global scope: + +```js +var NAMESPACE = {}; + +NAMESPACE.Widget = function (foo, bar) { + this.foo = foo; + this.bar = bar; +}; + +NAMESPACE.Widget.prototype.doSomethingAwesome = function () { + console.log("Something awesome!"); +}; + +var myWidget = new NAMESPACE.Widget("hello", "world"); +``` + +The original example produces no console output because its method body is only a placeholder. + +### A stricter singleton example + +```js +var settings = (function () { + var instance; + + function createSettings() { + return { theme: "dark" }; + } + + return { + getInstance: function () { + if (!instance) { + instance = createSettings(); + } + return instance; + } + }; +}()); + +var first = settings.getInstance(); +var second = settings.getInstance(); + +console.log(first === second); // true +``` + +### When it is useful + +Singleton-like objects may be useful for shared configuration, logging, or application-wide services. Use them carefully because hidden global state makes testing and dependency management harder. + +### Exercise + +Implement `NAMESPACE.Settings.getInstance()` and prove that repeated calls return the same object. + +--- + +## 5. Decorator + +**Category:** Structural +**Locations:** `decorator/sale`, `decorator/sale-closures`, and `decorator/validator` + +### Purpose + +A Decorator adds behavior to an object without permanently changing the original object or creating a subclass for every possible combination. + +### Sale example + +The sale begins at `$50`. One decorator adds federal tax and another formats the result as Canadian currency: + +```js +var sale = new Sale(50); +sale = sale.decorate("fedtax"); +sale = sale.decorate("cdn"); + +console.log(sale.getPrice()); +``` + +Console output: + +```text +CDN$52.50 +``` + +Decorators can be stacked, so the output of one decorator becomes the input to the next. + +### Closure-based example + +The closure variation wraps a function: + +```js +function usd(fn, context) { + var price = fn.call(context); + return "$" + price; +} + +sale.getPrice = decorate(usd, sale.getPrice, sale); +``` + +Wrapping the function three times produces: + +```text +$$$50 +$$$100 +``` + +### Validator example + +Validation rules are selected dynamically: + +```js +var validator = new Validator(); +validator.decorate("hasName", { length: 5 }); +validator.decorate("hasAge", { minimum: 21 }); +validator.decorate("hasZipCode"); +validator.validate({}); +``` + +Console output: + +```text +["no name!", "no age!", "no zip!"] +``` + +### When it is useful + +Use decorators for optional formatting, logging, caching, authorization, validation, middleware, or pricing rules. + +### Exercise + +Add a `discount` decorator that reduces the price by 10%, then test whether applying tax before the discount gives the same result as applying the discount before tax. + +--- + +## 6. Iterator + +**Category:** Behavioral +**Locations:** `iterator/arrays` and `iterator/objects` + +### Purpose + +An Iterator provides a consistent interface for traversing a collection without exposing how that collection is stored. + +The repository iterators expose four operations: + +- `next()` returns the next value. +- `hasNext()` checks whether another value exists. +- `current()` returns the current value. +- `rewind()` moves back to the beginning. + +### Array example + +The array iterator advances three positions at a time: + +```js +while (iterator.hasNext()) { + console.log(iterator.next()); +} +``` + +If this loop is added to the repository example, it prints: + +```text +1 +4 +7 +10 +``` + +### Object example + +The object iterator uses `Object.keys()` to traverse object values: + +```js +while (iterator.hasNext()) { + console.log(iterator.next()); +} +``` + +It prints: + +```text +foo +bar +baz +``` + +The original iterator files do not print anything because they define the iterators without calling their methods. + +### Modern JavaScript connection + +JavaScript now has built-in iteration tools such as `for...of`, generators, and `Symbol.iterator`. The repository demonstrates the underlying idea manually. + +### Exercise + +Change the array iterator so that its step size is provided as an argument rather than always being `3`. + +--- + +## 7. Observer + +**Category:** Behavioral +**Location:** `observer/publisher` + +### Purpose + +The Observer pattern allows one object—the publisher—to notify multiple interested objects—the subscribers—when an event occurs. + +The publisher exposes these operations: + +- `on(type, fn, context)` subscribes a function. +- `fire(type, publication)` publishes an event. +- `remove(type, fn, context)` unsubscribes a function. + +### Example usage + +The repository defines the publisher but does not register or fire any events. Add this code to try it: + +```js +function showMessage(message) { + console.log("Received: " + message); +} + +publisher.on("news", showMessage); +publisher.fire("news", "JavaScript patterns are useful"); +publisher.remove("news", showMessage); +publisher.fire("news", "This will not be printed"); +``` + +Expected output: + +```text +Received: JavaScript patterns are useful +``` + +### Where you already see it + +Browser events use the same general idea: + +```js +button.addEventListener("click", handleClick); +``` + +The button is the publisher, `handleClick` is the subscriber, and `click` is the event type. + +### When it is useful + +Use it for UI events, notifications, application event buses, state subscriptions, and communication between loosely coupled components. + +### Exercise + +Subscribe two functions to the same event, publish once, remove one subscriber, and publish again. + +--- + +## 8. Strategy + +**Category:** Behavioral +**Location:** `strategy/painter` + +### Purpose + +The Strategy pattern stores interchangeable algorithms behind a common interface. The application selects the behavior it needs at runtime. + +### Repository example + +The painter contains three brush strategies. Each has the same `draw` method but implements it differently: + +```js +var brushes = { + outline: { + draw: function (event, context) { + context.strokeRect(event.pageX, event.pageY, 10, 10); + } + }, + square: { + draw: function (event, context) { + context.fillRect(event.pageX, event.pageY, 10, 10); + } + }, + circle: { + draw: function (event, context) { + context.arc(event.pageX, event.pageY, 10, 0, Math.PI * 2); + context.fill(); + } + } +}; +``` + +The drawing code does not need to know which shape is selected: + +```js +brush.draw(event, context); +``` + +Clicking a button replaces the active strategy: + +```js +brush = brushes[brushType]; +``` + +### When it is useful + +Use Strategy when an application needs interchangeable sorting, payment, authentication, validation, compression, routing, or pricing algorithms. + +### Exercise + +Add a `largeSquare` or `line` brush. It should provide the same `draw(event, context)` interface as the existing brushes. + +--- + +## Expected output summary + +| Example | Result | +| --- | --- | +| Simple Factory | `Admin created!`, then `Customer created!` | +| Factory Method | `Veggie, Deep-dish Chicago Pizza` | +| Abstract Factory | `thin and crispy` | +| Decorator: Sale | `CDN$52.50` | +| Decorator: Sale Closures | `$$$50`, then `$$$100` | +| Decorator: Validator | `['no name!', 'no age!', 'no zip!']` | +| Iterator: Arrays | No output until iterator methods are called | +| Iterator: Objects | No output until iterator methods are called | +| Observer | No output until subscribers and events are added | +| Singleton/Namespace | No output | +| Strategy: Painter | Interactive canvas; no console output | + +## A practical learning method + +Use this loop for every example: + +```text +Predict -> Run -> Modify -> Break -> Repair -> Rebuild from memory +``` + +After understanding an example, recreate it in another domain. For instance: + +- Factory: create notification providers such as email, SMS, and push. +- Strategy: select credit card, UPI, or wallet payment behavior. +- Observer: notify UI components when application state changes. +- Decorator: add logging, caching, and authorization around a service. +- Iterator: traverse a custom tree or paginated result set. + +The main goal is not to memorize the code. Learn to recognize the problem each pattern solves, the parts allowed to vary, and whether the pattern makes the program clearer than a simpler function or object would. + +## Patterns listed but not implemented + +The original project plan mentioned several additional Gang of Four patterns, including Builder, Prototype, Adapter, Composite, Facade, Proxy, Command, Mediator, State, Template Method, and Visitor. They do not currently have example directories in this repository. + +## Further reading + +- *Design Patterns: Elements of Reusable Object-Oriented Software* by Gamma, Helm, Johnson, and Vlissides +- *JavaScript Patterns* by Stoyan Stefanov +- The original article series: