├── assets
└── star.png
├── LICENSE
└── README.md
/assets/star.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learning-zone/javascript-design-patterns/HEAD/assets/star.png
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Pradeep Kumar
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # JavaScript Design Patterns
2 |
3 | > *Click ★ if you like the project. Your contributions are heartily ♡ welcome.*
4 |
5 |
6 |
7 | ## Table of Contents
8 |
9 | * [Introduction](#-introduction)
10 | * [Types of Design Patterns](#-types-of-design-patterns)
11 | * [Creational Design Patterns](#-creational-design-patterns)
12 | * [Simple Factory](#-simple-factory-design-pattern)
13 | * [Factory Method](#-factory-method-design-pattern)
14 | * [Abstract Factory](#-abstract-factory-design-pattern)
15 | * [Builder](#-builder-design-pattern)
16 | * [Prototype](#-prototype-design-pattern)
17 | * [Singleton](#-singleton-design-pattern)
18 | * [Structural Design Patterns](#-structural-design-patterns)
19 | * [Adapter](#-adapter-design-pattern)
20 | * [Bridge](#-bridge-design-pattern)
21 | * [Composite](#-composite-design-pattern)
22 | * [Decorator](#-decorator-design-pattern)
23 | * [Facade](#-facade-design-pattern)
24 | * [Flyweight](#-flyweight-design-pattern)
25 | * [Proxy](#-proxy-design-pattern)
26 | * [Behavioral Design Patterns](#-behavioral-design-patterns)
27 | * [Chain of Responsibility](#-chain-of-responsibility-design-pattern)
28 | * [Command](#-command-design-pattern)
29 | * [Iterator](#-iterator-design-pattern)
30 | * [Mediator](#-mediator-design-pattern)
31 | * [Memento](#-memento-design-pattern)
32 | * [Observer](#-observer-design-pattern)
33 | * [Visitor](#-visitor-design-pattern)
34 | * [Strategy](#-strategy-design-pattern)
35 | * [State](#-state-design-pattern)
36 | * [Template Method](#-template-method-design-pattern)
37 |
38 | ## # Introduction
39 |
40 | Design patterns are solutions to recurring problems **guidelines on how to tackle certain problems**. They are not classes, packages or libraries that you can plug into your application and wait for the magic to happen. These are, rather, guidelines on how to tackle certain problems in certain situations.
41 |
42 | > Design patterns solutions to recurring problems guidelines on how to tackle certain problems
43 |
44 | Wikipedia describes them as
45 |
46 | > In software engineering, a software design pattern is a general reusable solution to a commonly occurring problem within a given context in software design. It is not a finished design that can be transformed directly into source or machine code. It is a description or template for how to solve a problem that can be used in many different situations.
47 |
48 |
51 |
52 | ## # Types of Design Patterns
53 |
54 | * [Creational Design Patterns](#-creational-design-patterns)
55 | * [Structural Design Patterns](#-structural-design-patterns)
56 | * [Behavioral Design Patterns](#-behavioral-design-patterns)
57 |
58 | ## # Creational Design Patterns
59 |
60 | In plain words
61 | > Creational patterns are focused towards how to instantiate an object or group of related objects.
62 |
63 | Wikipedia says
64 | > In software engineering, creational design patterns are design patterns that deal with object creation mechanisms, trying to create objects in a manner suitable to the situation. The basic form of object creation could result in design problems or added complexity to the design. Creational design patterns solve this problem by somehow controlling this object creation.
65 |
66 | * [Simple Factory Design Pattern](#-simple-factory-design-pattern)
67 | * [Factory Method Design Pattern](#-factory-method-design-pattern)
68 | * [Abstract Factory Design Pattern](#-abstract-factory-design-pattern)
69 | * [Builder Design Pattern](#-builder-design-pattern)
70 | * [Prototype Design Pattern](#-prototype-design-pattern)
71 | * [Singleton Design Pattern](#-singleton-design-pattern)
72 |
73 |
76 |
77 | ## # Simple Factory Design Pattern
78 |
79 | Real world example
80 | > Consider, you are building a house and you need doors. It would be a mess if every time you need a door, you put on your carpenter clothes and start making a door in your house. Instead you get it made from a factory.
81 |
82 | In plain words
83 | > Simple factory simply generates an instance for client without exposing any instantiation logic to the client
84 |
85 | Wikipedia says
86 | > In object-oriented programming (OOP), a factory is an object for creating other objects – formally a factory is a function or method that returns objects of a varying prototype or class from some method call, which is assumed to be "new".
87 |
88 | **Example:**
89 |
90 | First of all we have a door interface and the implementation
91 |
92 | ```js
93 | /*
94 | Door
95 |
96 | getWidth()
97 | getHeight()
98 |
99 | */
100 |
101 | class WoodenDoor {
102 | constructor(width, height){
103 | this.width = width
104 | this.height = height
105 | }
106 |
107 | getWidth(){
108 | return this.width
109 | }
110 |
111 | getHeight(){
112 | return this.height
113 | }
114 | }
115 | ```
116 |
117 | Then we have our door factory that makes the door and returns it
118 |
119 | ```js
120 | const DoorFactory = {
121 | makeDoor : (width, height) => new WoodenDoor(width, height)
122 | }
123 | ```
124 |
125 | And then it can be used as
126 |
127 | ```js
128 | const door = DoorFactory.makeDoor(100, 200)
129 | console.log('Width:', door.getWidth())
130 | console.log('Height:', door.getHeight())
131 | ```
132 |
133 | **When to Use?**
134 |
135 | When creating an object is not just a few assignments and involves some logic, it makes sense to put it in a dedicated factory instead of repeating the same code everywhere.
136 |
137 |
140 |
141 | ## # Factory Method Design Pattern
142 |
143 | Real world example
144 | > Consider the case of a hiring manager. It is impossible for one person to interview for each of the positions. Based on the job opening, she has to decide and delegate the interview steps to different people.
145 |
146 | In plain words
147 | > It provides a way to delegate the instantiation logic to child classes.
148 |
149 | Wikipedia says
150 | > In class-based programming, the factory method pattern is a creational pattern that uses factory methods to deal with the problem of creating objects without having to specify the exact class of the object that will be created. This is done by creating objects by calling a factory method—either specified in an interface and implemented by child classes, or implemented in a base class and optionally overridden by derived classes—rather than by calling a constructor.
151 |
152 | **Example:**
153 |
154 | Taking our hiring manager example above. First of all we have an interviewer interface and some implementations for it
155 |
156 | ```js
157 | /*
158 | Interviewer interface
159 |
160 | askQuestions()
161 | */
162 |
163 | class Developer {
164 | askQuestions() {
165 | console.log('Asking about design patterns!')
166 | }
167 | }
168 |
169 | class CommunityExecutive {
170 | askQuestions() {
171 | console.log('Asking about community building')
172 | }
173 | }
174 | ```
175 |
176 | Now let us create our `HiringManager`
177 |
178 | ```js
179 | class HiringManager {
180 |
181 | takeInterview() {
182 | const interviewer = this.makeInterviewer()
183 | interviewer.askQuestions()
184 | }
185 | }
186 | ```
187 |
188 | Now any child can extend it and provide the required interviewer
189 |
190 | ```js
191 | class DevelopmentManager extends HiringManager {
192 | makeInterviewer() {
193 | return new Developer()
194 | }
195 | }
196 |
197 | class MarketingManager extends HiringManager {
198 | makeInterviewer() {
199 | return new CommunityExecutive()
200 | }
201 | }
202 | ```
203 |
204 | and then it can be used as
205 |
206 | ```js
207 | const devManager = new DevelopmentManager()
208 | devManager.takeInterview() // Output: Asking about design patterns
209 |
210 | const marketingManager = new MarketingManager()
211 | marketingManager.takeInterview() // Output: Asking about community buildng.
212 | ```
213 |
214 | **When to use?**
215 |
216 | Useful when there is some generic processing in a class but the required sub-class is dynamically decided at runtime. Or putting it in other words, when the client doesn't know what exact sub-class it might need.
217 |
218 |
221 |
222 | ## # Abstract Factory Design Pattern
223 |
224 | Real world example
225 | > Extending our door example from Simple Factory. Based on your needs you might get a wooden door from a wooden door shop, iron door from an iron shop or a PVC door from the relevant shop. Plus you might need a guy with different kind of specialities to fit the door, for example a carpenter for wooden door, welder for iron door etc. As you can see there is a dependency between the doors now, wooden door needs carpenter, iron door needs a welder etc.
226 |
227 | In plain words
228 | > A factory of factories a factory that groups the individual but related/dependent factories together without specifying their concrete classes.
229 |
230 | Wikipedia says
231 | > The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes
232 |
233 | **Example:**
234 |
235 | Translating the door example above. First of all we have our `Door` interface and some implementation for it
236 |
237 | ```js
238 | /*
239 | Door interface :
240 |
241 | getDescription()
242 | */
243 |
244 | class WoodenDoor {
245 | getDescription() {
246 | console.log('I am a wooden door')
247 | }
248 | }
249 |
250 | class IronDoor {
251 | getDescription() {
252 | console.log('I am an iron door')
253 | }
254 | }
255 | ```
256 |
257 | Then we have some fitting experts for each door type
258 |
259 | ```js
260 | /*
261 | DoorFittingExpert interface :
262 |
263 | getDescription()
264 | */
265 |
266 | class Welder {
267 | getDescription() {
268 | console.log('I can only fit iron doors')
269 | }
270 | }
271 |
272 | class Carpenter {
273 | getDescription() {
274 | console.log('I can only fit wooden doors')
275 | }
276 | }
277 | ```
278 |
279 | Now we have our abstract factory that would let us make family of related objects i.e. wooden door factory would create a wooden door and wooden door fitting expert and iron door factory would create an iron door and iron door fitting expert
280 |
281 | ```js
282 | /*
283 | DoorFactory interface :
284 |
285 | makeDoor()
286 | makeFittingExpert()
287 | */
288 |
289 | // Wooden factory to return carpenter and wooden door
290 | class WoodenDoorFactory {
291 | makeDoor(){
292 | return new WoodenDoor()
293 | }
294 |
295 | makeFittingExpert() {
296 | return new Carpenter()
297 | }
298 | }
299 |
300 | // Iron door factory to get iron door and the relevant fitting expert
301 | class IronDoorFactory {
302 | makeDoor(){
303 | return new IronDoor()
304 | }
305 |
306 | makeFittingExpert() {
307 | return new Welder()
308 | }
309 | }
310 | ```
311 |
312 | And then it can be used as
313 |
314 | ```js
315 | woodenFactory = new WoodenDoorFactory()
316 |
317 | door = woodenFactory.makeDoor()
318 | expert = woodenFactory.makeFittingExpert()
319 |
320 | door.getDescription() // Output: I am a wooden door
321 | expert.getDescription() // Output: I can only fit wooden doors
322 |
323 | // Same for Iron Factory
324 | ironFactory = new IronDoorFactory()
325 |
326 | door = ironFactory.makeDoor()
327 | expert = ironFactory.makeFittingExpert()
328 |
329 | door.getDescription() // Output: I am an iron door
330 | expert.getDescription() // Output: I can only fit iron doors
331 | ```
332 |
333 | As you can see the wooden door factory has encapsulated the `carpenter` and the `wooden door` also iron door factory has encapsulated the `iron door` and `welder`. And thus it had helped us make sure that for each of the created door, we do not get a wrong fitting expert.
334 |
335 | **When to use?**
336 |
337 | When there are interrelated dependencies with not-that-simple creation logic involved
338 |
339 |
342 |
343 | ## # Builder Design Pattern
344 |
345 | Real world example
346 | > Imagine you are at Hardee's and you order a specific deal, lets say, "Big Hardee" and they hand it over to you without *any questions* this is the example of simple factory. But there are cases when the creation logic might involve more steps. For example you want a customized Subway deal, you have several options in how your burger is made e.g what bread do you want? what types of sauces would you like? What cheese would you want? etc. In such cases builder pattern comes to the rescue.
347 |
348 | In plain words
349 | > Allows you to create different flavors of an object while avoiding constructor pollution. Useful when there could be several flavors of an object. Or when there are a lot of steps involved in creation of an object.
350 |
351 | Wikipedia says
352 | > The builder pattern is an object creation software design pattern with the intentions of finding a solution to the telescoping constructor anti-pattern.
353 |
354 | Having said that let me add a bit about what telescoping constructor anti-pattern is. At one point or the other we have all seen a constructor like below:
355 |
356 | ```js
357 | constructor(size, cheese = true, pepperoni = true, tomato = false, lettuce = true) {
358 | // ...
359 | }
360 | ```
361 |
362 | As you can see the number of constructor parameters can quickly get out of hand and it might become difficult to understand the arrangement of parameters. Plus this parameter list could keep on growing if you would want to add more options in future. This is called telescoping constructor anti-pattern.
363 |
364 | **Example:**
365 |
366 | The sane alternative is to use the builder pattern. First of all we have our burger that we want to make
367 |
368 | ```js
369 | class Burger {
370 | constructor(builder) {
371 | this.size = builder.size
372 | this.cheeze = builder.cheeze || false
373 | this.pepperoni = builder.pepperoni || false
374 | this.lettuce = builder.lettuce || false
375 | this.tomato = builder.tomato || false
376 | }
377 | }
378 | ```
379 |
380 | And then we have the builder
381 |
382 | ```js
383 | class BurgerBuilder {
384 |
385 | constructor(size) {
386 | this.size = size
387 | }
388 |
389 | addPepperoni() {
390 | this.pepperoni = true
391 | return this
392 | }
393 |
394 | addLettuce() {
395 | this.lettuce = true
396 | return this
397 | }
398 |
399 | addCheeze() {
400 | this.cheeze = true
401 | return this
402 | }
403 |
404 | addTomato() {
405 | this.tomato = true
406 | return this
407 | }
408 |
409 | build() {
410 | return new Burger(this)
411 | }
412 | }
413 | ```
414 |
415 | And then it can be used as:
416 |
417 | ```js
418 | const burger = (new BurgerBuilder(14))
419 | .addPepperoni()
420 | .addLettuce()
421 | .addTomato()
422 | .build()
423 | ```
424 |
425 | __Javascript specific tip__ : When you find that the number of arguments to a function or method are too many (normally any more than 2 arguments is considered too much), use a single object argument instead of multiple arguments. This serves two purposes :
426 |
427 | 1. It makes your code look less cluttered, since there is only one argument.
428 | 2. You don't have to worry about the order of arguments since arguments are now passed as named properties of the object.
429 |
430 | For example :
431 |
432 | ```js
433 | const burger = new Burger({
434 | size : 14,
435 | pepperoni : true,
436 | cheeze : false,
437 | lettuce : true,
438 | tomato : true
439 | })
440 | ```
441 |
442 | instead of :
443 |
444 | ```
445 | const burger = new Burger(14, true, false, true, true)
446 | ```
447 |
448 | **When to use?**
449 |
450 | When there could be several flavors of an object and to avoid the constructor telescoping. The key difference from the factory pattern is that factory pattern is to be used when the creation is a one step process while builder pattern is to be used when the creation is a multi step process.
451 |
452 |
455 |
456 | ## # Singleton Design Pattern
457 |
458 | Real world example
459 | > There can only be one president of a country at a time. The same president has to be brought to action, whenever duty calls. President here is singleton.
460 |
461 | In plain words
462 | > Ensures that only one object of a particular class is ever created.
463 |
464 | Wikipedia says
465 | > In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.
466 |
467 | Singleton pattern is actually considered an anti-pattern and overuse of it should be avoided. It is not necessarily bad and could have some valid use-cases but should be used with caution because it introduces a global state in your application and change to it in one place could affect in the other areas and it could become pretty difficult to debug. The other bad thing about them is it makes your code tightly coupled plus it mocking the singleton could be difficult.
468 |
469 | **Example:**
470 |
471 | In javascript, singletons can be implemented using the module pattern. Private variables and functions are hidden in a function closure, and public methods are selectively exposed.
472 |
473 | ```js
474 | const president = (function(){
475 | const presidentsPrivateInformation = 'Super private'
476 |
477 | const name = 'Turd Sandwich'
478 |
479 | const getName = () => name
480 |
481 | return {
482 | getName
483 | }
484 | }())
485 | ```
486 |
487 | Here, `presidentsPrivateInformation` and `name` are kept private. However, `name` can be accessed with the exposed `president.getName` method.
488 |
489 | ```js
490 | president.getName() // Outputs 'Turd Sandwich'
491 | president.name // Outputs undefined
492 | president.presidentsPrivateInformation // Outputs undefined
493 | ```
494 |
495 |
498 |
499 | ## # Structural Design Patterns
500 |
501 | In plain words
502 | > Structural patterns are mostly concerned with object composition or in other words how the entities can use each other. Or yet another explanation would be, they help in answering "How to build a software component?"
503 |
504 | Wikipedia says
505 | > In software engineering, structural design patterns are design patterns that ease the design by identifying a simple way to realize relationships between entities.
506 |
507 | * [Adapter Design Pattern](#-adapter-design-pattern)
508 | * [Bridge Design Pattern](#-bridge-design-pattern)
509 | * [Composite Design Pattern](#-composite-design-pattern)
510 | * [Decorator Design Pattern](#-decorator-design-pattern)
511 | * [Facade Design Pattern](#-facade-design-pattern)
512 | * [Flyweight Design Pattern](#-flyweight-design-pattern)
513 | * [Proxy Design Pattern](#-proxy-design-pattern)
514 |
515 | ## # Adapter Design Pattern
516 |
517 | Real world example
518 | > Consider that you have some pictures in your memory card and you need to transfer them to your computer. In order to transfer them you need some kind of adapter that is compatible with your computer ports so that you can attach memory card to your computer. In this case card reader is an adapter.
519 | > Another example would be the famous power adapter a three legged plug can't be connected to a two pronged outlet, it needs to use a power adapter that makes it compatible with the two pronged outlet.
520 | > Yet another example would be a translator translating words spoken by one person to another
521 |
522 | In plain words
523 | > Adapter pattern lets you wrap an otherwise incompatible object in an adapter to make it compatible with another class.
524 |
525 | Wikipedia says
526 | > In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
527 |
528 | **Example:**
529 |
530 | Consider a game where there is a hunter and he hunts lions.
531 |
532 | First we have an interface `Lion` that all types of lions have to implement
533 |
534 | ```js
535 | /*
536 | Lion interface :
537 |
538 | roar()
539 | */
540 |
541 | class AfricanLion {
542 | roar() {}
543 | }
544 |
545 | class AsianLion {
546 | roar() {}
547 | }
548 | ```
549 |
550 | And hunter expects any implementation of `Lion` interface to hunt.
551 |
552 | ```js
553 | class Hunter {
554 | hunt(lion) {
555 | // ... some code before
556 | lion.roar()
557 | //... some code after
558 | }
559 | }
560 | ```
561 |
562 | Now let\'s say we have to add a `WildDog` in our game so that hunter can hunt that also. But we can't do that directly because dog has a different interface. To make it compatible for our hunter, we will have to create an adapter that is compatible
563 |
564 | ```js
565 | // This needs to be added to the game
566 | class WildDog {
567 | bark() {
568 | }
569 | }
570 |
571 | // Adapter around wild dog to make it compatible with our game
572 | class WildDogAdapter {
573 |
574 | constructor(dog) {
575 | this.dog = dog;
576 | }
577 |
578 | roar() {
579 | this.dog.bark();
580 | }
581 | }
582 | ```
583 |
584 | And now the `WildDog` can be used in our game using `WildDogAdapter`.
585 |
586 | ```js
587 | wildDog = new WildDog()
588 | wildDogAdapter = new WildDogAdapter(wildDog)
589 |
590 | hunter = new Hunter()
591 | hunter.hunt(wildDogAdapter)
592 | ```
593 |
594 |
597 |
598 | ## # Bridge Design Pattern
599 |
600 | Real world example
601 | > Consider you have a website with different pages and you are supposed to allow the user to change the theme. What would you do? Create multiple copies of each of the pages for each of the themes or would you just create separate theme and load them based on the user's preferences? Bridge pattern allows you to do the second i.e.
602 |
603 | 
604 |
605 | In Plain Words
606 | > Bridge pattern is about preferring composition over inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy.
607 |
608 | Wikipedia says
609 | > The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently"
610 |
611 | **Example:**
612 |
613 | Translating our WebPage example from above. Here we have the `WebPage` hierarchy
614 |
615 | ```js
616 | /*
617 | Webpage interface :
618 |
619 | constructor(theme)
620 | getContent()
621 | */
622 |
623 | class About{
624 | constructor(theme) {
625 | this.theme = theme
626 | }
627 |
628 | getContent() {
629 | return "About page in " + this.theme.getColor()
630 | }
631 | }
632 |
633 | class Careers{
634 | constructor(theme) {
635 | this.theme = theme
636 | }
637 |
638 | getContent() {
639 | return "Careers page in " + this.theme.getColor()
640 | }
641 | }
642 | ```
643 |
644 | And the separate theme hierarchy
645 |
646 | ```js
647 | /*
648 | Theme interface :
649 |
650 | getColor()
651 | */
652 |
653 | class DarkTheme{
654 | getColor() {
655 | return 'Dark Black'
656 | }
657 | }
658 | class LightTheme{
659 | getColor() {
660 | return 'Off white'
661 | }
662 | }
663 | class AquaTheme{
664 | getColor() {
665 | return 'Light blue'
666 | }
667 | }
668 | ```
669 |
670 | And both the hierarchies
671 |
672 | ```js
673 | const darkTheme = new DarkTheme()
674 |
675 | const about = new About(darkTheme)
676 | const careers = new Careers(darkTheme)
677 |
678 | console.log(about.getContent() )// "About page in Dark Black"
679 | console.log(careers.getContent() )// "Careers page in Dark Black"
680 | ```
681 |
682 |
685 |
686 | ## # Composite Design Pattern
687 |
688 | Real world example
689 | > Every organization is composed of employees. Each of the employees has same features i.e. has a salary, has some responsibilities, may or may not report to someone, may or may not have some subordinates etc.
690 |
691 | In plain words
692 | > Composite pattern lets clients to treat the individual objects in a uniform manner.
693 |
694 | Wikipedia says
695 | > In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.
696 |
697 | **Example:**
698 |
699 | Taking our employees example from above. Here we have different employee types
700 |
701 | ```js
702 | /*
703 | Employee interface :
704 |
705 | constructor(name, salary)
706 | getName()
707 | setSalary()
708 | getSalary()
709 | getRoles()
710 | */
711 |
712 | class Developer {
713 |
714 | constructor(name, salary) {
715 | this.name = name
716 | this.salary = salary
717 | }
718 |
719 | getName() {
720 | return this.name
721 | }
722 |
723 | setSalary(salary) {
724 | this.salary = salary
725 | }
726 |
727 | getSalary() {
728 | return this.salary
729 | }
730 |
731 | getRoles() {
732 | return this.roles
733 | }
734 |
735 | develop() {
736 | /* */
737 | }
738 | }
739 |
740 | class Designer {
741 |
742 | constructor(name, salary) {
743 | this.name = name
744 | this.salary = salary
745 | }
746 |
747 | getName() {
748 | return this.name
749 | }
750 |
751 | setSalary(salary) {
752 | this.salary = salary
753 | }
754 |
755 | getSalary() {
756 | return this.salary
757 | }
758 |
759 | getRoles() {
760 | return this.roles
761 | }
762 |
763 | design() {
764 | /* */
765 | }
766 | }
767 | ```
768 |
769 | Then we have an organization which consists of several different types of employees
770 |
771 | ```js
772 | class Organization {
773 | constructor(){
774 | this.employees = []
775 | }
776 |
777 | addEmployee(employee) {
778 | this.employees.push(employee)
779 | }
780 |
781 | getNetSalaries() {
782 | let netSalary = 0
783 |
784 | this.employees.forEach(employee => {
785 | netSalary += employee.getSalary()
786 | })
787 |
788 | return netSalary
789 | }
790 | }
791 | ```
792 |
793 | And then it can be used as
794 |
795 | ```js
796 | // Prepare the employees
797 | const john = new Developer('John Doe', 12000)
798 | const jane = new Designer('Jane', 10000)
799 |
800 | // Add them to organization
801 | const organization = new Organization()
802 | organization.addEmployee(john)
803 | organization.addEmployee(jane)
804 |
805 | console.log("Net salaries: " , organization.getNetSalaries()) // Net Salaries: 22000
806 | ```
807 |
808 |
811 |
812 | ## # Decorator Design Pattern
813 |
814 | Real world example
815 |
816 | > Imagine you run a car service shop offering multiple services. Now how do you calculate the bill to be charged? You pick one service and dynamically keep adding to it the prices for the provided services till you get the final cost. Here each type of service is a decorator.
817 |
818 | In plain words
819 | > Decorator pattern lets you dynamically change the behavior of an object at run time by wrapping them in an object of a decorator class.
820 |
821 | Wikipedia says
822 | > In object-oriented programming, the decorator pattern is a design pattern that allows behavior to be added to an individual object, either statically or dynamically, without affecting the behavior of other objects from the same class. The decorator pattern is often useful for adhering to the Single Responsibility Principle, as it allows functionality to be divided between classes with unique areas of concern.
823 |
824 | **Example:**
825 |
826 | Lets take coffee for example. First of all we have a simple coffee implementing the coffee interface
827 |
828 | ```js
829 | /*
830 | Coffee interface:
831 | getCost()
832 | getDescription()
833 | */
834 |
835 | class SimpleCoffee{
836 |
837 | getCost() {
838 | return 10
839 | }
840 |
841 | getDescription() {
842 | return 'Simple coffee'
843 | }
844 | }
845 | ```
846 | We want to make the code extensible to allow options to modify it if required. Lets make some add-ons (decorators)
847 | ```js
848 | class MilkCoffee {
849 |
850 |
851 | constructor(coffee) {
852 | this.coffee = coffee
853 | }
854 |
855 | getCost() {
856 | return this.coffee.getCost() + 2
857 | }
858 |
859 | getDescription() {
860 | return this.coffee.getDescription() + ', milk'
861 | }
862 | }
863 |
864 | class WhipCoffee {
865 |
866 | constructor(coffee) {
867 | this.coffee = coffee
868 | }
869 |
870 | getCost() {
871 | return this.coffee.getCost() + 5
872 | }
873 |
874 | getDescription() {
875 | return this.coffee.getDescription() + ', whip'
876 | }
877 | }
878 |
879 | class VanillaCoffee {
880 |
881 | constructor(coffee) {
882 | this.coffee = coffee
883 | }
884 |
885 | getCost() {
886 | return this.coffee.getCost() + 3
887 | }
888 |
889 | getDescription() {
890 | return this.coffee.getDescription() + ', vanilla'
891 | }
892 | }
893 |
894 | ```
895 |
896 | Lets make a coffee now
897 |
898 | ```js
899 | let someCoffee
900 |
901 | someCoffee = new SimpleCoffee()
902 | console.log(someCoffee.getCost())// 10
903 | console.log(someCoffee.getDescription())// Simple Coffee
904 |
905 | someCoffee = new MilkCoffee(someCoffee)
906 | console.log(someCoffee.getCost())// 12
907 | console.log(someCoffee.getDescription())// Simple Coffee, milk
908 |
909 | someCoffee = new WhipCoffee(someCoffee)
910 | console.log(someCoffee.getCost())// 17
911 | console.log(someCoffee.getDescription())// Simple Coffee, milk, whip
912 |
913 | someCoffee = new VanillaCoffee(someCoffee)
914 | console.log(someCoffee.getCost())// 20
915 | console.log(someCoffee.getDescription())// Simple Coffee, milk, whip, vanilla
916 | ```
917 |
918 |
921 |
922 | ## # Facade Design Pattern
923 |
924 | Real world example
925 | > How do you turn on the computer? "Hit the power button" you say! That is what you believe because you are using a simple interface that computer provides on the outside, internally it has to do a lot of stuff to make it happen. This simple interface to the complex subsystem is a facade.
926 |
927 | In plain words
928 | > Facade pattern provides a simplified interface to a complex subsystem.
929 |
930 | Wikipedia says
931 | > A facade is an object that provides a simplified interface to a larger body of code, such as a class library.
932 |
933 | **Example:**
934 | Taking our computer example from above. Here we have the computer class
935 |
936 | ```js
937 | class Computer {
938 |
939 | getElectricShock() {
940 | console.log('Ouch!')
941 | }
942 |
943 | makeSound() {
944 | console.log('Beep beep!')
945 | }
946 |
947 | showLoadingScreen() {
948 | console.log('Loading..')
949 | }
950 |
951 | bam() {
952 | console.log('Ready to be used!')
953 | }
954 |
955 | closeEverything() {
956 | console.log('Bup bup bup buzzzz!')
957 | }
958 |
959 | sooth() {
960 | console.log('Zzzzz')
961 | }
962 |
963 | pullCurrent() {
964 | console.log('Haaah!')
965 | }
966 | }
967 | ```
968 | Here we have the facade
969 | ```js
970 | class ComputerFacade
971 | {
972 | constructor(computer) {
973 | this.computer = computer
974 | }
975 |
976 | turnOn() {
977 | this.computer.getElectricShock()
978 | this.computer.makeSound()
979 | this.computer.showLoadingScreen()
980 | this.computer.bam()
981 | }
982 |
983 | turnOff() {
984 | this.computer.closeEverything()
985 | this.computer.pullCurrent()
986 | this.computer.sooth()
987 | }
988 | }
989 | ```
990 |
991 | Now to use the facade
992 |
993 | ```js
994 | const computer = new ComputerFacade(new Computer())
995 | computer.turnOn() // Ouch! Beep beep! Loading.. Ready to be used!
996 | computer.turnOff() // Bup bup buzzz! Haah! Zzzzz
997 | ```
998 |
999 |
1002 |
1003 | ## # Flyweight Design Pattern
1004 |
1005 | Real world example
1006 | > Did you ever have fresh tea from some stall? They often make more than one cup that you demanded and save the rest for any other customer so to save the resources e.g. gas etc. Flyweight pattern is all about that i.e. sharing.
1007 |
1008 | In plain words
1009 | > It is used to minimize memory usage or computational expenses by sharing as much as possible with similar objects.
1010 |
1011 | Wikipedia says
1012 | > In computer programming, flyweight is a software design pattern. A flyweight is an object that minimizes memory use by sharing as much data as possible with other similar objects it is a way to use objects in large numbers when a simple repeated representation would use an unacceptable amount of memory.
1013 |
1014 | **Example:**
1015 | Translating our tea example from above. First of all we have tea types and tea maker
1016 |
1017 | ```js
1018 | // Anything that will be cached is flyweight.
1019 | // Types of tea here will be flyweights.
1020 | class KarakTea {
1021 | }
1022 |
1023 | // Acts as a factory and saves the tea
1024 | class TeaMaker {
1025 | constructor(){
1026 | this.availableTea = {}
1027 | }
1028 |
1029 | make(preference) {
1030 | this.availableTea[preference] = this.availableTea[preference] || (new KarakTea())
1031 | return this.availableTea[preference]
1032 | }
1033 | }
1034 | ```
1035 |
1036 | Then we have the `TeaShop` which takes orders and serves them
1037 |
1038 | ```js
1039 | class TeaShop {
1040 | constructor(teaMaker) {
1041 | this.teaMaker = teaMaker
1042 | this.orders = []
1043 | }
1044 |
1045 | takeOrder(teaType, table) {
1046 | this.orders[table] = this.teaMaker.make(teaType)
1047 | }
1048 |
1049 | serve() {
1050 | this.orders.forEach((order, index) => {
1051 | console.log('Serving tea to table#' + index)
1052 | })
1053 | }
1054 | }
1055 | ```
1056 | And it can be used as below
1057 |
1058 | ```js
1059 | const teaMaker = new TeaMaker()
1060 | const shop = new TeaShop(teaMaker)
1061 |
1062 | shop.takeOrder('less sugar', 1)
1063 | shop.takeOrder('more milk', 2)
1064 | shop.takeOrder('without sugar', 5)
1065 |
1066 | shop.serve()
1067 | // Serving tea to table# 1
1068 | // Serving tea to table# 2
1069 | // Serving tea to table# 5
1070 | ```
1071 |
1072 |
1075 |
1076 | ## # Proxy Design Pattern
1077 |
1078 | Real world example
1079 | > Have you ever used an access card to go through a door? There are multiple options to open that door i.e. it can be opened either using access card or by pressing a button that bypasses the security. The door's main functionality is to open but there is a proxy added on top of it to add some functionality. Let me better explain it using the code example below.
1080 |
1081 | In plain words
1082 | > Using the proxy pattern, a class represents the functionality of another class.
1083 |
1084 | Wikipedia says
1085 | > A proxy, in its most general form, is a class functioning as an interface to something else. A proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked.
1086 |
1087 | **Example:**
1088 | Taking our security door example from above. Firstly we have the door interface and an implementation of door
1089 |
1090 | ```js
1091 | /*
1092 | Door interface :
1093 |
1094 | open()
1095 | close()
1096 | */
1097 |
1098 | class LabDoor {
1099 | open() {
1100 | console.log('Opening lab door')
1101 | }
1102 |
1103 | close() {
1104 | console.log('Closing the lab door')
1105 | }
1106 | }
1107 | ```
1108 | Then we have a proxy to secure any doors that we want
1109 | ```js
1110 | class Security {
1111 | constructor(door) {
1112 | this.door = door
1113 | }
1114 |
1115 | open(password) {
1116 | if (this.authenticate(password)) {
1117 | this.door.open()
1118 | } else {
1119 | console.log('Big no! It ain\'t possible.')
1120 | }
1121 | }
1122 |
1123 | authenticate(password) {
1124 | return password === 'ecr@t'
1125 | }
1126 |
1127 | close() {
1128 | this.door.close()
1129 | }
1130 | }
1131 | ```
1132 | And here is how it can be used
1133 | ```js
1134 | const door = new Security(new LabDoor())
1135 | door.open('invalid') // Big no! It ain't possible.
1136 |
1137 | door.open('ecr@t') // Opening lab door
1138 | door.close() // Closing lab door
1139 | ```
1140 |
1141 |
1144 |
1145 | ## # Behavioral Design Patterns
1146 |
1147 | In plain words
1148 | > It is concerned with assignment of responsibilities between the objects. What makes them different from structural patterns is they don't just specify the structure but also outline the patterns for message passing/communication between them. Or in other words, they assist in answering "How to run a behavior in software component?"
1149 |
1150 | Wikipedia says
1151 | > In software engineering, behavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication.
1152 |
1153 | * [Chain of Responsibility Design Pattern](#-chain-of-responsibility-design-pattern)
1154 | * [Command Design Pattern](#-command-design-pattern)
1155 | * [Iterator Design Pattern](#-iterator-design-pattern)
1156 | * [Mediator Design Pattern](#-mediator-design-pattern)
1157 | * [Memento Design Pattern](#-memento-design-pattern)
1158 | * [Observer Design Pattern](#-observer-design-pattern)
1159 | * [Visitor Design Pattern](#-visitor-design-pattern)
1160 | * [Strategy Design Pattern](#-strategy-design-pattern)
1161 | * [State Design Pattern](#-state-design-pattern)
1162 | * [Template Method Design Pattern](#-template-method-design-pattern)
1163 |
1164 | ## # Chain of Responsibility Design Pattern
1165 |
1166 | Real world example
1167 | > For example, you have three payment methods (`A`, `B` and `C`) setup in your account each having a different amount in it. `A` has 100 USD, `B` has 300 USD and `C` having 1000 USD and the preference for payments is chosen as `A` then `B` then `C`. You try to purchase something that is worth 210 USD. Using Chain of Responsibility, first of all account `A` will be checked if it can make the purchase, if yes purchase will be made and the chain will be broken. If not, request will move forward to account `B` checking for amount if yes chain will be broken otherwise the request will keep forwarding till it finds the suitable handler. Here `A`, `B` and `C` are links of the chain and the whole phenomenon is Chain of Responsibility.
1168 |
1169 | In plain words
1170 | > It helps building a chain of objects. Request enters from one end and keeps going from object to object till it finds the suitable handler.
1171 |
1172 | Wikipedia says
1173 | > In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle the rest are passed to the next processing object in the chain.
1174 |
1175 | **Example:**
1176 |
1177 | Translating our account example above. First of all we have a base account having the logic for chaining the accounts together and some accounts
1178 |
1179 | ```js
1180 | class Account {
1181 |
1182 | setNext(account) {
1183 | this.successor = account
1184 | }
1185 |
1186 | pay(amountToPay) {
1187 | if (this.canPay(amountToPay)) {
1188 | console.log(`Paid ${amountToPay} using ${this.name}`)
1189 | } else if (this.successor) {
1190 | console.log(`Cannot pay using ${this.name}. Proceeding...`)
1191 | this.successor.pay(amountToPay)
1192 | } else {
1193 | console.log('None of the accounts have enough balance')
1194 | }
1195 | }
1196 |
1197 | canPay(amount) {
1198 | return this.balance >= amount
1199 | }
1200 | }
1201 |
1202 | class Bank extends Account {
1203 | constructor(balance) {
1204 | super()
1205 | this.name = 'bank'
1206 | this.balance = balance
1207 | }
1208 | }
1209 |
1210 | class Paypal extends Account {
1211 | constructor(balance) {
1212 | super()
1213 | this.name = 'Paypal'
1214 | this.balance = balance
1215 | }
1216 | }
1217 |
1218 | class Bitcoin extends Account {
1219 | constructor(balance) {
1220 | super()
1221 | this.name = 'bitcoin'
1222 | this.balance = balance
1223 | }
1224 | }
1225 | ```
1226 |
1227 | Now let's prepare the chain using the links defined above (i.e. Bank, Paypal, Bitcoin)
1228 |
1229 | ```js
1230 | // Let's prepare a chain like below
1231 | // bank.paypal.bitcoin
1232 | //
1233 | // First priority bank
1234 | // If bank can't pay then paypal
1235 | // If paypal can't pay then bit coin
1236 |
1237 | const bank = new Bank(100) // Bank with balance 100
1238 | const paypal = new Paypal(200) // Paypal with balance 200
1239 | const bitcoin = new Bitcoin(300) // Bitcoin with balance 300
1240 |
1241 | bank.setNext(paypal)
1242 | paypal.setNext(bitcoin)
1243 |
1244 | // Let's try to pay using the first priority i.e. bank
1245 | bank.pay(259)
1246 |
1247 | // Output will be
1248 | // ==============
1249 | // Cannot pay using bank. Proceeding ..
1250 | // Cannot pay using paypal. Proceeding ..:
1251 | // Paid 259 using Bitcoin!
1252 | ```
1253 |
1254 |
1257 |
1258 | ## # Command Design Pattern
1259 |
1260 | Real world example
1261 | > A generic example would be you ordering a food at restaurant. You (i.e. `Client`) ask the waiter (i.e. `Invoker`) to bring some food (i.e. `Command`) and waiter simply forwards the request to Chef (i.e. `Receiver`) who has the knowledge of what and how to cook.
1262 | > Another example would be you (i.e. `Client`) switching on (i.e. `Command`) the television (i.e. `Receiver`) using a remote control (`Invoker`).
1263 |
1264 | In plain words
1265 | > Allows you to encapsulate actions in objects. The key idea behind this pattern is to provide the means to decouple client from receiver.
1266 |
1267 | Wikipedia says
1268 | > In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time. This information includes the method name, the object that owns the method and values for the method parameters.
1269 |
1270 | **Example:**
1271 |
1272 | First of all we have the receiver that has the implementation of every action that could be performed
1273 | ```js
1274 | // Receiver
1275 | class Bulb {
1276 | turnOn() {
1277 | console.log('Bulb has been lit')
1278 | }
1279 |
1280 | turnOff() {
1281 | console.log('Darkness!')
1282 | }
1283 | }
1284 | ```
1285 | then we have an interface that each of the commands are going to implement and then we have a set of commands
1286 | ```js
1287 | /*
1288 | Command interface :
1289 |
1290 | execute()
1291 | undo()
1292 | redo()
1293 | */
1294 |
1295 | // Command
1296 | class TurnOnCommand {
1297 | constructor(bulb) {
1298 | this.bulb = bulb
1299 | }
1300 |
1301 | execute() {
1302 | this.bulb.turnOn()
1303 | }
1304 |
1305 | undo() {
1306 | this.bulb.turnOff()
1307 | }
1308 |
1309 | redo() {
1310 | this.execute()
1311 | }
1312 | }
1313 |
1314 | class TurnOffCommand {
1315 | constructor(bulb) {
1316 | this.bulb = bulb
1317 | }
1318 |
1319 | execute() {
1320 | this.bulb.turnOff()
1321 | }
1322 |
1323 | undo() {
1324 | this.bulb.turnOn()
1325 | }
1326 |
1327 | redo() {
1328 | this.execute()
1329 | }
1330 | }
1331 | ```
1332 | Then we have an `Invoker` with whom the client will interact to process any commands
1333 | ```js
1334 | // Invoker
1335 | class RemoteControl {
1336 | submit(command) {
1337 | command.execute()
1338 | }
1339 | }
1340 | ```
1341 |
1342 | Finally let's see how we can use it in our client
1343 |
1344 | ```js
1345 | const bulb = new Bulb()
1346 |
1347 | const turnOn = new TurnOnCommand(bulb)
1348 | const turnOff = new TurnOffCommand(bulb)
1349 |
1350 | const remote = new RemoteControl()
1351 | remote.submit(turnOn) // Bulb has been lit!
1352 | remote.submit(turnOff) // Darkness!
1353 | ```
1354 |
1355 | Command pattern can also be used to implement a transaction based system. Where you keep maintaining the history of commands as soon as you execute them. If the final command is successfully executed, all good otherwise just iterate through the history and keep executing the `undo` on all the executed commands.
1356 |
1357 |
1360 |
1361 | ## # Iterator Design Pattern
1362 |
1363 | Real world example
1364 | > An old radio set will be a good example of iterator, where user could start at some channel and then use next or previous buttons to go through the respective channels. Or take an example of MP3 player or a TV set where you could press the next and previous buttons to go through the consecutive channels or in other words they all provide an interface to iterate through the respective channels, songs or radio stations.
1365 |
1366 | In plain words
1367 | > It presents a way to access the elements of an object without exposing the underlying presentation.
1368 |
1369 | Wikipedia says
1370 | > In object-oriented programming, the iterator pattern is a design pattern in which an iterator is used to traverse a container and access the container's elements. The iterator pattern decouples algorithms from containers in some cases, algorithms are necessarily container-specific and thus cannot be decoupled.
1371 |
1372 | **Example:**
1373 | Translating our radio stations example from above. First of all we have `RadioStation`
1374 |
1375 | ```js
1376 | class RadioStation {
1377 | constructor(frequency) {
1378 | this.frequency = frequency
1379 | }
1380 |
1381 | getFrequency() {
1382 | return this.frequency
1383 | }
1384 | }
1385 | ```
1386 | Then we have our iterator
1387 |
1388 | ```js
1389 | class StationList {
1390 | constructor(){
1391 | this.stations = []
1392 | }
1393 |
1394 | addStation(station) {
1395 | this.stations.push(station)
1396 | }
1397 |
1398 | removeStation(toRemove) {
1399 | const toRemoveFrequency = toRemove.getFrequency()
1400 | this.stations = this.stations.filter(station => {
1401 | return station.getFrequency() !== toRemoveFrequency
1402 | })
1403 | }
1404 | }
1405 | ```
1406 | And then it can be used as
1407 | ```js
1408 | const stationList = new StationList()
1409 |
1410 | stationList.addStation(new RadioStation(89))
1411 | stationList.addStation(new RadioStation(101))
1412 | stationList.addStation(new RadioStation(102))
1413 | stationList.addStation(new RadioStation(103.2))
1414 |
1415 | stationList.stations.forEach(station => console.log(station.getFrequency()))
1416 |
1417 | stationList.removeStation(new RadioStation(89)) // Will remove station 89
1418 | ```
1419 |
1420 |
1423 |
1424 | ## # Mediator Design Pattern
1425 |
1426 | Real world example
1427 | > A general example would be when you talk to someone on your mobile phone, there is a network provider sitting between you and them and your conversation goes through it instead of being directly sent. In this case network provider is mediator.
1428 |
1429 | In plain words
1430 | > Mediator pattern adds a third party object (called mediator) to control the interaction between two objects (called colleagues). It helps reduce the coupling between the classes communicating with each other. Because now they don't need to have the knowledge of each other's implementation.
1431 |
1432 | Wikipedia says
1433 | > In software engineering, the mediator pattern defines an object that encapsulates how a set of objects interact. This pattern is considered to be a behavioral pattern due to the way it can alter the program's running behavior.
1434 |
1435 | **Example:**
1436 |
1437 | Here is the simplest example of a chat room (i.e. mediator) with users (i.e. colleagues) sending messages to each other.
1438 |
1439 | First of all, we have the mediator i.e. the chat room
1440 |
1441 | ```js
1442 | // Mediator
1443 | class ChatRoom {
1444 | showMessage(user, message) {
1445 | const time = new Date()
1446 | const sender = user.getName()
1447 |
1448 | console.log(time + '[' + sender + ']:' + message)
1449 | }
1450 | }
1451 | ```
1452 |
1453 | Then we have our users i.e. colleagues
1454 | ```js
1455 | class User {
1456 | constructor(name, chatMediator) {
1457 | this.name = name
1458 | this.chatMediator = chatMediator
1459 | }
1460 |
1461 | getName() {
1462 | return this.name
1463 | }
1464 |
1465 | send(message) {
1466 | this.chatMediator.showMessage(this, message)
1467 | }
1468 | }
1469 | ```
1470 |
1471 | And the usage
1472 |
1473 | ```js
1474 | const mediator = new ChatRoom()
1475 |
1476 | const john = new User('John Doe', mediator)
1477 | const jane = new User('Jane Doe', mediator)
1478 |
1479 | john.send('Hi there!')
1480 | jane.send('Hey!')
1481 |
1482 | // Output will be
1483 | // Feb 14, 10:58 [John]: Hi there!
1484 | // Feb 14, 10:58 [Jane]: Hey!
1485 | ```
1486 |
1487 |
1490 |
1491 | ## # Memento Design Pattern
1492 |
1493 | Real world example
1494 | > Take the example of calculator (i.e. originator), where whenever you perform some calculation the last calculation is saved in memory (i.e. memento) so that you can get back to it and maybe get it restored using some action buttons (i.e. caretaker).
1495 |
1496 | In plain words
1497 | > Memento pattern is about capturing and storing the current state of an object in a manner that it can be restored later on in a smooth manner.
1498 |
1499 | Wikipedia says
1500 | > The memento pattern is a software design pattern that provides the ability to restore an object to its previous state (undo via rollback).
1501 |
1502 | Usually useful when you need to provide some sort of undo functionality.
1503 |
1504 | **Example:**
1505 |
1506 | Lets take an example of text editor which keeps saving the state from time to time and that you can restore if you want.
1507 |
1508 | First of all we have our memento object that will be able to hold the editor state
1509 |
1510 | ```js
1511 | class EditorMemento {
1512 | constructor(content) {
1513 | this._content = content
1514 | }
1515 |
1516 | getContent() {
1517 | return this._content
1518 | }
1519 | }
1520 | ```
1521 |
1522 | Then we have our editor i.e. originator that is going to use memento object
1523 |
1524 | ```js
1525 | class Editor {
1526 | constructor(){
1527 | this._content = ''
1528 | }
1529 |
1530 | type(words) {
1531 | this._content = this._content + ' ' + words
1532 | }
1533 |
1534 | getContent() {
1535 | return this._content
1536 | }
1537 |
1538 | save() {
1539 | return new EditorMemento(this._content)
1540 | }
1541 |
1542 | restore(memento) {
1543 | this._content = memento.getContent()
1544 | }
1545 | }
1546 | ```
1547 |
1548 | And then it can be used as
1549 |
1550 | ```js
1551 | const editor = new Editor()
1552 |
1553 | // Type some stuff
1554 | editor.type('This is the first sentence.')
1555 | editor.type('This is second.')
1556 |
1557 | // Save the state to restore to : This is the first sentence. This is second.
1558 | const saved = editor.save()
1559 |
1560 | // Type some more
1561 | editor.type('And this is third.')
1562 |
1563 | // Output: Content before Saving
1564 | console.log(editor.getContent())// This is the first sentence. This is second. And this is third.
1565 |
1566 | // Restoring to last saved state
1567 | editor.restore(saved)
1568 |
1569 | console.log(editor.getContent()) // This is the first sentence. This is second.
1570 | ```
1571 |
1572 |
1575 |
1576 | ## # Observer Design Pattern
1577 |
1578 | (Otherwise known as _"pub-sub"_)
1579 |
1580 | Real world example
1581 | > A good example would be the job seekers where they subscribe to some job posting site and they are notified whenever there is a matching job opportunity.
1582 |
1583 | In plain words
1584 | > Defines a dependency between objects so that whenever an object changes its state, all its dependents are notified.
1585 |
1586 | Wikipedia says
1587 | > The observer pattern is a software design pattern in which an object, called the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.
1588 |
1589 | **Example:**
1590 |
1591 | Translating our example from above. First of all we have job seekers that need to be notified for a job posting
1592 | ```js
1593 | const JobPost = title => ({
1594 | title: title
1595 | })
1596 |
1597 | class JobSeeker {
1598 | constructor(name) {
1599 | this._name = name
1600 | }
1601 |
1602 | notify(jobPost) {
1603 | console.log(this._name, 'has been notified of a new posting :', jobPost.title)
1604 | }
1605 | }
1606 | ```
1607 | Then we have our job postings to which the job seekers will subscribe
1608 | ```js
1609 | class JobBoard {
1610 | constructor() {
1611 | this._subscribers = []
1612 | }
1613 |
1614 | subscribe(jobSeeker) {
1615 | this._subscribers.push(jobSeeker)
1616 | }
1617 |
1618 | addJob(jobPosting) {
1619 | this._subscribers.forEach(subscriber => {
1620 | subscriber.notify(jobPosting)
1621 | })
1622 | }
1623 | }
1624 | ```
1625 | Then it can be used as
1626 | ```js
1627 | // Create subscribers
1628 | const jonDoe = new JobSeeker('John Doe')
1629 | const janeDoe = new JobSeeker('Jane Doe')
1630 | const kaneDoe = new JobSeeker('Kane Doe')
1631 |
1632 | // Create publisher and attach subscribers
1633 | const jobBoard = new JobBoard()
1634 | jobBoard.subscribe(jonDoe)
1635 | jobBoard.subscribe(janeDoe)
1636 |
1637 | // Add a new job and see if subscribers get notified
1638 | jobBoard.addJob(JobPost('Software Engineer'))
1639 |
1640 | // Output
1641 | // John Doe has been notified of a new posting : Software Engineer
1642 | // Jane Doe has been notified of a new posting : Software Engineer
1643 | ```
1644 |
1645 |
1648 |
1649 | ## # Visitor Design Pattern
1650 |
1651 | Real world example
1652 | > Consider someone visiting Dubai. They just need a way (i.e. visa) to enter Dubai. After arrival, they can come and visit any place in Dubai on their own without having to ask for permission or to do some leg work in order to visit any place here just let them know of a place and they can visit it. Visitor pattern let's you do just that, it helps you add places to visit so that they can visit as much as they can without having to do any legwork.
1653 |
1654 | In plain words
1655 | > Visitor pattern let's you add further operations to objects without having to modify them.
1656 |
1657 | Wikipedia says
1658 | > In object-oriented programming and software engineering, the visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures. It is one way to follow the open/closed principle.
1659 |
1660 | **Example:**
1661 |
1662 | Let's take an example of a zoo simulation where we have several different kinds of animals and we have to make them Sound. Let's translate this using visitor pattern
1663 |
1664 | We have our implementations for the animals
1665 | ```js
1666 | class Monkey {
1667 | shout() {
1668 | console.log('Ooh oo aa aa!')
1669 | }
1670 |
1671 | accept(operation) {
1672 | operation.visitMonkey(this)
1673 | }
1674 | }
1675 |
1676 | class Lion {
1677 | roar() {
1678 | console.log('Roaaar!')
1679 | }
1680 |
1681 | accept(operation) {
1682 | operation.visitLion(this)
1683 | }
1684 | }
1685 |
1686 | class Dolphin {
1687 | speak() {
1688 | console.log('Tuut tuttu tuutt!')
1689 | }
1690 |
1691 | accept(operation) {
1692 | operation.visitDolphin(this)
1693 | }
1694 | }
1695 | ```
1696 | Let's implement our visitor
1697 | ```js
1698 | const speak = {
1699 | visitMonkey(monkey){
1700 | monkey.shout()
1701 | },
1702 | visitLion(lion){
1703 | lion.roar()
1704 | },
1705 | visitDolphin(dolphin){
1706 | dolphin.speak()
1707 | }
1708 | }
1709 | ```
1710 |
1711 | And then it can be used as
1712 | ```js
1713 | const monkey = new Monkey()
1714 | const lion = new Lion()
1715 | const dolphin = new Dolphin()
1716 |
1717 | monkey.accept(speak) // Ooh oo aa aa!
1718 | lion.accept(speak) // Roaaar!
1719 | dolphin.accept(speak) // Tuut tutt tuutt!
1720 | ```
1721 | We could have done this simply by having a inheritance hierarchy for the animals but then we would have to modify the animals whenever we would have to add new actions to animals. But now we will not have to change them. For example, let's say we are asked to add the jump behavior to the animals, we can simply add that by creating a new visitor i.e.
1722 |
1723 | ```js
1724 | const jump = {
1725 | visitMonkey(monkey) {
1726 | console.log('Jumped 20 feet high! on to the tree!')
1727 | },
1728 | visitLion(lion) {
1729 | console.log('Jumped 7 feet! Back on the ground!')
1730 | },
1731 | visitDolphin(dolphin) {
1732 | console.log('Walked on water a little and disappeared')
1733 | }
1734 | }
1735 | ```
1736 | And for the usage
1737 | ```js
1738 | monkey.accept(speak) // Ooh oo aa aa!
1739 | monkey.accept(jump) // Jumped 20 feet high! on to the tree!
1740 |
1741 | lion.accept(speak) // Roaaar!
1742 | lion.accept(jump) // Jumped 7 feet! Back on the ground!
1743 |
1744 | dolphin.accept(speak) // Tuut tutt tuutt!
1745 | dolphin.accept(jump) // Walked on water a little and disappeared
1746 | ```
1747 |
1748 |
1751 |
1752 | ## # Strategy Design Pattern
1753 |
1754 | Real world example
1755 | > Consider the example of sorting, we implemented bubble sort but the data started to grow and bubble sort started getting very slow. In order to tackle this we implemented Quick sort. But now although the quick sort algorithm was doing better for large datasets, it was very slow for smaller datasets. In order to handle this we implemented a strategy where for small datasets, bubble sort will be used and for larger, quick sort.
1756 |
1757 | In plain words
1758 | > Strategy pattern allows you to switch the algorithm or strategy based upon the situation.
1759 |
1760 | Wikipedia says
1761 | > In computer programming, the strategy pattern (also known as the policy pattern) is a behavioural software design pattern that enables an algorithm's behavior to be selected at runtime.
1762 |
1763 | **Example:**
1764 |
1765 | Translating our example from above, we can easily implement this strategy in javascript using its feature of first class functions.
1766 |
1767 | ```js
1768 | const bubbleSort = dataset => {
1769 | console.log('Sorting with bubble sort')
1770 | // ...
1771 | // ...
1772 | return dataset
1773 | }
1774 |
1775 | const quickSort = dataset => {
1776 | console.log('Sorting with quick sort')
1777 | // ...
1778 | // ...
1779 | return dataset
1780 | }
1781 | ```
1782 |
1783 | And then we have our client that is going to use any strategy
1784 | ```js
1785 | const sorter = dataset => {
1786 | if(dataset.length > 5){
1787 | return quickSort
1788 | } else {
1789 | return bubbleSort
1790 | }
1791 | }
1792 | ```
1793 | And it can be used as
1794 | ```js
1795 | const longDataSet = [1, 5, 4, 3, 2, 8]
1796 | const shortDataSet = [1, 5, 4]
1797 |
1798 | const sorter1 = sorter(longDataSet)
1799 | const sorter2 = sorter(shortDataSet)
1800 |
1801 | sorter1(longDataSet) // Output : Sorting with quick sort
1802 | sorter2(shortDataSet) // Output : Sorting with bubble sort
1803 | ```
1804 |
1805 |
1808 |
1809 | ## # State Design Pattern
1810 |
1811 | Real world example
1812 | > Imagine you are using some drawing application, you choose the paint brush to draw. Now the brush changes it's behavior based on the selected color i.e. if you have chosen red color it will draw in red, if blue then it will be in blue etc.
1813 |
1814 | In plain words
1815 | > It lets you change the behavior of a class when the state changes.
1816 |
1817 | Wikipedia says
1818 | > The state pattern is a behavioral software design pattern that implements a state machine in an object-oriented way. With the state pattern, a state machine is implemented by implementing each individual state as a derived class of the state pattern interface, and implementing state transitions by invoking methods defined by the pattern's superclass.
1819 | > The state pattern can be interpreted as a strategy pattern which is able to switch the current strategy through invocations of methods defined in the pattern's interface
1820 |
1821 | **Example:**
1822 |
1823 | Let's take an example of text editor, it let's you change the state of text that is typed i.e. if you have selected bold, it starts writing in bold, if italic then in italics etc.
1824 |
1825 | First of all we have our transformation functions
1826 |
1827 | ```js
1828 | const upperCase = inputString => inputString.toUpperCase()
1829 | const lowerCase = inputString => inputString.toLowerCase()
1830 | const defaultTransform = inputString => inputString
1831 | ```
1832 | Then we have our editor
1833 | ```js
1834 | class TextEditor {
1835 | constructor(transform) {
1836 | this._transform = transform
1837 | }
1838 |
1839 | setTransform(transform) {
1840 | this._transform = transform
1841 | }
1842 |
1843 | type(words) {
1844 | console.log(this._transform(words))
1845 | }
1846 | }
1847 | ```
1848 | And then it can be used as
1849 | ```js
1850 | const editor = new TextEditor(defaultTransform)
1851 |
1852 | editor.type('First line')
1853 |
1854 | editor.setTransform(upperCase)
1855 |
1856 | editor.type('Second line')
1857 | editor.type('Third line')
1858 |
1859 | editor.setTransform(lowerCase)
1860 |
1861 | editor.type('Fourth line')
1862 | editor.type('Fifth line')
1863 |
1864 | // Output:
1865 | // First line
1866 | // SECOND LINE
1867 | // THIRD LINE
1868 | // fourth line
1869 | // fifth line
1870 | ```
1871 |
1872 |
1875 |
1876 | ## # Template Method Design Pattern
1877 |
1878 | Real world example
1879 |
1880 | > Suppose we are getting some house built. The steps for building might look like
1881 | > Prepare the base of house
1882 | > Build the walls
1883 | > Add roof
1884 | > Add other floors
1885 | > The order of these steps could never be changed i.e. you can't build the roof before building the walls etc but each of the steps could be modified for example walls can be made of wood or polyester or stone.
1886 |
1887 | In plain words
1888 | > Template method defines the skeleton of how certain algorithm could be performed but defers the implementation of those steps to the children classes.
1889 |
1890 | Wikipedia says
1891 | > In software engineering, the template method pattern is a behavioral design pattern that defines the program skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets one redefine certain steps of an algorithm without changing the algorithm's structure.
1892 |
1893 | **Example:**
1894 |
1895 | Imagine we have a build tool that helps us test, lint, build, generate build reports (i.e. code coverage reports, linting report etc) and deploy our app on the test server.
1896 |
1897 | First of all we have our base class that specifies the skeleton for the build algorithm
1898 |
1899 | ```js
1900 | class Builder {
1901 | // Template method
1902 | build() {
1903 | this.test()
1904 | this.lint()
1905 | this.assemble()
1906 | this.deploy()
1907 | }
1908 | }
1909 | ```
1910 |
1911 | Then we can have our implementations
1912 |
1913 | ```js
1914 | class AndroidBuilder extends Builder {
1915 | test() {
1916 | console.log("Running android tests");
1917 | }
1918 |
1919 | lint() {
1920 | console.log("Linting the android code");
1921 | }
1922 |
1923 | assemble() {
1924 | console.log("Assembling the android build");
1925 | }
1926 |
1927 | deploy() {
1928 | console.log("Deploying android build to server");
1929 | }
1930 | }
1931 |
1932 | class IosBuilder extends Builder {
1933 | test() {
1934 | console.log("Running ios tests");
1935 | }
1936 |
1937 | lint() {
1938 | console.log("Linting the ios code");
1939 | }
1940 |
1941 | assemble() {
1942 | console.log("Assembling the ios build");
1943 | }
1944 |
1945 | deploy() {
1946 | console.log("Deploying ios build to server");
1947 | }
1948 | }
1949 |
1950 | ```
1951 |
1952 | And then it can be used as
1953 |
1954 | ```js
1955 | const androidBuilder = new AndroidBuilder()
1956 | androidBuilder.build()
1957 |
1958 | // Output:
1959 | // Running android tests
1960 | // Linting the android code
1961 | // Assembling the android build
1962 | // Deploying android build to server
1963 |
1964 | const iosBuilder = new IosBuilder()
1965 | iosBuilder.build()
1966 |
1967 | // Output:
1968 | // Running ios tests
1969 | // Linting the ios code
1970 | // Assembling the ios build
1971 | // Deploying ios build to server
1972 | ```
1973 |
1974 |
1977 |
--------------------------------------------------------------------------------