├── .gitignore ├── abstract-factory-pattern ├── README.md ├── example1 │ ├── abstract-factory.ts │ ├── abstract-productA.ts │ ├── abstract-productB.ts │ ├── concrete-factory1.ts │ ├── concrete-factory2.ts │ ├── concrete-productA1.ts │ ├── concrete-productA2.ts │ ├── concrete-productB1.ts │ ├── concrete-productB2.ts │ └── index.ts ├── example2 │ ├── abstract-factory.ts │ ├── armor │ │ ├── armor-interface.ts │ │ ├── body-armor.model.ts │ │ ├── cloak.model.ts │ │ └── orc-armor.model.ts │ ├── index.ts │ ├── mage-factory.ts │ ├── orc-factory.ts │ ├── warrior-factory.ts │ └── weapons │ │ ├── axe.model.ts │ │ ├── mage-fireball.model.ts │ │ ├── sword.model.ts │ │ └── weapon.interface.ts ├── nodemon-example1.json ├── nodemon-example2.json ├── package-lock.json └── package.json ├── adapter-pattern ├── README.md ├── example1 │ ├── index.ts │ ├── interfaces │ │ └── warrior.interface.ts │ └── models │ │ ├── android-adapter.model.ts │ │ ├── android.model.ts │ │ ├── namekian.ts │ │ └── saiyan.model.ts ├── example2 │ ├── index.ts │ ├── interfaces │ │ └── pure-race.interface.ts │ └── models │ │ ├── human-adapter.model.ts │ │ ├── human.model.ts │ │ ├── namekian-adapter.model.ts │ │ ├── namekian.ts │ │ ├── saiyan-adapter.model.ts │ │ └── saiyan.model.ts ├── nodemon-example1.json ├── nodemon-example2.json ├── package-lock.json └── package.json ├── bridge-pattern ├── example1 │ ├── abstraction.ts │ ├── concrete-implementorA.ts │ ├── concrete-implementorB.ts │ ├── implementor.ts │ ├── index.ts │ ├── refine-abstraction.ts │ └── uml.puml ├── example2 │ ├── problem │ │ ├── desktop-messaging.ts │ │ ├── index.ts │ │ ├── mobile-messaging.ts │ │ └── uml.puml │ └── solution │ │ ├── advanced-messaging.ts │ │ ├── desktop-sender.ts │ │ ├── index.ts │ │ ├── message-sender.ts │ │ ├── messaging-sender.ts │ │ ├── mobile-sender.ts │ │ └── uml.puml ├── nodemon-example1.json ├── nodemon-example2-problem.json ├── nodemon-example2-solve.json ├── package-lock.json ├── package.json └── post.md ├── builder-pattern ├── README.md ├── example1 │ ├── builder.interface.ts │ ├── concrete-builder1.ts │ ├── director.ts │ ├── index.ts │ └── product.ts ├── example2 │ ├── hero-builder.ts │ ├── hero-director.ts │ ├── hero.model.ts │ ├── human-hero-builder.ts │ ├── index.ts │ └── orc-hero-builder.ts ├── example3 │ ├── buger-director.ts │ ├── burger-builder.ts │ ├── burger-type.interface.ts │ ├── burger.model.ts │ └── index.ts ├── example4 │ ├── data-webpages.ts │ ├── google-sitemap.builder.ts │ ├── html-sitemap.builder.ts │ ├── index.ts │ ├── sitemap-director.ts │ ├── sitemap.builder.ts │ └── webpage.model.ts ├── nodemon-example1.json ├── nodemon-example2.json ├── nodemon-example3.json ├── nodemon-example4.json ├── package-lock.json └── package.json ├── chain-responsibility-pattern ├── README.md ├── example1 │ ├── abstract-handler.ts │ ├── handler.ts │ ├── handlerA.ts │ ├── handlerB.ts │ ├── handlerC.ts │ └── index.ts ├── example2 │ ├── app.constants.ts │ ├── index.ts │ ├── interfaces │ │ ├── handler.ts │ │ ├── index.ts │ │ └── user.interface.ts │ ├── middlewares │ │ ├── index.ts │ │ ├── middleware.ts │ │ ├── role.middleware.ts │ │ ├── throttling.middleware.ts │ │ └── user-exists.middleware.ts │ └── server.ts ├── nodemon-example1.json ├── nodemon-example2.json ├── package-lock.json ├── package.json └── tsconfig.json ├── command-pattern ├── README.md ├── example1 │ ├── command-solution-1 │ │ ├── agent.class.ts │ │ ├── buy-stock-order.ts │ │ ├── index.ts │ │ ├── order.class.ts │ │ ├── sell-stock-order.ts │ │ └── stock.trader.class.ts │ └── problem │ │ ├── agent.class.ts │ │ ├── index.ts │ │ └── stock.trader.class.ts ├── example2 │ └── command-solution-1 │ │ ├── commands │ │ ├── clean-command.ts │ │ ├── command.class.ts │ │ ├── index.ts │ │ ├── move-command.ts │ │ └── save-secret-command.ts │ │ ├── index.ts │ │ ├── r2d2.ts │ │ └── services │ │ ├── index.ts │ │ ├── r2d2.service.ts │ │ └── store.service.ts ├── nodemon-example1-command-solution-1.json ├── nodemon-example1-problem.json ├── nodemon-example2-command-solution-1.json ├── package-lock.json └── package.json ├── composite-pattern ├── example1 │ ├── component.ts │ ├── composite.ts │ ├── index.ts │ ├── leaf.ts │ └── uml.puml ├── example2 │ ├── problem │ │ ├── index.ts │ │ ├── ingredient.ts │ │ ├── recipe.ts │ │ └── uml.puml │ └── solution │ │ ├── index.ts │ │ ├── ingredient.ts │ │ ├── recipe-component.ts │ │ ├── recipe.ts │ │ └── uml.puml ├── nodemon-example1.json ├── nodemon-example2-problem.json ├── nodemon-example2-solve.json ├── package-lock.json ├── package.json └── post.md ├── decorator-pattern ├── README.md ├── example1 │ ├── decorator-solution-1 │ │ ├── component.interface.ts │ │ ├── concrete-component.ts │ │ ├── concrete-decorator-a.ts │ │ ├── concrete-decorator-b.ts │ │ ├── concrete-decorator-c.ts │ │ ├── decorator.ts │ │ └── index.ts │ └── problem │ │ ├── component-a.ts │ │ ├── component-b.ts │ │ ├── component-ba.ts │ │ ├── component-base.ts │ │ ├── component-c.ts │ │ ├── component-ca.ts │ │ ├── component-cba.ts │ │ ├── component.interface.ts │ │ └── index.ts ├── example2 │ └── decorator-solution │ │ ├── character-decorator.ts │ │ ├── character.interface.ts │ │ ├── index.ts │ │ ├── lord-decorator.ts │ │ ├── simple-character.ts │ │ └── white-walker-decorator.ts ├── nodemon-example1-decorator-solution-1.json ├── nodemon-example1-problem.json ├── nodemon-example2-decorator-solution-1.json ├── package-lock.json ├── package.json ├── render1560346139406.gif └── tsconfig.json ├── demeter ├── example1 │ ├── index.ts │ ├── interfaces │ │ ├── address.interface.ts │ │ ├── fake-object.ts │ │ ├── house.interface.ts │ │ └── person.interface.ts │ └── models │ │ ├── address.model.ts │ │ ├── house.model.ts │ │ └── person.model.ts ├── nodemon-example1.json ├── package-lock.json └── package.json ├── facade-pattern ├── README.md ├── example1 │ ├── facade-solution-1 │ │ ├── facade.ts │ │ ├── index.ts │ │ ├── system1 │ │ │ ├── classA.ts │ │ │ ├── classA2.ts │ │ │ └── classB.ts │ │ └── system2 │ │ │ ├── classC.ts │ │ │ ├── classC2.ts │ │ │ ├── classC3.ts │ │ │ └── classD.ts │ ├── facade-solution-2 │ │ ├── facade.ts │ │ ├── index.ts │ │ ├── system1 │ │ │ ├── classA.ts │ │ │ ├── classA2.ts │ │ │ ├── classB.ts │ │ │ └── facade-system1.ts │ │ └── system2 │ │ │ ├── classC.ts │ │ │ ├── classC2.ts │ │ │ ├── classC3.ts │ │ │ ├── classD.ts │ │ │ └── facade-system2.ts │ └── problem │ │ ├── index.ts │ │ ├── system1 │ │ ├── classA.ts │ │ ├── classA2.ts │ │ └── classB.ts │ │ └── system2 │ │ ├── classC.ts │ │ ├── classC2.ts │ │ ├── classC3.ts │ │ └── classD.ts ├── example2 │ ├── facade-solution │ │ ├── dragonball │ │ │ ├── dragonball-facade.ts │ │ │ ├── interfaces │ │ │ │ └── pure-race.interface.ts │ │ │ └── models │ │ │ │ ├── human-adapter.model.ts │ │ │ │ ├── human.model.ts │ │ │ │ ├── namekian-adapter.model.ts │ │ │ │ ├── namekian.model.ts │ │ │ │ ├── saiyan-adapter.model.ts │ │ │ │ └── saiyan.model.ts │ │ ├── index.ts │ │ └── pokemon │ │ │ ├── fighting-pokemon.model.ts │ │ │ ├── ground-pokemon.model.ts │ │ │ ├── poison-pokemon.model.ts │ │ │ ├── pokemon-facade.ts │ │ │ └── pokemon.model.ts │ └── problem │ │ ├── dragonball │ │ ├── interfaces │ │ │ └── pure-race.interface.ts │ │ └── models │ │ │ ├── human-adapter.model.ts │ │ │ ├── human.model.ts │ │ │ ├── namekian-adapter.model.ts │ │ │ ├── namekian.ts │ │ │ ├── saiyan-adapter.model.ts │ │ │ └── saiyan.model.ts │ │ ├── index.ts │ │ └── pokemon │ │ ├── fighting-pokemon.model.ts │ │ ├── ground-pokemon.model.ts │ │ ├── poison-pokemon.model.ts │ │ └── pokemon.model.ts ├── nodemon-example1-facade-solution-1.json ├── nodemon-example1-facade-solution-2.json ├── nodemon-example1-problem.json ├── nodemon-example2-facade-solution-1.json ├── nodemon-example2-problem.json ├── package-lock.json └── package.json ├── factory-method-pattern ├── README.md ├── example1 │ ├── concrete-creator1.ts │ ├── concrete-creator2.ts │ ├── concrete-product1.ts │ ├── concrete-product2.ts │ ├── creator.ts │ ├── index.ts │ └── product.interface.ts ├── example2 │ ├── burger.model.ts │ ├── index.ts │ ├── kebab.model.ts │ ├── pizza.model.ts │ ├── product-manager.ts │ ├── product-type.enum.ts │ └── product.interface.ts ├── example3 │ ├── burger-creator.ts │ ├── burger.model.ts │ ├── creator.ts │ ├── index.ts │ ├── kebab-creator.ts │ ├── kebab.model.ts │ ├── pizza-creator.ts │ ├── pizza.model.ts │ └── product.interface.ts ├── nodemon-example1.json ├── nodemon-example2.json ├── nodemon-example3.json ├── nodemon-example4.json ├── package-lock.json └── package.json ├── flyweight-pattern ├── example1 │ ├── concrete-flyweight.ts │ ├── flyweight-factory.ts │ ├── flyweight.ts │ ├── index.ts │ ├── uml.puml │ └── unshared-flyweight.ts ├── example2 │ ├── problem │ │ ├── index.ts │ │ ├── interfaces │ │ │ ├── index.ts │ │ │ ├── position.interface.ts │ │ │ ├── stats.interface.ts │ │ │ └── team.interface.ts │ │ ├── models │ │ │ ├── index.ts │ │ │ ├── player.ts │ │ │ ├── position.ts │ │ │ ├── stats.ts │ │ │ └── team.ts │ │ ├── sports-simulator.ts │ │ └── uml.puml │ └── solution │ │ ├── flyweight.factory.ts │ │ ├── flyweight.ts │ │ ├── index.ts │ │ ├── interfaces │ │ ├── index.ts │ │ ├── position.interface.ts │ │ ├── stats.interface.ts │ │ └── team.interface.ts │ │ ├── models │ │ ├── index.ts │ │ ├── player.ts │ │ ├── position.ts │ │ ├── stats.ts │ │ └── team.ts │ │ ├── position-flyweight.factory.ts │ │ ├── position-flyweight.ts │ │ ├── sports-simulator.ts │ │ ├── team-flyweight.factory.ts │ │ ├── team-flyweight.ts │ │ └── uml.puml ├── nodemon-example1.json ├── nodemon-example2-problem.json ├── nodemon-example2-solve.json ├── package-lock.json ├── package.json └── tsconfig.json ├── iterator-pattern ├── README.md ├── example1 │ ├── iterator-solution-1 │ │ ├── agregator.interface.ts │ │ ├── alphabetical-order-iterator.ts │ │ ├── index.ts │ │ ├── iterator-solution1.puml │ │ ├── iterator.interface.ts │ │ ├── uml.puml │ │ └── words-collection.ts │ └── problem │ │ ├── example1.puml │ │ ├── index.ts │ │ ├── uml.puml │ │ └── words-collection.ts ├── example2 │ └── iterator-solution │ │ ├── dev-to │ │ ├── dev-to-iterator.ts │ │ └── dev-to.class.ts │ │ ├── index.ts │ │ ├── medium │ │ ├── medium-iterator.ts │ │ └── medium.class.ts │ │ ├── profile-iterator.interface.ts │ │ ├── profile.class.ts │ │ ├── social-network.interface.ts │ │ ├── social-spammer.ts │ │ └── uml.puml ├── nodemon-example1-iterator-solution-1.json ├── nodemon-example1-problem.json ├── nodemon-example2-iterator-solution-1.json ├── package-lock.json ├── package.json ├── render1560345403864.gif ├── render1560346139406.gif └── tsconfig.json ├── iterators-javascript ├── README.md ├── demo.yml ├── example1 │ ├── iterator-solution-1 │ │ ├── agregator.interface.ts │ │ ├── alphabetical-order-iterator.ts │ │ ├── index.ts │ │ ├── iterator.interface.ts │ │ └── words-collection.ts │ └── problem │ │ ├── index.ts │ │ └── words-collection.ts ├── example2 │ └── iterator-solution │ │ ├── dev-to │ │ ├── dev-to-iterator.ts │ │ └── dev-to.class.ts │ │ ├── index.ts │ │ ├── medium │ │ ├── medium-iterator.ts │ │ └── medium.class.ts │ │ ├── profile-iterator.interface.ts │ │ ├── profile.class.ts │ │ ├── social-network.interface.ts │ │ └── social-spammer.ts ├── nodemon-example1-iterator-solution-1.json ├── nodemon-example1-problem.json ├── nodemon-example2-iterator-solution-1.json ├── package-lock.json └── package.json ├── mediator-pattern ├── example1 │ ├── colleague.ts │ ├── colleague1.ts │ ├── colleague2.ts │ ├── concrete-mediator.ts │ ├── index.ts │ ├── mediator.ts │ └── uml.puml ├── example2 │ ├── problem │ │ ├── index.ts │ │ └── uml.puml │ └── solution │ │ ├── actuator.ts │ │ ├── concrete-iot-mediator.ts │ │ ├── index.ts │ │ ├── iot-device.ts │ │ ├── iot-mediator.ts │ │ ├── sensor.ts │ │ └── uml.puml ├── nodemon-example1.json ├── nodemon-example2-problem.json ├── nodemon-example2-solve.json ├── package-lock.json ├── package.json └── tsconfig.json ├── memoization ├── index.html └── index.js ├── null-object-pattern ├── README.md ├── example1 │ ├── null-object-solution-1 │ │ ├── index.ts │ │ ├── null-saiyan.class.ts │ │ ├── real-saiyan.class.ts │ │ ├── saiyan-factory.class.ts │ │ ├── saiyan.class.ts │ │ └── saiyan.interface.ts │ └── problem │ │ ├── index.ts │ │ ├── saiyan-factory.class.ts │ │ ├── saiyan.class.ts │ │ └── saiyan.interface.ts ├── nodemon-example1-problem.json ├── nodemon-example1-solution-1.json ├── package-lock.json └── package.json ├── observer-pattern ├── README.md ├── example1 │ ├── concrete-observerA.ts │ ├── concrete-observerB.ts │ ├── concrete-subject.ts │ ├── index.ts │ ├── observer.interface.ts │ └── subject.interface.ts ├── example2 │ ├── agent.interface.ts │ ├── auctioneer.interface.ts │ ├── concrete-agent.ts │ ├── concrete-auctioneerA.ts │ ├── concrete-auctioneerB.ts │ ├── concrete-auctioneerC.ts │ ├── concrete-auctioneerD.ts │ ├── index.ts │ └── product.model.ts ├── nodemon-example1.json ├── nodemon-example2.json ├── package-lock.json ├── package.json └── tsconfig.json ├── prototype-pattern ├── example1 │ ├── concrete-prototype2.ts │ ├── concrete.prototype1.ts │ ├── index.ts │ ├── prototype.ts │ └── uml.puml ├── example2 │ ├── problem │ │ ├── index.ts │ │ ├── uml.puml │ │ └── virtual-pet.ts │ └── solution │ │ ├── clonable.ts │ │ ├── index.ts │ │ ├── uml.puml │ │ └── virtual-pet.ts ├── nodemon-example1.json ├── nodemon-example2-problem.json ├── nodemon-example2-solve.json ├── package-lock.json ├── package.json └── tsconfig.json ├── proxy-pattern ├── example1 │ ├── index.ts │ └── uml.puml ├── example2 │ ├── index.ts │ ├── proxies │ │ ├── access.proxy.ts │ │ ├── cache.proxy.ts │ │ ├── index.ts │ │ ├── lazy.proxy.ts │ │ ├── logging.proxy.ts │ │ └── remote.proxy.ts │ ├── subjects │ │ ├── index.ts │ │ ├── real-subject-cache.ts │ │ ├── real-subject.ts │ │ ├── subject-cache.interface.ts │ │ └── subject.interface.ts │ └── uml.puml ├── nodemon-example1.json ├── nodemon-example2-problem.json ├── nodemon-example2-solve.json ├── package-lock.json ├── package.json └── tsconfig.json ├── refactoring-caesar ├── package-lock.json ├── package.json ├── refactoring-caesar-original.js ├── refactoring-caesar-step1.js ├── refactoring-caesar-step2.js ├── refactoring-caesar-step3.js ├── refactoring-caesar-step4.js ├── refactoring-caesar-step5.js ├── refactoring-caesar-step6.js ├── refactoring-caesar-step7.js └── refactoring-caesar-step8.js ├── singleton-pattern ├── README.md ├── example1 │ ├── problem │ │ ├── client1.ts │ │ ├── client2.ts │ │ ├── database-connection.ts │ │ ├── database-problem.plantuml │ │ └── index.ts │ └── singleton-solution-1 │ │ ├── client1.ts │ │ ├── client2.ts │ │ ├── database-connection.ts │ │ ├── database.plantuml │ │ └── index.ts ├── example2 │ ├── problem │ │ ├── client1.ts │ │ ├── client2.ts │ │ ├── database-connection.ts │ │ └── index.ts │ └── singleton-solution │ │ ├── batman.ts │ │ ├── client1.ts │ │ ├── client2.ts │ │ ├── hero-base.class.ts │ │ ├── hero.interface.ts │ │ ├── heroes.plantuml │ │ ├── index.ts │ │ └── spiderman.ts ├── nodemon-example1-problem.json ├── nodemon-example1-singleton-solution-1.json ├── nodemon-example2-problem.json ├── nodemon-example2-singleton-solution-1.json ├── package-lock.json └── package.json ├── state-pattern ├── README.md ├── example1 │ ├── problem │ │ ├── context.ts │ │ ├── index.ts │ │ ├── state.enum.ts │ │ └── uml.plantuml │ └── state-solution-1 │ │ ├── concrete-state-A.ts │ │ ├── concrete-state-B.ts │ │ ├── context.ts │ │ ├── index.ts │ │ ├── state.ts │ │ └── uml.plantuml ├── example2 │ ├── problem │ │ ├── freeza.ts │ │ ├── index.ts │ │ ├── state.enum.ts │ │ └── uml.plantuml │ └── solution │ │ ├── freeza.ts │ │ ├── index.ts │ │ ├── state.ts │ │ ├── states │ │ ├── golden-freezer.ts │ │ ├── transformation1.ts │ │ ├── transformation2.ts │ │ ├── transformation3.ts │ │ └── transformation4.ts │ │ ├── test.puml │ │ └── uml.plantuml ├── nodemon-example1-problem.json ├── nodemon-example1-state-solution-1.json ├── nodemon-example2-problem.json ├── nodemon-example2-state-solution-1.json ├── package-lock.json ├── package.json └── tsconfig.json ├── strategy-pattern ├── README.md ├── strategy1.html ├── strategy1.js ├── strategy2.html └── strategy2.js ├── template-method-pattern ├── README.md ├── package-lock.json ├── package.json ├── template-method0.js └── template-method1.js ├── understanding-this-in-js ├── 01-global-browser │ ├── global-browser.html │ └── global-browser.js ├── 02-global-nodejs │ └── global-nodejs.js ├── 03-keyword-new │ ├── new-01.js │ ├── new-02.js │ └── new-03.js ├── 04-invoker-object │ └── invoker-object.js ├── 05-call-apply │ └── call-apply.js ├── 06-bind │ └── bind.js ├── 07-fat-arrow │ ├── fat-arrow-bad.js │ ├── fat-arrow-call-apply.js │ └── fat-arrow.js ├── 08-class │ └── class.js ├── 09 - exercises │ └── exercise-01.js ├── README.md ├── package-lock.json └── package.json └── workshop-javascript ├── problems ├── Problem1 │ ├── index.html │ └── main.js ├── Problem2 │ ├── index.html │ └── main.js ├── Problem3 │ ├── controller │ │ └── rope.js │ ├── css │ │ ├── img │ │ │ ├── campo.jpeg │ │ │ └── rope.png │ │ └── rope.css │ └── rope.html └── Problem4 │ ├── balon.gif │ ├── field.png │ ├── football.js │ ├── index.html │ └── style.css └── solutions ├── Problem1 ├── index.html ├── main.js └── quadratic-equations.html ├── Problem2 ├── index.html └── main.js ├── Problem3 ├── controller │ └── rope.js ├── css │ ├── img │ │ ├── campo.jpeg │ │ └── rope.png │ └── rope.css └── rope.html └── Problem4 ├── balon.gif ├── field.png ├── football.js ├── index.html └── style.css /.gitignore: -------------------------------------------------------------------------------- 1 | adapter-pattern/node_modules/ 2 | abstract-factory-pattern/node_modules/ 3 | builder-pattern/node_modules/ 4 | command-pattern/node_modules 5 | computer-algorithms/node_modules/ 6 | chain-responsibility-pattern/node_modules/ 7 | decorator-pattern/node_modules/ 8 | demeter/node_modules/ 9 | factory-method-pattern/node_modules/ 10 | facade-pattern/node_modules/ 11 | iterator-pattern/node_modules/ 12 | iterators-javascript/node_modules/ 13 | null-object-pattern/node_modules/ 14 | observer-pattern/node_modules/ 15 | refactoring-caesar/node_modules/ 16 | template-method-pattern/node_modules/ 17 | understanding-this-in-js/node_modules/ 18 | -------------------------------------------------------------------------------- /abstract-factory-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Factory Method (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-factory-method/).** 4 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/abstract-factory.ts: -------------------------------------------------------------------------------- 1 | import { AbstractProductA } from "./abstract-productA"; 2 | import { AbstractProductB } from "./abstract-productB"; 3 | 4 | export interface AbstractFactory { 5 | createProductA(): AbstractProductA; 6 | createProductB(): AbstractProductB; 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/abstract-productA.ts: -------------------------------------------------------------------------------- 1 | export interface AbstractProductA { 2 | usefulFunctionA(): string; 3 | } 4 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/abstract-productB.ts: -------------------------------------------------------------------------------- 1 | export interface AbstractProductB { 2 | usefulFunctionB(): string; 3 | } 4 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/concrete-factory1.ts: -------------------------------------------------------------------------------- 1 | import { AbstractFactory } from "./abstract-factory"; 2 | import { AbstractProductA } from "./abstract-productA"; 3 | import { AbstractProductB } from "./abstract-productB"; 4 | import { ConcreteProductA1 } from "./concrete-productA1"; 5 | import { ConcreteProductB1 } from "./concrete-productB1"; 6 | 7 | export class ConcreteFactory1 implements AbstractFactory { 8 | public createProductA(): AbstractProductA { 9 | return new ConcreteProductA1(); 10 | } 11 | 12 | public createProductB(): AbstractProductB { 13 | return new ConcreteProductB1(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/concrete-factory2.ts: -------------------------------------------------------------------------------- 1 | import { AbstractFactory } from "./abstract-factory"; 2 | import { AbstractProductA } from "./abstract-productA"; 3 | import { AbstractProductB } from "./abstract-productB"; 4 | import { ConcreteProductA2 } from "./concrete-productA2"; 5 | import { ConcreteProductB2 } from "./concrete-productB2"; 6 | 7 | export class ConcreteFactory2 implements AbstractFactory { 8 | public createProductA(): AbstractProductA { 9 | return new ConcreteProductA2(); 10 | } 11 | 12 | public createProductB(): AbstractProductB { 13 | return new ConcreteProductB2(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/concrete-productA1.ts: -------------------------------------------------------------------------------- 1 | import { AbstractProductA } from "./abstract-productA"; 2 | 3 | export class ConcreteProductA1 implements AbstractProductA { 4 | public usefulFunctionA(): string { 5 | return "The result of the product A1."; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/concrete-productA2.ts: -------------------------------------------------------------------------------- 1 | import { AbstractProductA } from "./abstract-productA"; 2 | 3 | export class ConcreteProductA2 implements AbstractProductA { 4 | public usefulFunctionA(): string { 5 | return "The result of the product A2."; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/concrete-productB1.ts: -------------------------------------------------------------------------------- 1 | import { AbstractProductB } from "./abstract-productB"; 2 | 3 | export class ConcreteProductB1 implements AbstractProductB { 4 | public usefulFunctionB(): string { 5 | return "The result of the product B1."; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/concrete-productB2.ts: -------------------------------------------------------------------------------- 1 | import { AbstractProductB } from "./abstract-productB"; 2 | 3 | export class ConcreteProductB2 implements AbstractProductB { 4 | public usefulFunctionB(): string { 5 | return "The result of the product B2."; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { AbstractFactory } from "./abstract-factory"; 2 | import { ConcreteFactory1 } from "./concrete-factory1"; 3 | import { ConcreteFactory2 } from "./concrete-factory2"; 4 | 5 | function clientCode(factory: AbstractFactory) { 6 | const productA = factory.createProductA(); 7 | const productB = factory.createProductB(); 8 | 9 | console.log(productA.usefulFunctionA()); 10 | console.log(productB.usefulFunctionB()); 11 | } 12 | 13 | console.log("Client: Testing client code with ConcreteFactory1"); 14 | clientCode(new ConcreteFactory1()); 15 | 16 | console.log("----------------"); 17 | 18 | console.log("Client: Testing the same client code with ConcreteFactory2"); 19 | clientCode(new ConcreteFactory2()); 20 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/abstract-factory.ts: -------------------------------------------------------------------------------- 1 | import { Armor } from "./armor/armor-interface"; 2 | import { Weapon } from "./weapons/weapon.interface"; 3 | 4 | export interface AbstractFactory { 5 | createWeapon(): Weapon; 6 | createArmor(): Armor; 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/armor/armor-interface.ts: -------------------------------------------------------------------------------- 1 | import { Weapon } from "../weapons/weapon.interface"; 2 | 3 | export interface Armor { 4 | usefulFunction(): string; 5 | usefulFunctionWithWeapon(collaborator: Weapon): string; 6 | } 7 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/armor/body-armor.model.ts: -------------------------------------------------------------------------------- 1 | import { Armor } from "./armor-interface"; 2 | import { Weapon } from "../weapons/weapon.interface"; 3 | 4 | export class BodyArmor implements Armor { 5 | public usefulFunction(): string { 6 | return "The result of the BodyArmor"; 7 | } 8 | 9 | public usefulFunctionWithWeapon(collaborator: Weapon): string { 10 | const result = collaborator.usefulFunction(); 11 | return `The result of the BodyAmor collaborating with the (${result})`; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/armor/cloak.model.ts: -------------------------------------------------------------------------------- 1 | import { Armor } from "./armor-interface"; 2 | import { Weapon } from "../weapons/weapon.interface"; 3 | 4 | export class Cloak implements Armor { 5 | public usefulFunction(): string { 6 | return "The result of the Cloak"; 7 | } 8 | 9 | public usefulFunctionWithWeapon(collaborator: Weapon): string { 10 | const result = collaborator.usefulFunction(); 11 | return `The result of the Cloak collaborating with the (${result})`; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/armor/orc-armor.model.ts: -------------------------------------------------------------------------------- 1 | import { Armor } from "./armor-interface"; 2 | import { Weapon } from "../weapons/weapon.interface"; 3 | 4 | export class OrcArmor implements Armor { 5 | public usefulFunction(): string { 6 | return "The result of the OrcArmor"; 7 | } 8 | 9 | public usefulFunctionWithWeapon(collaborator: Weapon): string { 10 | const result = collaborator.usefulFunction(); 11 | return `The result of the OrcAmor collaborating with the (${result})`; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/index.ts: -------------------------------------------------------------------------------- 1 | import { AbstractFactory } from "./abstract-factory"; 2 | import { MageFactory } from "./mage-factory"; 3 | import { OrcFactory } from "./orc-factory"; 4 | import { WarriorFactory } from "./warrior-factory"; 5 | 6 | function clientCode(factory: AbstractFactory) { 7 | const sword = factory.createWeapon(); 8 | const armor = factory.createArmor(); 9 | 10 | console.log(armor.usefulFunction()); 11 | console.log(armor.usefulFunctionWithWeapon(sword)); 12 | } 13 | 14 | console.log("Client: WarriorFactory"); 15 | clientCode(new WarriorFactory()); 16 | 17 | console.log("----------------"); 18 | 19 | console.log("Client: OrcFactory"); 20 | clientCode(new OrcFactory()); 21 | 22 | console.log("----------------"); 23 | 24 | console.log("Client: MageFactory"); 25 | clientCode(new MageFactory()); 26 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/mage-factory.ts: -------------------------------------------------------------------------------- 1 | import { AbstractFactory } from "./abstract-factory"; 2 | import { Armor } from "./armor/armor-interface"; 3 | import { Cloak } from "./armor/cloak.model"; 4 | import { MageFireball } from "./weapons/mage-fireball.model"; 5 | import { Weapon } from "./weapons/weapon.interface"; 6 | 7 | export class MageFactory implements AbstractFactory { 8 | public createWeapon(): Weapon { 9 | return new MageFireball(); 10 | } 11 | 12 | public createArmor(): Armor { 13 | return new Cloak(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/orc-factory.ts: -------------------------------------------------------------------------------- 1 | import { AbstractFactory } from "./abstract-factory"; 2 | import { Armor } from "./armor/armor-interface"; 3 | import { Axe } from "./weapons/axe.model"; 4 | import { OrcArmor } from "./armor/orc-armor.model"; 5 | import { Weapon } from "./weapons/weapon.interface"; 6 | 7 | export class OrcFactory implements AbstractFactory { 8 | public createWeapon(): Weapon { 9 | return new Axe(); 10 | } 11 | 12 | public createArmor(): Armor { 13 | return new OrcArmor(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/warrior-factory.ts: -------------------------------------------------------------------------------- 1 | import { AbstractFactory } from "./abstract-factory"; 2 | import { Armor } from "./armor/armor-interface"; 3 | import { BodyArmor } from "./armor/body-armor.model"; 4 | import { Sword } from "./weapons/sword.model"; 5 | import { Weapon } from "./weapons/weapon.interface"; 6 | 7 | export class WarriorFactory implements AbstractFactory { 8 | public createWeapon(): Weapon { 9 | return new Sword(); 10 | } 11 | 12 | public createArmor(): Armor { 13 | return new BodyArmor(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/weapons/axe.model.ts: -------------------------------------------------------------------------------- 1 | import { Weapon } from "./weapon.interface"; 2 | 3 | export class Axe implements Weapon { 4 | public usefulFunction(): string { 5 | return "The result of the Axe"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/weapons/mage-fireball.model.ts: -------------------------------------------------------------------------------- 1 | import { Weapon } from "./weapon.interface"; 2 | 3 | export class MageFireball implements Weapon { 4 | public usefulFunction(): string { 5 | return "The result of the MageFireball"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/weapons/sword.model.ts: -------------------------------------------------------------------------------- 1 | import { Weapon } from "./weapon.interface"; 2 | 3 | export class Sword implements Weapon { 4 | public usefulFunction(): string { 5 | return "The result of the Sword"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /abstract-factory-pattern/example2/weapons/weapon.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Weapon { 2 | usefulFunction(): string; 3 | } 4 | -------------------------------------------------------------------------------- /abstract-factory-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /abstract-factory-pattern/nodemon-example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /abstract-factory-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "abstract-factory-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2": "nodemon --config nodemon-example2.json", 8 | "example3": "nodemon --config nodemon-example3.json", 9 | "example4": "nodemon --config nodemon-example4.json" 10 | }, 11 | "keywords": [ 12 | "abstract-factory-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /adapter-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Adapter (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-adapter/).** -------------------------------------------------------------------------------- /adapter-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { Saiyan } from './models/saiyan.model'; 2 | import { Namekian } from './models/namekian'; 3 | import { AndroidAdapter } from './models/android-adapter.model'; 4 | import { Android } from './models/android.model'; 5 | 6 | const goku = new Saiyan(); 7 | const vegeta = new Saiyan(); 8 | const piccolo = new Namekian(); 9 | const C17 = new AndroidAdapter(new Android()); 10 | 11 | console.log(`Goku attack: ${goku.attack()}`); 12 | console.log(`Vegeta attack: ${vegeta.attack()}`); 13 | console.log(`Picolo attack: ${piccolo.attack()}`); 14 | console.log(`C17 attack: ${C17.attack()}`); 15 | -------------------------------------------------------------------------------- /adapter-pattern/example1/interfaces/warrior.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Warrior { 2 | ATTACK_BASE: number; 3 | attack(): number; 4 | } 5 | -------------------------------------------------------------------------------- /adapter-pattern/example1/models/android-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { Warrior } from '../interfaces/warrior.interface'; 2 | import { Android } from './android.model'; 3 | 4 | export class AndroidAdapter implements Warrior { 5 | constructor(private android: Android) {} 6 | public ATTACK_BASE = 50; 7 | public attack(): number { 8 | return this.android.kick() + this.android.punch() + this.ATTACK_BASE; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /adapter-pattern/example1/models/android.model.ts: -------------------------------------------------------------------------------- 1 | export class Android { 2 | public punch(): number { 3 | return 10; 4 | } 5 | public kick(): number { 6 | return Math.random() * this.punch() + this.punch(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /adapter-pattern/example1/models/namekian.ts: -------------------------------------------------------------------------------- 1 | import { Warrior } from '../interfaces/warrior.interface'; 2 | 3 | export class Namekian implements Warrior { 4 | public ATTACK_BASE = 50; 5 | public attack(): number { 6 | return Math.random() * 50 + this.ATTACK_BASE; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /adapter-pattern/example1/models/saiyan.model.ts: -------------------------------------------------------------------------------- 1 | import { Warrior } from '../interfaces/warrior.interface'; 2 | 3 | export class Saiyan implements Warrior { 4 | public ATTACK_BASE = 100; 5 | public attack(): number { 6 | return Math.random() * 100 + this.ATTACK_BASE; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /adapter-pattern/example2/interfaces/pure-race.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PureRace { 2 | genki(): number; 3 | } 4 | -------------------------------------------------------------------------------- /adapter-pattern/example2/models/human-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Human } from './human.model'; 3 | 4 | export class HumanAdapter implements PureRace { 5 | constructor(private human: Human) {} 6 | 7 | public genki(): number { 8 | return this.human.sharedPower(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /adapter-pattern/example2/models/human.model.ts: -------------------------------------------------------------------------------- 1 | export class Human { 2 | public sharedPower(): number { 3 | return 10; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /adapter-pattern/example2/models/namekian-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Namekian } from './namekian'; 3 | 4 | export class NamekianAdapter implements PureRace { 5 | constructor(private namekian: Namekian) {} 6 | public genki(): number { 7 | return this.namekian.getPower(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /adapter-pattern/example2/models/namekian.ts: -------------------------------------------------------------------------------- 1 | export class Namekian { 2 | public getPower(): number { 3 | return Math.random() * 20 + 20; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /adapter-pattern/example2/models/saiyan-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Saiyan } from './saiyan.model'; 3 | 4 | export class SaiyanAdapter implements PureRace { 5 | constructor(private saiyan: Saiyan) {} 6 | public genki(): number { 7 | return this.saiyan.myPowerPart1() + this.saiyan.myPowerPart2(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /adapter-pattern/example2/models/saiyan.model.ts: -------------------------------------------------------------------------------- 1 | export class Saiyan { 2 | myPowerPart1(): number { 3 | return Math.random() * 100 + 100; 4 | } 5 | myPowerPart2(): number { 6 | return Math.random() * 1000 + 500; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /adapter-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /adapter-pattern/nodemon-example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /adapter-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adapter-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2": "nodemon --config nodemon-example2.json" 8 | }, 9 | "keywords": [ 10 | "adapter-pattern", 11 | "clean-code" 12 | ], 13 | "author": "Carlos Caballero", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "nodemon": "^1.18.10", 17 | "typescript": "^3.3.3333" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /bridge-pattern/example1/abstraction.ts: -------------------------------------------------------------------------------- 1 | // Esta es la abstracción en el patrón Bridge. 2 | import { Implementor } from './implementor'; 3 | 4 | export abstract class Abstraction { 5 | protected implementor: Implementor; 6 | 7 | constructor(implementor: Implementor) { 8 | this.implementor = implementor; 9 | } 10 | 11 | abstract operation(): void; 12 | } 13 | -------------------------------------------------------------------------------- /bridge-pattern/example1/concrete-implementorA.ts: -------------------------------------------------------------------------------- 1 | // Esta es una implementación concreta de Implementor. 2 | import { Implementor } from './implementor'; 3 | 4 | export class ConcreteImplementorA implements Implementor { 5 | operationImplementation(): void { 6 | console.log("ConcreteImplementorA operation implementation."); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /bridge-pattern/example1/concrete-implementorB.ts: -------------------------------------------------------------------------------- 1 | // Otra implementación concreta de Implementor. 2 | import { Implementor } from './implementor'; 3 | 4 | export class ConcreteImplementorB implements Implementor { 5 | operationImplementation(): void { 6 | console.log("ConcreteImplementorB operation implementation."); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /bridge-pattern/example1/implementor.ts: -------------------------------------------------------------------------------- 1 | // Este es el Implementor en el patrón Bridge. Define la interfaz para las implementaciones concretas. 2 | export interface Implementor { 3 | operationImplementation(): void; 4 | } 5 | -------------------------------------------------------------------------------- /bridge-pattern/example1/refine-abstraction.ts: -------------------------------------------------------------------------------- 1 | // Esta es una versión refinada de la abstracción. 2 | import { Abstraction } from './abstraction'; 3 | 4 | export class RefinedAbstraction extends Abstraction { 5 | operation(): void { 6 | console.log('RefinedAbstraction: operation'); 7 | this.implementor.operationImplementation(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bridge-pattern/example2/problem/desktop-messaging.ts: -------------------------------------------------------------------------------- 1 | // DesktopMessaging.ts 2 | export class DesktopMessaging { 3 | sendMessageFromDesktop(message: string): void { 4 | console.log("Sending message from a desktop: " + message); 5 | } 6 | 7 | receiveMessageOnDesktop(): string { 8 | return "Message received on a desktop."; 9 | } 10 | } -------------------------------------------------------------------------------- /bridge-pattern/example2/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { DesktopMessaging } from "./desktop-messaging"; 2 | import { MobileMessaging } from "./mobile-messaging"; 3 | 4 | class Client { 5 | static main(): void { 6 | // Utilizando la mensajería móvil 7 | const mobileMessaging = new MobileMessaging(); 8 | mobileMessaging.sendMessageFromMobile("Hello from Mobile!"); 9 | console.log(mobileMessaging.receiveMessageOnMobile()); 10 | 11 | // Utilizando la mensajería de escritorio 12 | const desktopMessaging = new DesktopMessaging(); 13 | desktopMessaging.sendMessageFromDesktop("Hello from Desktop!"); 14 | console.log(desktopMessaging.receiveMessageOnDesktop()); 15 | } 16 | } 17 | 18 | Client.main(); -------------------------------------------------------------------------------- /bridge-pattern/example2/problem/mobile-messaging.ts: -------------------------------------------------------------------------------- 1 | // MobileMessaging.ts 2 | export class MobileMessaging { 3 | sendMessageFromMobile(message: string): void { 4 | console.log("Sending message from a mobile device: " + message); 5 | } 6 | 7 | receiveMessageOnMobile(): string { 8 | return "Message received on a mobile device."; 9 | } 10 | } 11 | 12 | 13 | -------------------------------------------------------------------------------- /bridge-pattern/example2/solution/advanced-messaging.ts: -------------------------------------------------------------------------------- 1 | import { MessagingService } from './messaging-sender'; 2 | 3 | export class AdvancedMessaging extends MessagingService { 4 | send(message: string): void { 5 | this.sender.sendMessage(message); 6 | } 7 | 8 | receive(): string { 9 | return this.sender.receiveMessage(); 10 | } 11 | } -------------------------------------------------------------------------------- /bridge-pattern/example2/solution/desktop-sender.ts: -------------------------------------------------------------------------------- 1 | import { MessageSender } from './message-sender'; 2 | 3 | export class DesktopSender implements MessageSender { 4 | sendMessage(message: string): void { 5 | console.log("Sending message from a desktop: " + message); 6 | } 7 | 8 | receiveMessage(): string { 9 | return "Message received on a desktop."; 10 | } 11 | } -------------------------------------------------------------------------------- /bridge-pattern/example2/solution/index.ts: -------------------------------------------------------------------------------- 1 | import { AdvancedMessaging } from "./advanced-messaging"; 2 | import { DesktopSender } from "./desktop-sender"; 3 | import { MobileSender } from "./mobile-sender"; 4 | 5 | class Client { 6 | static main(): void { 7 | // Use mobile sender 8 | const mobileMessaging = new AdvancedMessaging(new MobileSender()); 9 | mobileMessaging.send("Hello from Mobile!"); 10 | console.log(mobileMessaging.receive()); 11 | 12 | // Use desktop sender 13 | const desktopMessaging = new AdvancedMessaging(new DesktopSender()); 14 | desktopMessaging.send("Hello from Desktop!"); 15 | console.log(desktopMessaging.receive()); 16 | } 17 | } 18 | 19 | Client.main(); -------------------------------------------------------------------------------- /bridge-pattern/example2/solution/message-sender.ts: -------------------------------------------------------------------------------- 1 | export interface MessageSender { 2 | sendMessage(message: string): void; 3 | receiveMessage(): string; 4 | } -------------------------------------------------------------------------------- /bridge-pattern/example2/solution/messaging-sender.ts: -------------------------------------------------------------------------------- 1 | import { MessageSender } from './message-sender'; 2 | 3 | export abstract class MessagingService { 4 | protected sender: MessageSender; 5 | 6 | constructor(sender: MessageSender) { 7 | this.sender = sender; 8 | } 9 | 10 | abstract send(message: string): void; 11 | abstract receive(): string; 12 | } -------------------------------------------------------------------------------- /bridge-pattern/example2/solution/mobile-sender.ts: -------------------------------------------------------------------------------- 1 | import { MessageSender } from './message-sender'; 2 | 3 | export class MobileSender implements MessageSender { 4 | sendMessage(message: string): void { 5 | console.log("Sending message from a mobile device: " + message); 6 | } 7 | 8 | receiveMessage(): string { 9 | return "Message received on a mobile device."; 10 | } 11 | } -------------------------------------------------------------------------------- /bridge-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /bridge-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /bridge-pattern/nodemon-example2-solve.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /bridge-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bridge-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2:problem": "nodemon --config nodemon-example2-problem.json", 8 | "example2:solve": "nodemon --config nodemon-example2-solve.json" 9 | 10 | }, 11 | "keywords": [ 12 | "bridge-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /builder-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Builder (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-builder/).** -------------------------------------------------------------------------------- /builder-pattern/example1/builder.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Builder { 2 | addPartA(): void; 3 | addPartB(): void; 4 | addPartC(): void; 5 | removePartA(): void; 6 | removePartB(): void; 7 | removePartC(): void; 8 | } -------------------------------------------------------------------------------- /builder-pattern/example1/director.ts: -------------------------------------------------------------------------------- 1 | import { Builder } from "./builder.interface"; 2 | 3 | export class Director { 4 | private builder: Builder; 5 | 6 | public setBuilder(builder: Builder): void { 7 | this.builder = builder; 8 | } 9 | 10 | public buildBasicObject(): void { 11 | this.builder.addPartA(); 12 | } 13 | 14 | public buildFullObject(): void { 15 | this.builder.addPartA(); 16 | this.builder.addPartB(); 17 | this.builder.addPartC(); 18 | } 19 | } -------------------------------------------------------------------------------- /builder-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteBuilder1 } from './concrete-builder1'; 2 | import { Director } from './director'; 3 | 4 | function client(director: Director) { 5 | const builder = new ConcreteBuilder1(); 6 | director.setBuilder(builder); 7 | 8 | console.log('A preconfigured basic object:'); 9 | director.buildBasicObject(); 10 | builder.build().showParts(); 11 | 12 | console.log('A preconfigured full object:'); 13 | director.buildFullObject(); 14 | builder.build().showParts(); 15 | 16 | // A custom object can be create without a Director class. 17 | console.log('Custom product:'); 18 | builder.addPartA(); 19 | builder.addPartC(); 20 | builder.build().showParts(); 21 | } 22 | 23 | const director = new Director(); 24 | client(director); 25 | -------------------------------------------------------------------------------- /builder-pattern/example1/product.ts: -------------------------------------------------------------------------------- 1 | export class Product { 2 | public parts: string[] = []; 3 | 4 | public addPart(part: string): void { 5 | this.parts.push(part); 6 | } 7 | public removePart(part: string): void { 8 | this.parts = this.parts.filter(_part => _part !== part); 9 | } 10 | 11 | public showParts(): void { 12 | console.log(`Product parts: ${this.parts.join(', ')}\n`); 13 | } 14 | } -------------------------------------------------------------------------------- /builder-pattern/example2/hero-builder.ts: -------------------------------------------------------------------------------- 1 | import { Hero } from "./hero.model"; 2 | 3 | export abstract class HeroBuilder { 4 | protected hero: Hero; 5 | 6 | public abstract setArmor(): void; 7 | public abstract setWeapon(): void; 8 | public abstract setSkills(): void; 9 | 10 | public abstract build(): Hero; 11 | } -------------------------------------------------------------------------------- /builder-pattern/example2/hero-director.ts: -------------------------------------------------------------------------------- 1 | import { Hero } from "./hero.model"; 2 | import { HeroBuilder } from "./hero-builder"; 3 | 4 | export class HeroDirector { 5 | 6 | public createHero (heroBuilder: HeroBuilder): Hero { 7 | heroBuilder.setArmor(); 8 | heroBuilder.setSkills(); 9 | heroBuilder.setWeapon(); 10 | return heroBuilder.build(); 11 | } 12 | public createHeroBasic (heroBuilder: HeroBuilder): Hero{ 13 | return heroBuilder.build(); 14 | } 15 | public createHeroWithArmor(heroBuilder: HeroBuilder): Hero{ 16 | heroBuilder.setArmor(); 17 | return heroBuilder.build(); 18 | } 19 | } -------------------------------------------------------------------------------- /builder-pattern/example2/hero.model.ts: -------------------------------------------------------------------------------- 1 | export class Hero { 2 | public race: string; 3 | public armor: string; 4 | public weapon: string; 5 | public skills: string[]; 6 | 7 | 8 | public toString(): string { 9 | return `Hero: 10 | race=${this.race ? this.race : 'empty'} 11 | armor=${this.armor ? this.armor: 'empty'} 12 | weapon=${this.weapon ? this.weapon: 'empty'} 13 | skills=${this.skills ? this.skills: 'empty'} 14 | `; 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /builder-pattern/example2/human-hero-builder.ts: -------------------------------------------------------------------------------- 1 | import { Hero } from "./hero.model"; 2 | import { HeroBuilder } from "./hero-builder"; 3 | 4 | export class HumanHeroBuilder extends HeroBuilder { 5 | 6 | constructor() { 7 | super(); 8 | this.reset(); 9 | } 10 | public reset() { 11 | this.hero = new Hero(); 12 | this.hero.race = "Human"; 13 | } 14 | 15 | public setArmor():void { 16 | this.hero.armor = "Human armor"; 17 | } 18 | 19 | public setWeapon(): void { 20 | this.hero.weapon = 'Human weapon'; 21 | } 22 | public setSkills(): void { 23 | this.hero.skills = ['Human skill1', 'Human skill2']; 24 | } 25 | public build(): Hero { 26 | const hero = this.hero; 27 | this.reset(); 28 | return hero; 29 | } 30 | } -------------------------------------------------------------------------------- /builder-pattern/example2/orc-hero-builder.ts: -------------------------------------------------------------------------------- 1 | import { Hero } from "./hero.model"; 2 | import { HeroBuilder } from "./hero-builder"; 3 | 4 | export class OrcHeroBuilder extends HeroBuilder { 5 | 6 | constructor() { 7 | super(); 8 | this.reset(); 9 | } 10 | public reset() { 11 | this.hero = new Hero(); 12 | this.hero.race = "Orc"; 13 | } 14 | 15 | public setArmor():void { 16 | this.hero.armor = "Orc armor"; 17 | } 18 | 19 | public setWeapon(): void { 20 | this.hero.weapon = 'Orc weapon'; 21 | } 22 | public setSkills(): void { 23 | this.hero.skills = ['Orc skill1', 'Orc skill2']; 24 | } 25 | public build(): Hero { 26 | const hero = this.hero; 27 | this.reset(); 28 | return hero; 29 | } 30 | } -------------------------------------------------------------------------------- /builder-pattern/example3/burger-type.interface.ts: -------------------------------------------------------------------------------- 1 | export enum BurgerType { 2 | NORMAL, 3 | CHEESE, 4 | VEGGIE, 5 | DOUBLE, 6 | CHEESE_BACON, 7 | DOTTECH, 8 | GODZILLA 9 | } 10 | -------------------------------------------------------------------------------- /builder-pattern/example4/data-webpages.ts: -------------------------------------------------------------------------------- 1 | import { WebPage } from "./webpage.model" 2 | 3 | export const WEBPAGES: Array = [ 4 | new WebPage("Dave", "http://example.com/dave", null, null), 5 | new WebPage("Matt", "http://example.com/matt", null, null), 6 | new WebPage("Greg", "http://example.com/greg", null, null), 7 | new WebPage("Kathy", "http://example.com/kathy", null, null), 8 | new WebPage("Tim", "http://example.com/tim", null, null), 9 | new WebPage("Ben", "http://example.com/ben", null, null), 10 | new WebPage("Zack", "http://example.com/zack", null, null) 11 | ]; 12 | 13 | -------------------------------------------------------------------------------- /builder-pattern/example4/google-sitemap.builder.ts: -------------------------------------------------------------------------------- 1 | import { SiteMapBuilder } from "./sitemap.builder"; 2 | 3 | export class GoogleSiteMapBuilder extends SiteMapBuilder { 4 | 5 | 6 | public buildHeader(): void { 7 | this.header = "\n" 8 | + "\n"; 9 | } 10 | 11 | public buildFooter(): void { 12 | this.footer = "\n\n\n"; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /builder-pattern/example4/html-sitemap.builder.ts: -------------------------------------------------------------------------------- 1 | import { SiteMapBuilder } from "./sitemap.builder"; 2 | 3 | export class HtmlSiteMapBuilder extends SiteMapBuilder { 4 | 5 | 6 | 7 | public buildHeader(): void { 8 | this.header = "\n"; 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /builder-pattern/example4/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | https://github.com/dparoulek/java-koans/tree/master/src/main/java/com/upgradingdave/koans/builder */ 4 | 5 | import { GoogleSiteMapBuilder } from "./google-sitemap.builder"; 6 | import { SiteMapDirector } from "./sitemap-director"; 7 | 8 | //const builder = new HtmlSiteMapBuilder(); 9 | const builder = new GoogleSiteMapBuilder(); 10 | 11 | const siteMapMaker = new SiteMapDirector(builder); 12 | console.log(siteMapMaker.siteMap); -------------------------------------------------------------------------------- /builder-pattern/example4/sitemap-director.ts: -------------------------------------------------------------------------------- 1 | import { SiteMapBuilder } from "./sitemap.builder"; 2 | import { WEBPAGES } from "./data-webpages"; 3 | 4 | export class SiteMapDirector { 5 | public siteMap: string; 6 | 7 | constructor(siteMapBuilder: SiteMapBuilder) { 8 | 9 | siteMapBuilder.buildHeader(); 10 | siteMapBuilder.buildFooter(); 11 | 12 | for (const webpage of WEBPAGES){ 13 | siteMapBuilder.buildPage(new URL(webpage.url)); 14 | } 15 | 16 | this.siteMap = siteMapBuilder.getSiteMap(); 17 | } 18 | } -------------------------------------------------------------------------------- /builder-pattern/example4/sitemap.builder.ts: -------------------------------------------------------------------------------- 1 | export abstract class SiteMapBuilder { 2 | 3 | protected header: string; 4 | protected footer: string; 5 | protected urls: Array = []; 6 | 7 | abstract buildHeader(): void; 8 | abstract buildFooter(): void; 9 | 10 | buildPage(url: URL): void { 11 | if (this.urls.length === 0) { 12 | this.urls = []; 13 | } 14 | this.urls.push(url); 15 | } 16 | 17 | getSiteMap(): string { 18 | const body = this.urls.reduce((content, url) => 19 | content += `\n\t\n\t\t ${url.toString()} \n\t`, 20 | ''); 21 | 22 | return this.header + body + this.footer; 23 | 24 | }; 25 | } -------------------------------------------------------------------------------- /builder-pattern/example4/webpage.model.ts: -------------------------------------------------------------------------------- 1 | 2 | export class WebPage { 3 | 4 | public title: string; 5 | public url: string; 6 | public parent: WebPage; 7 | public children: Array; 8 | 9 | public constructor(title: string, url: string, parent: WebPage, children: Array) { 10 | this.title = title; 11 | this.url = url; 12 | this.parent = parent; 13 | this.children = children; 14 | } 15 | 16 | 17 | } -------------------------------------------------------------------------------- /builder-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /builder-pattern/nodemon-example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /builder-pattern/nodemon-example3.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example3"], 3 | "ext": "ts", 4 | "ignore": ["example3/**/*.spec.ts"], 5 | "exec": "ts-node ./example3/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /builder-pattern/nodemon-example4.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example4"], 3 | "ext": "ts", 4 | "ignore": ["example4/**/*.spec.ts"], 5 | "exec": "ts-node ./example4/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /builder-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "adapter-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2": "nodemon --config nodemon-example2.json", 8 | "example3": "nodemon --config nodemon-example3.json", 9 | "example4": "nodemon --config nodemon-example4.json" 10 | }, 11 | "keywords": [ 12 | "builder-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Chain of Responsibility (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/).** 4 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example1/abstract-handler.ts: -------------------------------------------------------------------------------- 1 | import { Handler } from "./handler"; 2 | 3 | export abstract class AbstractHandler implements Handler { 4 | private nextHandler: Handler; 5 | 6 | public setNext(handler: Handler): Handler { 7 | this.nextHandler = handler; 8 | return handler; 9 | } 10 | 11 | public handle(request: string): string { 12 | if (this.nextHandler) { 13 | return this.nextHandler.handle(request); 14 | } 15 | 16 | return null; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example1/handler.ts: -------------------------------------------------------------------------------- 1 | export interface Handler { 2 | setNext(handler: Handler): Handler; 3 | 4 | handle(request: string): string; 5 | } 6 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example1/handlerA.ts: -------------------------------------------------------------------------------- 1 | import { AbstractHandler } from "./abstract-handler"; 2 | 3 | export class HandlerA extends AbstractHandler { 4 | public handle(request: string): string { 5 | if (request === "optionA") { 6 | return `A: I'll do the operation -> ${request}.`; 7 | } 8 | return super.handle(request); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example1/handlerB.ts: -------------------------------------------------------------------------------- 1 | import { AbstractHandler } from "./abstract-handler"; 2 | 3 | export class HandlerB extends AbstractHandler { 4 | public handle(request: string): string { 5 | if (request === "optionB") { 6 | return `B: I'll do the operation -> ${request}.`; 7 | } 8 | return super.handle(request); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example1/handlerC.ts: -------------------------------------------------------------------------------- 1 | import { AbstractHandler } from "./abstract-handler"; 2 | 3 | export class HandlerC extends AbstractHandler { 4 | public handle(request: string): string { 5 | if (request === "optionC") { 6 | return `C: I'll do the operation -> ${request}.`; 7 | } 8 | return super.handle(request); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example2/app.constants.ts: -------------------------------------------------------------------------------- 1 | export const USERS = { 2 | ADMIN: { 3 | email: "admin@example.com", 4 | password: "admin", 5 | }, 6 | USER: { 7 | email: "user@example.com", 8 | password: "user", 9 | }, 10 | }; 11 | 12 | export const REQUEST_PER_MINUTE = 2; 13 | export const WAIT_TIME = 60_000; 14 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example2/interfaces/handler.ts: -------------------------------------------------------------------------------- 1 | import { User } from "./user.interface"; 2 | 3 | export interface Handler { 4 | setNextMiddleware(handler: Handler): void; 5 | 6 | execute(user: User): boolean; 7 | } 8 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example2/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export { Handler } from "./handler"; 2 | export { User } from "./user.interface"; 3 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example2/interfaces/user.interface.ts: -------------------------------------------------------------------------------- 1 | export interface User { 2 | email: string; 3 | password: string; 4 | } 5 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example2/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | export { RoleMiddleware } from "./role.middleware"; 2 | export { ThrottlingMiddleware } from "./throttling.middleware"; 3 | export { UserExistsMiddleware } from "./user-exists.middleware"; 4 | export { Middleware } from "./middleware"; 5 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example2/middlewares/middleware.ts: -------------------------------------------------------------------------------- 1 | import { Handler, User } from "../interfaces"; 2 | 3 | export abstract class Middleware implements Handler { 4 | private next: Handler; 5 | 6 | public setNextMiddleware(next: Handler): Handler { 7 | this.next = next; 8 | return next; 9 | } 10 | public abstract execute(user: User): boolean; 11 | 12 | protected checkNext(user: User): boolean { 13 | if (!this.next) { 14 | return true; 15 | } 16 | return this.next.execute(user); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example2/middlewares/role.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Middleware } from "./middleware"; 2 | import { USERS } from "../app.constants"; 3 | import { User } from "../interfaces"; 4 | 5 | export class RoleMiddleware extends Middleware { 6 | public execute({ email, password }: User): boolean { 7 | if (email === USERS.ADMIN.email) { 8 | console.log("Hello, admin!"); 9 | return true; 10 | } 11 | console.log("Hello, user!"); 12 | return this.checkNext({ email, password }); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/example2/middlewares/user-exists.middleware.ts: -------------------------------------------------------------------------------- 1 | import { Middleware } from "./middleware"; 2 | import { Server } from "../server"; 3 | import { User } from "../interfaces"; 4 | 5 | export class UserExistsMiddleware extends Middleware { 6 | public constructor(private server: Server) { 7 | super(); 8 | } 9 | 10 | public execute({ email, password }: User): boolean { 11 | if (!this.server.hasEmail(email)) { 12 | console.log("This email is not registered!"); 13 | return false; 14 | } 15 | if (!this.server.isValidPassword(email, password)) { 16 | console.log("Wrong password!"); 17 | return false; 18 | } 19 | return this.checkNext({ email, password }); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/nodemon-example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chain-responsibility-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2": "nodemon --config nodemon-example2.json" 8 | }, 9 | "keywords": [ 10 | "chain-responsibility-pattern", 11 | "clean-code" 12 | ], 13 | "author": "Carlos Caballero", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "nodemon": "^1.19.4", 17 | "ts-node": "^9.1.1", 18 | "typescript": "^3.9.7" 19 | }, 20 | "dependencies": { 21 | "@types/node": "^15.0.1", 22 | "readline-sync": "^1.4.10" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chain-responsibility-pattern/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es7"], 4 | "target": "es5" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /command-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Facade (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-command/).** 4 | -------------------------------------------------------------------------------- /command-pattern/example1/command-solution-1/agent.class.ts: -------------------------------------------------------------------------------- 1 | import { Order } from './order.class'; 2 | 3 | // Invoker. 4 | export class Agent { 5 | private orders: Order[] = []; 6 | 7 | public constructor() {} 8 | 9 | placeOrder(order: Order) { 10 | this.orders.push(order); 11 | order.execute(); 12 | } 13 | 14 | listOrders(): string { 15 | return this.orders.reduce((acc, curr) => acc + curr.toString() + '\n', ''); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /command-pattern/example1/command-solution-1/buy-stock-order.ts: -------------------------------------------------------------------------------- 1 | import { Order } from './order.class'; 2 | import { StockTrade } from './stock.trader.class'; 3 | 4 | //ConcreteCommand Class. 5 | export class BuyStockOrder extends Order { 6 | private stock: StockTrade; 7 | protected type: string; 8 | public constructor(stockTrade: StockTrade) { 9 | super(); 10 | this.stock = stockTrade; 11 | this.type = 'Buy'; 12 | } 13 | public execute(): void { 14 | this.stock.buy(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /command-pattern/example1/command-solution-1/index.ts: -------------------------------------------------------------------------------- 1 | import { Agent } from './agent.class'; 2 | import { BuyStockOrder } from './buy-stock-order'; 3 | import { SellStockOrder } from './sell-stock-order'; 4 | import { StockTrade } from './stock.trader.class'; 5 | 6 | const stock = new StockTrade(); 7 | const buyStock = new BuyStockOrder(stock); 8 | const sellStock = new SellStockOrder(stock); 9 | const agent = new Agent(); 10 | 11 | agent.placeOrder(buyStock); // Buy Shares 12 | agent.placeOrder(sellStock); // Sell Shares 13 | 14 | console.log(agent.listOrders()); 15 | -------------------------------------------------------------------------------- /command-pattern/example1/command-solution-1/order.class.ts: -------------------------------------------------------------------------------- 1 | export abstract class Order { 2 | protected uid = Math.floor(Math.random() * 100000); 3 | protected type: string; 4 | public toString(): string { 5 | return 'UID: ' + this.uid + ' - Operation: ' + this.type; 6 | } 7 | abstract execute(); 8 | } 9 | -------------------------------------------------------------------------------- /command-pattern/example1/command-solution-1/sell-stock-order.ts: -------------------------------------------------------------------------------- 1 | import { Order } from './order.class'; 2 | import { StockTrade } from './stock.trader.class'; 3 | 4 | //ConcreteCommand Class. 5 | export class SellStockOrder extends Order { 6 | private stockTrade: StockTrade; 7 | protected type = 'sell'; 8 | public constructor(stockTrade: StockTrade) { 9 | super(); 10 | this.stockTrade = stockTrade; 11 | } 12 | public execute() { 13 | this.stockTrade.sell(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /command-pattern/example1/command-solution-1/stock.trader.class.ts: -------------------------------------------------------------------------------- 1 | // Receiver class. 2 | export class StockTrade { 3 | buy() { 4 | console.log('You want to buy stocks'); 5 | } 6 | sell() { 7 | console.log('You want to sell stocks '); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /command-pattern/example1/problem/agent.class.ts: -------------------------------------------------------------------------------- 1 | import { StockTrade } from '../command-solution-1/stock.trader.class'; 2 | 3 | // Invoker. 4 | export class Agent { 5 | private stockTrade: StockTrade = new StockTrade(); 6 | 7 | public constructor() {} 8 | 9 | placeOrder(orderType: string): void { 10 | if (orderType === 'buy') { 11 | this.stockTrade.buy(); 12 | } else if (orderType === 'sell') { 13 | this.stockTrade.sell(); 14 | } else { 15 | console.log('OPERATION NOT FOUND!'); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /command-pattern/example1/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { Agent } from './agent.class'; 2 | 3 | const agent = new Agent(); 4 | agent.placeOrder('buy'); 5 | agent.placeOrder('sell'); 6 | -------------------------------------------------------------------------------- /command-pattern/example1/problem/stock.trader.class.ts: -------------------------------------------------------------------------------- 1 | // Receiver class. 2 | export class StockTrade { 3 | buy() { 4 | console.log('You want to buy stocks'); 5 | } 6 | sell() { 7 | console.log('You want to sell stocks '); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/commands/clean-command.ts: -------------------------------------------------------------------------------- 1 | import { Command } from './command.class'; 2 | import { R2D2Service } from '../services'; 3 | 4 | export class CleanCommand extends Command { 5 | public constructor(private r2d2Service: R2D2Service) { 6 | super(); 7 | } 8 | public execute(): void { 9 | console.log('Execute -> Clean Command'); 10 | this.r2d2Service.clean(); 11 | console.log(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/commands/command.class.ts: -------------------------------------------------------------------------------- 1 | export abstract class Command { 2 | protected uid = Math.floor(Math.random() * 100000); 3 | 4 | public toString(): string { 5 | return 'R2D2: Command UID:: ' + this.uid; 6 | } 7 | abstract execute(args): void; 8 | } 9 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/commands/index.ts: -------------------------------------------------------------------------------- 1 | export * from './command.class'; 2 | export * from './save-secret-command'; 3 | export * from './clean-command'; 4 | export * from './move-command'; 5 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/commands/move-command.ts: -------------------------------------------------------------------------------- 1 | import { Command } from './command.class'; 2 | import { R2D2Service } from '../services'; 3 | 4 | export class MoveCommand extends Command { 5 | public constructor(private r2d2Service: R2D2Service) { 6 | super(); 7 | } 8 | public execute({ direction }: { direction: string }): void { 9 | console.log('Execute -> Move Command'); 10 | this.r2d2Service.move(direction); 11 | console.log(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/commands/save-secret-command.ts: -------------------------------------------------------------------------------- 1 | import { Command } from './command.class'; 2 | import { StoreService } from '../services/store.service'; 3 | 4 | export class SaveSecretCommand extends Command { 5 | public constructor(private storeService: StoreService) { 6 | super(); 7 | } 8 | public execute({ message }: { message: string }): void { 9 | console.log('Execute -> Execute Command...'); 10 | this.storeService.storeMessage(message); 11 | console.log(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/index.ts: -------------------------------------------------------------------------------- 1 | import { CleanCommand, MoveCommand, SaveSecretCommand } from './commands'; 2 | import { R2D2Service, StoreService } from './services'; 3 | 4 | import { R2D2 } from './r2d2'; 5 | 6 | const storeService = new StoreService(); 7 | const r2d2Service = new R2D2Service(); 8 | 9 | const saveSecretCommand = new SaveSecretCommand(storeService); 10 | const cleanCommand = new CleanCommand(r2d2Service); 11 | const moveCommand = new MoveCommand(r2d2Service); 12 | 13 | const r2d2 = new R2D2(); 14 | 15 | r2d2.excuteCommand(saveSecretCommand, { message: 'SuperSecretMessage' }); 16 | r2d2.excuteCommand(cleanCommand); 17 | r2d2.excuteCommand(moveCommand, { direction: 'Málaga, Paradise' }); 18 | 19 | console.log(r2d2.listCommands()); 20 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/r2d2.ts: -------------------------------------------------------------------------------- 1 | import { Command } from './commands/command.class'; 2 | 3 | export class R2D2 { 4 | private commands: Command[] = []; 5 | 6 | public constructor() {} 7 | 8 | excuteCommand(command: Command, commandArgs?: any): void { 9 | this.commands.push(command); 10 | command.execute(commandArgs); 11 | } 12 | 13 | listCommands(): string { 14 | return this.commands.reduce( 15 | (acc, curr) => acc + curr.toString() + '\n', 16 | '' 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/services/index.ts: -------------------------------------------------------------------------------- 1 | export * from './store.service'; 2 | export * from './r2d2.service'; 3 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/services/r2d2.service.ts: -------------------------------------------------------------------------------- 1 | export class R2D2Service { 2 | constructor() {} 3 | clean() { 4 | console.log('R2D2Service -> Clean '); 5 | } 6 | move(direction: string) { 7 | console.log('R2D2Service -> Move: ', direction); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /command-pattern/example2/command-solution-1/services/store.service.ts: -------------------------------------------------------------------------------- 1 | export class StoreService { 2 | constructor() {} 3 | storeMessage(message: string) { 4 | console.log('StoreService -> StoreMessage: ', message); 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /command-pattern/nodemon-example1-command-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/command-solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /command-pattern/nodemon-example1-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /command-pattern/nodemon-example2-command-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/command-solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /command-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "command-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1-problem": "nodemon --config nodemon-example1-problem.json", 7 | "example1-command-solution-1": "nodemon --config nodemon-example1-command-solution-1.json", 8 | "example1-command-solution-2": "nodemon --config nodemon-example1-command-solution-2.json", 9 | "example2-command-solution-1": "nodemon --config nodemon-example2-command-solution-1.json" 10 | }, 11 | "keywords": [ 12 | "command-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.18.10", 19 | "typescript": "^3.3.3333" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /composite-pattern/example1/component.ts: -------------------------------------------------------------------------------- 1 | export interface Component { 2 | operation(): void; 3 | } 4 | -------------------------------------------------------------------------------- /composite-pattern/example1/composite.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "./component"; 2 | 3 | export class Composite implements Component { 4 | private children: Component[] = []; 5 | 6 | // Add a child to the list of children 7 | addChild(child: Component): void { 8 | this.children.push(child); 9 | } 10 | 11 | // Remove a child from the list of children 12 | removeChild(child: Component): void { 13 | const index = this.children.indexOf(child); 14 | if (index !== -1) { 15 | this.children.splice(index, 1); 16 | } 17 | } 18 | 19 | // Implement the operation defined in the Component interface 20 | operation(): void { 21 | console.log("Composite operation."); 22 | this.children.forEach(child => child.operation()); 23 | } 24 | } -------------------------------------------------------------------------------- /composite-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "./component"; 2 | import { Composite } from "./composite"; 3 | import { Leaf } from "./leaf"; 4 | 5 | const leaf1: Component = new Leaf(); 6 | const leaf2: Component = new Leaf(); 7 | const leaf3: Component = new Leaf(); 8 | const composite1: Component = new Composite(); 9 | const composite2: Component = new Composite(); 10 | 11 | // Build the tree structure 12 | (composite1 as Composite).addChild(leaf1); 13 | (composite1 as Composite).addChild(leaf2); 14 | (composite2 as Composite).addChild(leaf3); 15 | (composite2 as Composite).addChild(composite1); 16 | 17 | // Call the operation on the root composite 18 | composite2.operation(); 19 | 20 | -------------------------------------------------------------------------------- /composite-pattern/example1/leaf.ts: -------------------------------------------------------------------------------- 1 | import { Component } from "./component"; 2 | 3 | export class Leaf implements Component { 4 | operation(): void { 5 | console.log("Leaf operation."); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /composite-pattern/example2/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { Ingredient } from "./ingredient"; 2 | import { Recipe } from "./recipe"; 3 | 4 | const ingredient1 = new Ingredient("Flour", "2 cups"); 5 | const ingredient2 = new Ingredient("Eggs", "3"); 6 | const recipe = new Recipe("Cake"); 7 | recipe.addIngredient(ingredient1); 8 | recipe.addIngredient(ingredient2); 9 | 10 | const ingredient3 = new Ingredient("Milk", "1 cup"); 11 | const compositeRecipe = new Recipe("Cake with Milk"); 12 | compositeRecipe.addRecipe(recipe); 13 | compositeRecipe.addIngredient(ingredient3); 14 | 15 | console.log(compositeRecipe.showDetails()); 16 | -------------------------------------------------------------------------------- /composite-pattern/example2/problem/ingredient.ts: -------------------------------------------------------------------------------- 1 | export class Ingredient { 2 | name: string; 3 | amount: string; 4 | 5 | constructor(name: string, amount: string) { 6 | this.name = name; 7 | this.amount = amount; 8 | } 9 | 10 | showDetails(): string { 11 | return `Ingredient: ${this.name}, Amount: ${this.amount}`; 12 | } 13 | } -------------------------------------------------------------------------------- /composite-pattern/example2/solution/index.ts: -------------------------------------------------------------------------------- 1 | import { Ingredient } from "./ingredient"; 2 | import { Recipe } from "./recipe"; 3 | 4 | // Usage 5 | const ingredient1 = new Ingredient("Flour", "2 cups"); 6 | const ingredient2 = new Ingredient("Eggs", "3"); 7 | const recipe = new Recipe("Cake"); 8 | recipe.addComponent(ingredient1); 9 | recipe.addComponent(ingredient2); 10 | 11 | const ingredient3 = new Ingredient("Milk", "1 cup"); 12 | const compositeRecipe = new Recipe("Cake with Milk"); 13 | compositeRecipe.addComponent(recipe); 14 | compositeRecipe.addComponent(ingredient3); 15 | 16 | console.log(compositeRecipe.showDetails()); 17 | 18 | -------------------------------------------------------------------------------- /composite-pattern/example2/solution/ingredient.ts: -------------------------------------------------------------------------------- 1 | import { RecipeComponent } from "./recipe-component"; 2 | 3 | export class Ingredient implements RecipeComponent { 4 | name: string; 5 | amount: string; 6 | 7 | constructor(name: string, amount: string) { 8 | this.name = name; 9 | this.amount = amount; 10 | } 11 | 12 | showDetails(): string { 13 | return `Ingredient: ${this.name}, Amount: ${this.amount}`; 14 | } 15 | } -------------------------------------------------------------------------------- /composite-pattern/example2/solution/recipe-component.ts: -------------------------------------------------------------------------------- 1 | export interface RecipeComponent { 2 | showDetails(): string; 3 | } 4 | -------------------------------------------------------------------------------- /composite-pattern/example2/solution/recipe.ts: -------------------------------------------------------------------------------- 1 | import { RecipeComponent } from "./recipe-component"; 2 | 3 | export class Recipe implements RecipeComponent { 4 | name: string; 5 | components: RecipeComponent[] = []; 6 | 7 | constructor(name: string) { 8 | this.name = name; 9 | } 10 | 11 | addComponent(component: RecipeComponent): void { 12 | this.components.push(component); 13 | } 14 | 15 | showDetails(): string { 16 | let details = `Recipe: ${this.name}\n`; 17 | 18 | this.components.forEach(component => { 19 | details += ` ${component.showDetails()}\n`; 20 | }); 21 | 22 | return details; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /composite-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /composite-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /composite-pattern/nodemon-example2-solve.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /composite-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "composite-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2:problem": "nodemon --config nodemon-example2-problem.json", 8 | "example2:solve": "nodemon --config nodemon-example2-solve.json" 9 | 10 | }, 11 | "keywords": [ 12 | "composite-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /decorator-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Decorator (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-decorator/).** 4 | -------------------------------------------------------------------------------- /decorator-pattern/example1/decorator-solution-1/component.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Component { 2 | operation(): string; 3 | } 4 | -------------------------------------------------------------------------------- /decorator-pattern/example1/decorator-solution-1/concrete-component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from './component.interface'; 2 | 3 | export class ConcreteComponent implements Component { 4 | public operation(): string { 5 | return 'ConcreteComponent'; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/decorator-solution-1/concrete-decorator-a.ts: -------------------------------------------------------------------------------- 1 | import { Decorator } from './decorator'; 2 | 3 | export class ConcreteDecoratorA extends Decorator { 4 | public operation(): string { 5 | return `ConcreteDecoratorA(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/decorator-solution-1/concrete-decorator-b.ts: -------------------------------------------------------------------------------- 1 | import { Decorator } from './decorator'; 2 | 3 | export class ConcreteDecoratorB extends Decorator { 4 | public operation(): string { 5 | return `ConcreteDecoratorB(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/decorator-solution-1/concrete-decorator-c.ts: -------------------------------------------------------------------------------- 1 | import { Decorator } from './decorator'; 2 | 3 | export class ConcreteDecoratorC extends Decorator { 4 | public operation(): string { 5 | return `ConcreteDecoratorC(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/decorator-solution-1/decorator.ts: -------------------------------------------------------------------------------- 1 | import { Component } from './component.interface'; 2 | 3 | export class Decorator implements Component { 4 | protected component: Component; 5 | 6 | constructor(component: Component) { 7 | this.component = component; 8 | } 9 | 10 | public operation(): string { 11 | return this.component.operation(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /decorator-pattern/example1/problem/component-a.ts: -------------------------------------------------------------------------------- 1 | import { ComponentBase } from './component-base'; 2 | 3 | export class ComponentA extends ComponentBase { 4 | public operation(): string { 5 | return `ComponentA(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/problem/component-b.ts: -------------------------------------------------------------------------------- 1 | import { ComponentBase } from './component-base'; 2 | 3 | export class ComponentB extends ComponentBase { 4 | public operation(): string { 5 | return `ComponentB(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/problem/component-ba.ts: -------------------------------------------------------------------------------- 1 | import { ComponentBase } from './component-base'; 2 | 3 | export class ComponentBA extends ComponentBase { 4 | public operation(): string { 5 | return `ComponentBA(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/problem/component-base.ts: -------------------------------------------------------------------------------- 1 | import { Component } from './component.interface'; 2 | 3 | export class ComponentBase implements Component { 4 | public operation(): string { 5 | return 'ComponentBase'; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/problem/component-c.ts: -------------------------------------------------------------------------------- 1 | import { ComponentBase } from './component-base'; 2 | 3 | export class ComponentC extends ComponentBase { 4 | public operation(): string { 5 | return `ComponentC(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/problem/component-ca.ts: -------------------------------------------------------------------------------- 1 | import { ComponentBase } from './component-base'; 2 | 3 | export class ComponentCA extends ComponentBase { 4 | public operation(): string { 5 | return `ComponentCA(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/problem/component-cba.ts: -------------------------------------------------------------------------------- 1 | import { ComponentBase } from './component-base'; 2 | 3 | export class ComponentCBA extends ComponentBase { 4 | public operation(): string { 5 | return `ComponentCBA(${super.operation()})`; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /decorator-pattern/example1/problem/component.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Component { 2 | operation(): string; 3 | } 4 | -------------------------------------------------------------------------------- /decorator-pattern/example2/decorator-solution/character.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Character { 2 | attack(): number; 3 | defend(): number; 4 | isAlive(): boolean; 5 | setLife(life: number): void; 6 | getLife(): number; 7 | getName(): string; 8 | toString(): string; 9 | receiveHit(attack: number): void; 10 | } 11 | -------------------------------------------------------------------------------- /decorator-pattern/example2/decorator-solution/lord-decorator.ts: -------------------------------------------------------------------------------- 1 | import { Character } from './character.interface'; 2 | import { WhiteWalkerDecorator } from './white-walker-decorator'; 3 | 4 | export class LordDecorator extends WhiteWalkerDecorator { 5 | constructor(character: Character) { 6 | super(character); 7 | character.setLife(1000); 8 | this.power = 3; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /decorator-pattern/example2/decorator-solution/white-walker-decorator.ts: -------------------------------------------------------------------------------- 1 | import { Character } from './character.interface'; 2 | import { CharacterDecorator } from './character-decorator'; 3 | 4 | export class WhiteWalkerDecorator extends CharacterDecorator { 5 | protected power = Math.random(); 6 | 7 | constructor(character: Character) { 8 | super(character); 9 | } 10 | setPower(power: number) { 11 | this.power = power; 12 | } 13 | private whiteWalkerModifier(): number { 14 | return Math.random() * this.power + this.power; 15 | } 16 | attack(): number { 17 | return this.whiteWalkerModifier() * super.attack(); 18 | } 19 | defend(): number { 20 | return this.whiteWalkerModifier() * super.attack(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /decorator-pattern/nodemon-example1-decorator-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/decorator-solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /decorator-pattern/nodemon-example1-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /decorator-pattern/nodemon-example2-decorator-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/decorator-solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /decorator-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "decorator-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1-problem": "nodemon --config nodemon-example1-problem.json", 7 | "example1-decorator-solution-1": "nodemon --config nodemon-example1-decorator-solution-1.json", 8 | "example2-decorator-solution-1": "nodemon --config nodemon-example2-decorator-solution-1.json" 9 | }, 10 | "keywords": [ 11 | "decorator-pattern", 12 | "clean-code" 13 | ], 14 | "author": "Carlos Caballero", 15 | "license": "MIT", 16 | "devDependencies": { 17 | "nodemon": "^1.18.10", 18 | "typescript": "^3.3.3333" 19 | }, 20 | "dependencies": { 21 | "commander": "^2.20.0", 22 | "inquirer": "^6.3.1", 23 | "terminalizer": "^0.6.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /decorator-pattern/render1560346139406.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/decorator-pattern/render1560346139406.gif -------------------------------------------------------------------------------- /decorator-pattern/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es2015", "dom"], 5 | "strict": false, 6 | "declaration": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /demeter/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { FakeObject } from './interfaces/fake-object'; 2 | // Create a person 3 | const person = FakeObject.createAPerson(); 4 | 5 | // Example 1: Violated the Demeter's Law 6 | console.log( 7 | person 8 | .getHouse() // return an House's object 9 | .getAddress() // return an Address's object 10 | .getZipCode() // return a ZipCode Object 11 | ); 12 | 13 | // Example 1: Not violate the Demeter's Law 14 | console.log(person.getZipCode()); 15 | 16 | // Example 2: Violated the Demeter's Law 17 | console.log( 18 | person 19 | .getHouse() // return an House's object 20 | .getAddress() // return an Address's object 21 | .getZipCode() === '56565656' // return a ZipCode Object 22 | ); 23 | 24 | // Example 2: Not violate the Demeter's Law 25 | console.log(person.isZipCode('56565656')); 26 | -------------------------------------------------------------------------------- /demeter/example1/interfaces/address.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IAddress { 2 | getZipCode(): string; 3 | getNumber(): string; 4 | getStreet(): string; 5 | getCity(): string; 6 | getState(): string; 7 | getCounty(): string; 8 | } 9 | -------------------------------------------------------------------------------- /demeter/example1/interfaces/fake-object.ts: -------------------------------------------------------------------------------- 1 | import { Person } from '../models/person.model'; 2 | import { Address } from '../models/address.model'; 3 | import { House } from '../models/house.model'; 4 | 5 | export class FakeObject { 6 | public static createAnAddress() { 7 | return new Address( 8 | '56565656', 9 | '1456', 10 | 'Rua da Aurora', 11 | 'Recife', 12 | 'Pernambuco', 13 | 'Brasil' 14 | ); 15 | } 16 | 17 | public static createAHouse(): House { 18 | return new House(this.createAnAddress(), 'Yellow', 125.7); 19 | } 20 | 21 | public static createAPerson(): Person { 22 | return new Person('Carlos Caballero', this.createAHouse()); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /demeter/example1/interfaces/house.interface.ts: -------------------------------------------------------------------------------- 1 | import { IAddress } from './address.interface'; 2 | 3 | export interface IHouse { 4 | getAddress(): IAddress; 5 | getColor(): string; 6 | getSize(): number; 7 | getZipCode(): string; 8 | } 9 | -------------------------------------------------------------------------------- /demeter/example1/interfaces/person.interface.ts: -------------------------------------------------------------------------------- 1 | import { IHouse } from './house.interface'; 2 | 3 | export interface IPerson { 4 | getName(): String; 5 | getHouse(): IHouse; 6 | getZipCode(): String; 7 | } 8 | -------------------------------------------------------------------------------- /demeter/example1/models/house.model.ts: -------------------------------------------------------------------------------- 1 | import { IHouse } from '../interfaces/house.interface'; 2 | import { IAddress } from '../interfaces/address.interface'; 3 | 4 | export class House implements IHouse { 5 | private address: IAddress; 6 | private color: string; 7 | private size: number; 8 | 9 | public constructor(address: IAddress, color: string, size: number) { 10 | this.address = address; 11 | this.color = color; 12 | this.size = size; 13 | } 14 | public getAddress(): IAddress { 15 | return this.address; 16 | } 17 | 18 | public getColor(): string { 19 | return this.color; 20 | } 21 | 22 | public getSize(): number { 23 | return this.size; 24 | } 25 | 26 | public getZipCode(): string { 27 | return this.address.getZipCode(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /demeter/example1/models/person.model.ts: -------------------------------------------------------------------------------- 1 | import { IPerson } from '../interfaces/person.interface'; 2 | import { IHouse } from '../interfaces/house.interface'; 3 | 4 | export class Person implements IPerson { 5 | private name: string; 6 | private house: IHouse; 7 | 8 | public constructor(name: string, house: IHouse) { 9 | this.name = name; 10 | this.house = house; 11 | } 12 | /* Method's are not neccesary to respect Demeter's Law */ 13 | public getName(): string { 14 | return this.name; 15 | } 16 | public getHouse(): IHouse { 17 | return this.house; 18 | } 19 | /** */ 20 | 21 | public getZipCode(): string { 22 | return this.house.getZipCode(); 23 | } 24 | 25 | public isZipCode(zipCode: string): boolean { 26 | return this.house.getZipCode() === zipCode; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /demeter/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /demeter/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demeter-law", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json" 7 | }, 8 | "keywords": [ 9 | "demeter-law", 10 | "clean-code" 11 | ], 12 | "author": "Carlos Caballero", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "typescript": "^3.3.3333", 16 | "nodemon": "^1.18.10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /facade-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Facade (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-facade/).** -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-1/index.ts: -------------------------------------------------------------------------------- 1 | import { Facade } from './facade'; 2 | 3 | class Client { 4 | facade = new Facade(); 5 | methodClient1() { 6 | this.facade.methodClient1(); 7 | } 8 | 9 | methodClient2() { 10 | this.facade.methodClient2(); 11 | } 12 | } 13 | 14 | const client = new Client(); 15 | client.methodClient1(); 16 | client.methodClient2(); 17 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-1/system1/classA.ts: -------------------------------------------------------------------------------- 1 | export class ClassA { 2 | methodA() { 3 | console.log('methodA from Class A'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-1/system1/classA2.ts: -------------------------------------------------------------------------------- 1 | import { ClassA } from './classA'; 2 | 3 | export class ClassA2 extends ClassA { 4 | methodA2() { 5 | console.log('MethodA2 from ClassA2'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-1/system1/classB.ts: -------------------------------------------------------------------------------- 1 | export class ClassB { 2 | methodB() { 3 | console.log('methodB from ClassB'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-1/system2/classC.ts: -------------------------------------------------------------------------------- 1 | export class ClassC { 2 | methodC() { 3 | console.log('methodC from ClassC'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-1/system2/classC2.ts: -------------------------------------------------------------------------------- 1 | import { ClassC } from './classC'; 2 | 3 | export class ClassC2 extends ClassC {} 4 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-1/system2/classC3.ts: -------------------------------------------------------------------------------- 1 | import { ClassC } from './classC'; 2 | 3 | export class ClassC3 extends ClassC {} 4 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-1/system2/classD.ts: -------------------------------------------------------------------------------- 1 | export class ClassD { 2 | methodD() { 3 | console.log('methodD from ClassD'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/facade.ts: -------------------------------------------------------------------------------- 1 | import { FacadeSystem1 } from './system1/facade-system1'; 2 | import { FacadeSystem2 } from './system2/facade-system2'; 3 | 4 | export class Facade { 5 | private facadeSystem1 = new FacadeSystem1(); 6 | private facadeSystem2 = new FacadeSystem2(); 7 | 8 | methodClient1() { 9 | console.log('\n...methodClient1\n'); 10 | this.facadeSystem1.methodA2(); 11 | this.facadeSystem1.methodB(); 12 | this.facadeSystem2.methodC(); 13 | this.facadeSystem2.methodD(); 14 | } 15 | 16 | methodClient2() { 17 | console.log('\n...methodClient2\n'); 18 | this.facadeSystem1.methodA(); 19 | this.facadeSystem2.methodC3(); 20 | this.facadeSystem2.methodC(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/index.ts: -------------------------------------------------------------------------------- 1 | import { Facade } from './facade'; 2 | 3 | class Client { 4 | facade = new Facade(); 5 | methodClient1() { 6 | this.facade.methodClient1(); 7 | } 8 | 9 | methodClient2() { 10 | this.facade.methodClient2(); 11 | } 12 | } 13 | 14 | const client = new Client(); 15 | client.methodClient1(); 16 | client.methodClient2(); 17 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system1/classA.ts: -------------------------------------------------------------------------------- 1 | export class ClassA { 2 | methodA() { 3 | console.log('methodA from Class A'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system1/classA2.ts: -------------------------------------------------------------------------------- 1 | import { ClassA } from './classA'; 2 | 3 | export class ClassA2 extends ClassA { 4 | methodA2() { 5 | console.log('MethodA2 from ClassA2'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system1/classB.ts: -------------------------------------------------------------------------------- 1 | export class ClassB { 2 | methodB() { 3 | console.log('methodB from ClassB'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system1/facade-system1.ts: -------------------------------------------------------------------------------- 1 | import { ClassA } from './classA'; 2 | import { ClassA2 } from './classA2'; 3 | import { ClassB } from './classB'; 4 | 5 | export class FacadeSystem1 { 6 | private a = new ClassA(); 7 | private a2 = new ClassA2(); 8 | private b = new ClassB(); 9 | 10 | methodA2() { 11 | this.a2.methodA2(); 12 | } 13 | methodA() { 14 | this.a.methodA(); 15 | } 16 | methodB() { 17 | this.b.methodB(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system2/classC.ts: -------------------------------------------------------------------------------- 1 | export class ClassC { 2 | methodC() { 3 | console.log('methodC from ClassC'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system2/classC2.ts: -------------------------------------------------------------------------------- 1 | import { ClassC } from './classC'; 2 | 3 | export class ClassC2 extends ClassC {} 4 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system2/classC3.ts: -------------------------------------------------------------------------------- 1 | import { ClassC } from './classC'; 2 | 3 | export class ClassC3 extends ClassC { 4 | methodC3() { 5 | console.log('methodC3 from ClassC3'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system2/classD.ts: -------------------------------------------------------------------------------- 1 | export class ClassD { 2 | methodD() { 3 | console.log('methodD from ClassD'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/facade-solution-2/system2/facade-system2.ts: -------------------------------------------------------------------------------- 1 | import { ClassC } from './classC'; 2 | import { ClassC3 } from './classC3'; 3 | import { ClassD } from './classD'; 4 | 5 | export class FacadeSystem2 { 6 | private c = new ClassC(); 7 | private c3 = new ClassC3(); 8 | 9 | private d = new ClassD(); 10 | 11 | methodC() { 12 | this.c.methodC(); 13 | } 14 | methodC3() { 15 | this.c3.methodC3(); 16 | } 17 | methodD() { 18 | this.d.methodD(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /facade-pattern/example1/problem/system1/classA.ts: -------------------------------------------------------------------------------- 1 | export class ClassA { 2 | methodA() { 3 | console.log('methodA from Class A'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/problem/system1/classA2.ts: -------------------------------------------------------------------------------- 1 | import { ClassA } from './classA'; 2 | 3 | export class ClassA2 extends ClassA { 4 | methodA2() { 5 | console.log('MethodA2 from ClassA2'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /facade-pattern/example1/problem/system1/classB.ts: -------------------------------------------------------------------------------- 1 | export class ClassB { 2 | methodB() { 3 | console.log('methodB from ClassB'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/problem/system2/classC.ts: -------------------------------------------------------------------------------- 1 | export class ClassC { 2 | methodC() { 3 | console.log('methodC from ClassC'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example1/problem/system2/classC2.ts: -------------------------------------------------------------------------------- 1 | import { ClassC } from './classC'; 2 | 3 | export class ClassC2 extends ClassC {} 4 | -------------------------------------------------------------------------------- /facade-pattern/example1/problem/system2/classC3.ts: -------------------------------------------------------------------------------- 1 | import { ClassC } from './classC'; 2 | 3 | export class ClassC3 extends ClassC { 4 | methodC3() { 5 | console.log('methodC3 from ClassC3'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /facade-pattern/example1/problem/system2/classD.ts: -------------------------------------------------------------------------------- 1 | export class ClassD { 2 | methodD() { 3 | console.log('methodD from ClassD'); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/dragonball/interfaces/pure-race.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PureRace { 2 | genki(): number; 3 | } 4 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/dragonball/models/human-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Human } from './human.model'; 3 | 4 | export class HumanAdapter implements PureRace { 5 | constructor(private human: Human) {} 6 | 7 | public genki(): number { 8 | return this.human.sharedPower(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/dragonball/models/human.model.ts: -------------------------------------------------------------------------------- 1 | export class Human { 2 | public sharedPower(): number { 3 | return 10; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/dragonball/models/namekian-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Namekian } from './namekian.model'; 3 | 4 | export class NamekianAdapter implements PureRace { 5 | constructor(private namekian: Namekian) {} 6 | public genki(): number { 7 | return this.namekian.getPower(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/dragonball/models/namekian.model.ts: -------------------------------------------------------------------------------- 1 | export class Namekian { 2 | public getPower(): number { 3 | return Math.random() * 20 + 20; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/dragonball/models/saiyan-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Saiyan } from './saiyan.model'; 3 | 4 | export class SaiyanAdapter implements PureRace { 5 | constructor(private saiyan: Saiyan) {} 6 | public genki(): number { 7 | return this.saiyan.myPowerPart1() + this.saiyan.myPowerPart2(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/dragonball/models/saiyan.model.ts: -------------------------------------------------------------------------------- 1 | export class Saiyan { 2 | myPowerPart1(): number { 3 | return Math.random() * 100 + 100; 4 | } 5 | myPowerPart2(): number { 6 | return Math.random() * 1000 + 500; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/index.ts: -------------------------------------------------------------------------------- 1 | import { DragonballFacade } from './dragonball/dragonball-facade'; 2 | import { PokemonFacade } from './pokemon/pokemon-facade'; 3 | 4 | // Client/Context - System 1 5 | console.log('DragonBall...\n'); 6 | const dragonballFacade = new DragonballFacade(); 7 | const genki = dragonballFacade.genki(); 8 | console.log(`everybody attack: ${genki}`); 9 | 10 | // Client/Context - System 2 11 | console.log('\nPokemon...\n'); 12 | const pokemonFacade = new PokemonFacade(); 13 | pokemonFacade.calculateDamage('passimian'); 14 | pokemonFacade.calculateDamage('poipole'); 15 | pokemonFacade.calculateDamage('mudsdale'); 16 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/pokemon/fighting-pokemon.model.ts: -------------------------------------------------------------------------------- 1 | import { Pokemon } from './pokemon.model'; 2 | 3 | export class FightingPokemon extends Pokemon { 4 | constructor(_pokemon) { 5 | super(_pokemon); 6 | } 7 | calculateImpact(multipliers) { 8 | return Math.floor((this.attack / this.defense) * multipliers) + 1; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/pokemon/ground-pokemon.model.ts: -------------------------------------------------------------------------------- 1 | import { Pokemon } from './pokemon.model'; 2 | 3 | export class GroundPokemon extends Pokemon { 4 | constructor(_pokemon) { 5 | super(_pokemon); 6 | } 7 | calculateImpact(multipliers) { 8 | return Math.floor((this.attack + this.defense) * multipliers) + 1; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /facade-pattern/example2/facade-solution/pokemon/poison-pokemon.model.ts: -------------------------------------------------------------------------------- 1 | import { Pokemon } from './pokemon.model'; 2 | 3 | export class PoisonPokemon extends Pokemon { 4 | constructor(_pokemon) { 5 | super(_pokemon); 6 | } 7 | calculateImpact(multipliers) { 8 | return Math.floor((this.attack - this.defense) * multipliers) + 1; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/dragonball/interfaces/pure-race.interface.ts: -------------------------------------------------------------------------------- 1 | export interface PureRace { 2 | genki(): number; 3 | } 4 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/dragonball/models/human-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Human } from './human.model'; 3 | 4 | export class HumanAdapter implements PureRace { 5 | constructor(private human: Human) {} 6 | 7 | public genki(): number { 8 | return this.human.sharedPower(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/dragonball/models/human.model.ts: -------------------------------------------------------------------------------- 1 | export class Human { 2 | public sharedPower(): number { 3 | return 10; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/dragonball/models/namekian-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Namekian } from './namekian'; 3 | 4 | export class NamekianAdapter implements PureRace { 5 | constructor(private namekian: Namekian) {} 6 | public genki(): number { 7 | return this.namekian.getPower(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/dragonball/models/namekian.ts: -------------------------------------------------------------------------------- 1 | export class Namekian { 2 | public getPower(): number { 3 | return Math.random() * 20 + 20; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/dragonball/models/saiyan-adapter.model.ts: -------------------------------------------------------------------------------- 1 | import { PureRace } from '../interfaces/pure-race.interface'; 2 | import { Saiyan } from './saiyan.model'; 3 | 4 | export class SaiyanAdapter implements PureRace { 5 | constructor(private saiyan: Saiyan) {} 6 | public genki(): number { 7 | return this.saiyan.myPowerPart1() + this.saiyan.myPowerPart2(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/dragonball/models/saiyan.model.ts: -------------------------------------------------------------------------------- 1 | export class Saiyan { 2 | myPowerPart1(): number { 3 | return Math.random() * 100 + 100; 4 | } 5 | myPowerPart2(): number { 6 | return Math.random() * 1000 + 500; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/pokemon/fighting-pokemon.model.ts: -------------------------------------------------------------------------------- 1 | import { Pokemon } from './pokemon.model'; 2 | 3 | export class FightingPokemon extends Pokemon { 4 | constructor(_pokemon) { 5 | super(_pokemon); 6 | } 7 | calculateImpact(multipliers) { 8 | return Math.floor((this.attack / this.defense) * multipliers) + 1; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/pokemon/ground-pokemon.model.ts: -------------------------------------------------------------------------------- 1 | import { Pokemon } from './pokemon.model'; 2 | 3 | export class GroundPokemon extends Pokemon { 4 | constructor(_pokemon) { 5 | super(_pokemon); 6 | } 7 | calculateImpact(multipliers) { 8 | return Math.floor((this.attack + this.defense) * multipliers) + 1; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /facade-pattern/example2/problem/pokemon/poison-pokemon.model.ts: -------------------------------------------------------------------------------- 1 | import { Pokemon } from './pokemon.model'; 2 | 3 | export class PoisonPokemon extends Pokemon { 4 | constructor(_pokemon) { 5 | super(_pokemon); 6 | } 7 | calculateImpact(multipliers) { 8 | return Math.floor((this.attack - this.defense) * multipliers) + 1; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /facade-pattern/nodemon-example1-facade-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/facade-solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /facade-pattern/nodemon-example1-facade-solution-2.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/facade-solution-2/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /facade-pattern/nodemon-example1-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /facade-pattern/nodemon-example2-facade-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/facade-solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /facade-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /facade-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "facade-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1-problem": "nodemon --config nodemon-example1-problem.json", 7 | "example1-facade-solution-1": "nodemon --config nodemon-example1-facade-solution-1.json", 8 | "example1-facade-solution-2": "nodemon --config nodemon-example1-facade-solution-2.json", 9 | "example2-problem": "nodemon --config nodemon-example2-problem.json", 10 | "example2-facade-solution1": "nodemon --config nodemon-example2-facade-solution-1.json" 11 | }, 12 | "keywords": [ 13 | "facade-pattern", 14 | "clean-code" 15 | ], 16 | "author": "Carlos Caballero", 17 | "license": "MIT", 18 | "devDependencies": { 19 | "nodemon": "^1.18.10", 20 | "typescript": "^3.3.3333" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /factory-method-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Factory Method (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-factory-method/).** 4 | -------------------------------------------------------------------------------- /factory-method-pattern/example1/concrete-creator1.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteProduct1 } from "./concrete-product1"; 2 | import { Creator } from "./creator"; 3 | import { Product } from "./product.interface"; 4 | 5 | export class ConcreteCreator1 extends Creator { 6 | protected factoryMethod(): Product { 7 | return new ConcreteProduct1(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /factory-method-pattern/example1/concrete-creator2.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteProduct2 } from "./concrete-product2"; 2 | import { Creator } from "./creator"; 3 | import { Product } from "./product.interface"; 4 | 5 | export class ConcreteCreator2 extends Creator { 6 | protected factoryMethod(): Product { 7 | return new ConcreteProduct2(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /factory-method-pattern/example1/concrete-product1.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export class ConcreteProduct1 implements Product { 4 | public operation(): string { 5 | return "ConcreteProduct1: Operation"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method-pattern/example1/concrete-product2.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export class ConcreteProduct2 implements Product { 4 | public operation(): string { 5 | return "ConcreteProduct2: Operation"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method-pattern/example1/creator.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export abstract class Creator { 4 | protected abstract factoryMethod(): Product; 5 | 6 | public operation(): string { 7 | const product = this.factoryMethod(); 8 | return `Creator: ${product.operation()}`; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /factory-method-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteCreator1 } from "./concrete-creator1"; 2 | import { ConcreteCreator2 } from "./concrete-creator2"; 3 | import { Creator } from "./creator"; 4 | 5 | function client(creator: Creator) { 6 | console.log(`Client: I'm not aware of the creator's class`); 7 | console.log(creator.operation()); 8 | } 9 | 10 | const concreteCreator1 = new ConcreteCreator1(); 11 | const concreteCreator2 = new ConcreteCreator2(); 12 | 13 | client(concreteCreator1); 14 | 15 | console.log("----------"); 16 | 17 | client(concreteCreator2); 18 | -------------------------------------------------------------------------------- /factory-method-pattern/example1/product.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Product { 2 | operation(): string; 3 | } 4 | -------------------------------------------------------------------------------- /factory-method-pattern/example2/burger.model.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export class Burger implements Product { 4 | public operation(): string { 5 | return "Burger: Results"; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /factory-method-pattern/example2/index.ts: -------------------------------------------------------------------------------- 1 | import { PRODUCT_TYPE } from "./product-type.enum"; 2 | import { ProductManager } from "./product-manager"; 3 | 4 | const productManager = new ProductManager(); 5 | 6 | const burger = productManager.createProduct(PRODUCT_TYPE.BURGER); 7 | const pizza = productManager.createProduct(PRODUCT_TYPE.PIZZA); 8 | const kebab = productManager.createProduct(PRODUCT_TYPE.KEBAB); 9 | 10 | console.log(burger.operation()); 11 | console.log(pizza.operation()); 12 | console.log(kebab.operation()); 13 | -------------------------------------------------------------------------------- /factory-method-pattern/example2/kebab.model.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export class Kebab implements Product { 4 | public operation(): string { 5 | return 'Kebab: Operation'; 6 | } 7 | } -------------------------------------------------------------------------------- /factory-method-pattern/example2/pizza.model.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export class Pizza implements Product { 4 | public operation(): string { 5 | return 'Pizza: Operation'; 6 | } 7 | } -------------------------------------------------------------------------------- /factory-method-pattern/example2/product-manager.ts: -------------------------------------------------------------------------------- 1 | import { Burger } from "./burger.model"; 2 | import { Kebab } from "./kebab.model"; 3 | import { PRODUCT_TYPE } from "./product-type.enum"; 4 | import { Pizza } from "./pizza.model"; 5 | 6 | export class ProductManager { 7 | constructor() {} 8 | createProduct(type): Product { 9 | switch (type) { 10 | case PRODUCT_TYPE.PIZZA: 11 | return new Pizza(); 12 | case PRODUCT_TYPE.KEBAB: 13 | return new Kebab(); 14 | case PRODUCT_TYPE.BURGER: 15 | return new Burger(); 16 | default: 17 | throw new Error("Error: Product invalid!"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /factory-method-pattern/example2/product-type.enum.ts: -------------------------------------------------------------------------------- 1 | export enum PRODUCT_TYPE { 2 | PIZZA = "Pizza", 3 | KEBAB = "Kebab", 4 | BURGER = "Burger", 5 | } 6 | -------------------------------------------------------------------------------- /factory-method-pattern/example2/product.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Product { 2 | operation(): string; 3 | } 4 | -------------------------------------------------------------------------------- /factory-method-pattern/example3/burger-creator.ts: -------------------------------------------------------------------------------- 1 | import { Burger } from "./burger.model"; 2 | import { Creator } from "./creator"; 3 | import { Product } from "./product.interface"; 4 | 5 | export class BurgerCreator extends Creator { 6 | public factoryMethod(): Product { 7 | return new Burger(); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /factory-method-pattern/example3/burger.model.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export class Burger implements Product { 4 | public operation(): string { 5 | return 'Burger: Results'; 6 | } 7 | } -------------------------------------------------------------------------------- /factory-method-pattern/example3/creator.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export abstract class Creator { 4 | 5 | public abstract factoryMethod(): Product; 6 | 7 | public someOperation(): string { 8 | const product = this.factoryMethod(); 9 | return `Creator: The same creator's code has just worked with ${product.operation()}`; 10 | } 11 | } -------------------------------------------------------------------------------- /factory-method-pattern/example3/index.ts: -------------------------------------------------------------------------------- 1 | import { BurgerCreator } from "./burger-creator"; 2 | import { Creator } from "./creator"; 3 | import { KebabCreator } from "./kebab-creator"; 4 | import { PizzaCreator } from "./pizza-creator"; 5 | 6 | function client(creator: Creator) { 7 | console.log('Client: I\'m not aware of the creator\'s class, but it still works.'); 8 | console.log(creator.someOperation()); 9 | } 10 | 11 | const pizzaCreator = new PizzaCreator(); 12 | const burgerCreator = new BurgerCreator(); 13 | const kebabCreator = new KebabCreator(); 14 | 15 | 16 | console.log('App: Launched with the PizzaCreator'); 17 | client(pizzaCreator); 18 | 19 | console.log('----------'); 20 | 21 | console.log('App: Launched with the BurgerCreator'); 22 | client(burgerCreator); -------------------------------------------------------------------------------- /factory-method-pattern/example3/kebab-creator.ts: -------------------------------------------------------------------------------- 1 | import { Creator } from "./creator"; 2 | import { Kebab } from "./kebab.model"; 3 | import { Product } from "./product.interface"; 4 | 5 | export class KebabCreator extends Creator { 6 | public factoryMethod(): Product { 7 | return new Kebab(); 8 | } 9 | } -------------------------------------------------------------------------------- /factory-method-pattern/example3/kebab.model.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export class Kebab implements Product { 4 | public operation(): string { 5 | return 'Kebab: Operation'; 6 | } 7 | } -------------------------------------------------------------------------------- /factory-method-pattern/example3/pizza-creator.ts: -------------------------------------------------------------------------------- 1 | import { Creator } from "./creator"; 2 | import { Pizza } from "./pizza.model"; 3 | import { Product } from "./product.interface"; 4 | 5 | export class PizzaCreator extends Creator { 6 | public factoryMethod(): Product { 7 | return new Pizza(); 8 | } 9 | } -------------------------------------------------------------------------------- /factory-method-pattern/example3/pizza.model.ts: -------------------------------------------------------------------------------- 1 | import { Product } from "./product.interface"; 2 | 3 | export class Pizza implements Product { 4 | public operation(): string { 5 | return 'Pizza: Operation'; 6 | } 7 | } -------------------------------------------------------------------------------- /factory-method-pattern/example3/product.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Product { 2 | operation(): string; 3 | } -------------------------------------------------------------------------------- /factory-method-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /factory-method-pattern/nodemon-example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /factory-method-pattern/nodemon-example3.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example3"], 3 | "ext": "ts", 4 | "ignore": ["example3/**/*.spec.ts"], 5 | "exec": "ts-node ./example3/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /factory-method-pattern/nodemon-example4.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example4"], 3 | "ext": "ts", 4 | "ignore": ["example4/**/*.spec.ts"], 5 | "exec": "ts-node ./example4/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /factory-method-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "factory-method-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2": "nodemon --config nodemon-example2.json", 8 | "example3": "nodemon --config nodemon-example3.json", 9 | "example4": "nodemon --config nodemon-example4.json" 10 | }, 11 | "keywords": [ 12 | "factory-method-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /flyweight-pattern/example1/concrete-flyweight.ts: -------------------------------------------------------------------------------- 1 | import { Flyweight } from "./flyweight"; 2 | 3 | export class ConcreteFlyweight implements Flyweight { 4 | private intrinsicState: string; 5 | 6 | constructor(intrinsicState: string) { 7 | this.intrinsicState = intrinsicState; 8 | } 9 | 10 | public operation(extrinsicState: any): void { 11 | console.log(`ConcreteFlyweight: Intrinsic State = ${this.intrinsicState}, Extrinsic State = ${extrinsicState}`); 12 | } 13 | } -------------------------------------------------------------------------------- /flyweight-pattern/example1/flyweight.ts: -------------------------------------------------------------------------------- 1 | // Flyweight Interface 2 | export interface Flyweight { 3 | operation(extrinsicState: any): void; 4 | } -------------------------------------------------------------------------------- /flyweight-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { FlyweightFactory } from "./flyweight-factory"; 2 | import { UnsharedConcreteFlyweight } from "./unshared-flyweight"; 3 | 4 | const factory = new FlyweightFactory(); 5 | 6 | // Create and use shared flyweights 7 | const flyweight1 = factory.getFlyweight('A'); 8 | flyweight1.operation('First Call'); 9 | 10 | const flyweight2 = factory.getFlyweight('B'); 11 | flyweight2.operation('Second Call'); 12 | 13 | const flyweight3 = factory.getFlyweight('A'); 14 | flyweight3.operation('Third Call'); 15 | 16 | // Create and use unshared flyweights 17 | const unsharedFlyweight1 = new UnsharedConcreteFlyweight('Unique State'); 18 | unsharedFlyweight1.operation('Fourth Call'); 19 | 20 | factory.listFlyweights(); 21 | 22 | -------------------------------------------------------------------------------- /flyweight-pattern/example1/unshared-flyweight.ts: -------------------------------------------------------------------------------- 1 | import { Flyweight } from "./flyweight"; 2 | 3 | // Unshared Concrete Flyweight 4 | export class UnsharedConcreteFlyweight implements Flyweight { 5 | private allState: string; 6 | 7 | constructor(allState: string) { 8 | this.allState = allState; 9 | } 10 | 11 | public operation(extrinsicState: any): void { 12 | console.log(`UnsharedConcreteFlyweight: All State = ${this.allState}, Extrinsic State = ${extrinsicState}`); 13 | } 14 | } -------------------------------------------------------------------------------- /flyweight-pattern/example2/problem/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './position.interface'; 2 | export * from './stats.interface'; 3 | export * from './team.interface'; -------------------------------------------------------------------------------- /flyweight-pattern/example2/problem/interfaces/position.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IPosition { 2 | name: string; 3 | role: string; 4 | }; -------------------------------------------------------------------------------- /flyweight-pattern/example2/problem/interfaces/stats.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IStats { 2 | goals: number; 3 | assists: number; 4 | passes: number; 5 | }; -------------------------------------------------------------------------------- /flyweight-pattern/example2/problem/interfaces/team.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ITeam { 2 | name: string; 3 | coach: string; 4 | }; -------------------------------------------------------------------------------- /flyweight-pattern/example2/problem/models/index.ts: -------------------------------------------------------------------------------- 1 | export { Player } from './player'; 2 | export { Position } from './position'; 3 | export { Stats } from './stats'; 4 | export { Team } from './team'; -------------------------------------------------------------------------------- /flyweight-pattern/example2/problem/models/position.ts: -------------------------------------------------------------------------------- 1 | export class Position { 2 | constructor(public name: string, public role: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /flyweight-pattern/example2/problem/models/stats.ts: -------------------------------------------------------------------------------- 1 | export class Stats { 2 | constructor(public goals: number, public assists: number, public matches: number) {} 3 | } -------------------------------------------------------------------------------- /flyweight-pattern/example2/problem/models/team.ts: -------------------------------------------------------------------------------- 1 | export class Team { 2 | constructor(public name: string, public coach: string) {} 3 | } 4 | -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/flyweight.factory.ts: -------------------------------------------------------------------------------- 1 | import { Flyweight } from "./flyweight"; 2 | 3 | // Flyweight Factory 4 | export abstract class FlyweightFactory { 5 | protected flyweights: Map = new Map(); 6 | 7 | public abstract getFlyweight(key: T): Flyweight; 8 | 9 | public listFlyweights(): void { 10 | console.log(`FlyweightFactory: I have ${this.flyweights.size} flyweights:`); 11 | for (const [key, _] of this.flyweights) { 12 | console.log(key); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/flyweight.ts: -------------------------------------------------------------------------------- 1 | // Flyweight Interfaces 2 | export interface Flyweight { 3 | display(extrinsicState: any): void; 4 | } 5 | -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | export * from './position.interface'; 2 | export * from './stats.interface'; 3 | export * from './team.interface'; -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/interfaces/position.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IPosition { 2 | name: string; 3 | role: string; 4 | }; -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/interfaces/stats.interface.ts: -------------------------------------------------------------------------------- 1 | export interface IStats { 2 | goals: number; 3 | assists: number; 4 | passes: number; 5 | }; -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/interfaces/team.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ITeam { 2 | name: string; 3 | coach: string; 4 | }; -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/models/index.ts: -------------------------------------------------------------------------------- 1 | export { Player } from "./player"; 2 | export { Position } from "./position"; 3 | export { Stats } from "./stats"; 4 | export { Team } from "./team"; -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/models/position.ts: -------------------------------------------------------------------------------- 1 | export class Position { 2 | constructor(public name: string, public role: string) {} 3 | } -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/models/stats.ts: -------------------------------------------------------------------------------- 1 | export class Stats { 2 | constructor(public goals: number, public assists: number, public matches: number) {} 3 | } -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/models/team.ts: -------------------------------------------------------------------------------- 1 | export class Team { 2 | constructor(public name: string, public coach: string) {} 3 | } -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/position-flyweight.ts: -------------------------------------------------------------------------------- 1 | import { Flyweight } from "./flyweight"; 2 | import { Position } from "./models"; 3 | 4 | // Concrete Flyweights 5 | export class PositionFlyweight implements Flyweight { 6 | #position: Position; // Intrinsic State 7 | 8 | constructor(position: Position) { 9 | this.#position = position; 10 | } 11 | 12 | public display(extrinsicState: any): void { 13 | console.log(`Position: ${this.#position.name} (${this.#position.role})`); 14 | } 15 | 16 | public position(): Position { 17 | return this.#position; 18 | } 19 | } -------------------------------------------------------------------------------- /flyweight-pattern/example2/solution/team-flyweight.ts: -------------------------------------------------------------------------------- 1 | import { Flyweight } from "./flyweight"; 2 | import { Team } from "./models"; 3 | 4 | // Concrete Flyweights 5 | export class TeamFlyweight implements Flyweight { 6 | #team: Team; // Intrinsic State 7 | 8 | constructor(team: Team) { 9 | this.#team = team; 10 | } 11 | 12 | public display(extrinsicState: any): void { 13 | console.log(`Team: ${this.#team.name} (${this.#team.coach})`); 14 | } 15 | 16 | public team(): Team { 17 | return this.#team; 18 | } 19 | } -------------------------------------------------------------------------------- /flyweight-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /flyweight-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /flyweight-pattern/nodemon-example2-solve.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /flyweight-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flyweight-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2:problem": "nodemon --config nodemon-example2-problem.json", 8 | "example2:solve": "nodemon --config nodemon-example2-solve.json" 9 | 10 | }, 11 | "keywords": [ 12 | "flyweight-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /iterator-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Facade (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-facade/).** -------------------------------------------------------------------------------- /iterator-pattern/example1/iterator-solution-1/agregator.interface.ts: -------------------------------------------------------------------------------- 1 | import { Iterator } from './iterator.interface'; 2 | 3 | export interface Aggregator { 4 | getIterator(): Iterator; 5 | getReverseIterator(): Iterator; 6 | } 7 | -------------------------------------------------------------------------------- /iterator-pattern/example1/iterator-solution-1/index.ts: -------------------------------------------------------------------------------- 1 | import { WordsCollection } from './words-collection'; 2 | 3 | const collection = new WordsCollection(); 4 | collection.addItem('First'); 5 | collection.addItem('Second'); 6 | collection.addItem('Third'); 7 | 8 | const iterator = collection.getIterator(); 9 | 10 | console.log('Straight traversal:'); 11 | while (!iterator.hasMoreElements()) { 12 | console.log(iterator.next()); 13 | } 14 | 15 | console.log(''); 16 | console.log('Reverse traversal:'); 17 | const reverseIterator = collection.getReverseIterator(); 18 | while (!reverseIterator.hasMoreElements()) { 19 | console.log(reverseIterator.next()); 20 | } 21 | -------------------------------------------------------------------------------- /iterator-pattern/example1/iterator-solution-1/iterator.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Iterator { 2 | // Return the current element. 3 | current(): T; 4 | 5 | // Return the current element and move forward to next element. 6 | next(): T; 7 | 8 | // Return the key of the current element. 9 | key(): number; 10 | 11 | // Checks if current position is valid. 12 | hasMoreElements(): boolean; 13 | 14 | // Rewind the Iterator to the first element. 15 | rewind(): void; 16 | } 17 | -------------------------------------------------------------------------------- /iterator-pattern/example1/problem/example1.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | class Client {} 3 | class WordsCollection { 4 | -items: string[] = [] 5 | 6 | +getItems(): string[] 7 | +addItem(item: string): void 8 | } 9 | 10 | Client .> WordsCollection : "<>" 11 | 12 | note left of Client 13 | 14 | { 15 | ... 16 | items = collection.getItems(); 17 | for(...) { 18 | console.log(items[i]); 19 | } 20 | ... 21 | } 22 | end note 23 | 24 | @enduml 25 | -------------------------------------------------------------------------------- /iterator-pattern/example1/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { WordsCollection } from './words-collection'; 2 | 3 | const collection = new WordsCollection(); 4 | collection.addItem('First'); 5 | collection.addItem('Second'); 6 | collection.addItem('Third'); 7 | 8 | const items = collection.getItems(); 9 | const elements = items.length; 10 | 11 | console.log('Straight traversal:'); 12 | for (let i = 0; i <= elements; i++) { 13 | console.log(items[i]); 14 | } 15 | 16 | console.log(''); 17 | console.log('Reverse traversal:'); 18 | for (let i = elements; i > 0; i--) { 19 | console.log(items[i]); 20 | } 21 | -------------------------------------------------------------------------------- /iterator-pattern/example1/problem/words-collection.ts: -------------------------------------------------------------------------------- 1 | export class WordsCollection { 2 | private items: string[] = []; 3 | 4 | public getItems(): string[] { 5 | return this.items; 6 | } 7 | 8 | public addItem(item: string): void { 9 | this.items.push(item); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /iterator-pattern/example2/iterator-solution/profile-iterator.interface.ts: -------------------------------------------------------------------------------- 1 | import { Profile } from './profile.class'; 2 | 3 | export interface ProfileIterator { 4 | hasNext(): boolean; 5 | getNext(): Profile; 6 | reset(): void; 7 | } 8 | -------------------------------------------------------------------------------- /iterator-pattern/example2/iterator-solution/social-network.interface.ts: -------------------------------------------------------------------------------- 1 | import { ProfileIterator } from './profile-iterator.interface'; 2 | 3 | export interface SocialNetwork { 4 | createFriendsIterator(profileEmail: string): ProfileIterator; 5 | createCoworkersIterator(profileEmail: string): ProfileIterator; 6 | } 7 | -------------------------------------------------------------------------------- /iterator-pattern/nodemon-example1-iterator-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/iterator-solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /iterator-pattern/nodemon-example1-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /iterator-pattern/nodemon-example2-iterator-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/iterator-solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /iterator-pattern/render1560345403864.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/iterator-pattern/render1560345403864.gif -------------------------------------------------------------------------------- /iterator-pattern/render1560346139406.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/iterator-pattern/render1560346139406.gif -------------------------------------------------------------------------------- /iterator-pattern/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["es2015", "dom"], 5 | "strict": false, 6 | "declaration": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /iterators-javascript/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Facade (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-facade/).** -------------------------------------------------------------------------------- /iterators-javascript/example1/iterator-solution-1/agregator.interface.ts: -------------------------------------------------------------------------------- 1 | import { CustomIterator } from './iterator.interface'; 2 | 3 | export interface Aggregator { 4 | getIterator(): CustomIterator; 5 | getReverseIterator(): CustomIterator; 6 | } 7 | -------------------------------------------------------------------------------- /iterators-javascript/example1/iterator-solution-1/iterator.interface.ts: -------------------------------------------------------------------------------- 1 | export interface CustomIterator { 2 | // Return the current element. 3 | current(): T; 4 | 5 | // Return the current element and move forward to next element. 6 | next(): T; 7 | 8 | // Checks if current position is valid. 9 | hasMoreElements(): boolean; 10 | } 11 | -------------------------------------------------------------------------------- /iterators-javascript/example1/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { WordsCollection } from './words-collection'; 2 | 3 | const collection = new WordsCollection(); 4 | collection.addItem('First'); 5 | collection.addItem('Second'); 6 | collection.addItem('Third'); 7 | 8 | const items = collection.getItems(); 9 | const elements = items.length; 10 | 11 | console.log('Straight traversal:'); 12 | for (let i = 0; i <= elements; i++) { 13 | console.log(items[i]); 14 | } 15 | 16 | console.log(''); 17 | console.log('Reverse traversal:'); 18 | for (let i = elements; i > 0; i--) { 19 | console.log(items[i]); 20 | } 21 | -------------------------------------------------------------------------------- /iterators-javascript/example1/problem/words-collection.ts: -------------------------------------------------------------------------------- 1 | export class WordsCollection { 2 | private items: string[] = []; 3 | 4 | public getItems(): string[] { 5 | return this.items; 6 | } 7 | 8 | public addItem(item: string): void { 9 | this.items.push(item); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /iterators-javascript/example2/iterator-solution/profile-iterator.interface.ts: -------------------------------------------------------------------------------- 1 | import { Profile } from './profile.class'; 2 | 3 | export interface ProfileIterator { 4 | hasNext(): boolean; 5 | next(): Profile; 6 | reset(): void; 7 | } 8 | -------------------------------------------------------------------------------- /iterators-javascript/example2/iterator-solution/social-network.interface.ts: -------------------------------------------------------------------------------- 1 | import { Profile } from './profile.class'; 2 | import { ProfileIterator } from './profile-iterator.interface'; 3 | 4 | export interface SocialNetwork { 5 | createFriendsIterator(profileEmail: string): ProfileIterator; 6 | createCoworkersIterator(profileEmail: string): ProfileIterator; 7 | createNativeIterator(type: string, profileEmail: string): Iterable; 8 | } 9 | -------------------------------------------------------------------------------- /iterators-javascript/nodemon-example1-iterator-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/iterator-solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /iterators-javascript/nodemon-example1-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /iterators-javascript/nodemon-example2-iterator-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/iterator-solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /mediator-pattern/example1/colleague.ts: -------------------------------------------------------------------------------- 1 | import { Mediator } from "./mediator"; 2 | 3 | export interface Colleague { 4 | setMediator(mediator: Mediator): void; 5 | action(): void; 6 | } -------------------------------------------------------------------------------- /mediator-pattern/example1/colleague1.ts: -------------------------------------------------------------------------------- 1 | import { Colleague } from "./colleague"; 2 | import { Mediator } from "./mediator"; 3 | 4 | export class Colleague1 implements Colleague { 5 | private mediator: Mediator; 6 | 7 | setMediator(mediator: Mediator): void { 8 | this.mediator = mediator; 9 | } 10 | 11 | action(): void { 12 | this.mediator.notify(this, "Event from Colleague1"); 13 | } 14 | } -------------------------------------------------------------------------------- /mediator-pattern/example1/colleague2.ts: -------------------------------------------------------------------------------- 1 | import { Colleague } from "./colleague"; 2 | import { Mediator } from "./mediator"; 3 | 4 | export class Colleague2 implements Colleague { 5 | private mediator: Mediator; 6 | 7 | setMediator(mediator: Mediator): void { 8 | this.mediator = mediator; 9 | } 10 | 11 | action(): void { 12 | this.mediator.notify(this, "Event from Colleague2"); 13 | } 14 | } -------------------------------------------------------------------------------- /mediator-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { Colleague1 } from "./colleague1"; 2 | import { Colleague2 } from "./colleague2"; 3 | import { ConcreteMediator } from "./concrete-mediator"; 4 | 5 | const colleague1 = new Colleague1(); 6 | const colleague2 = new Colleague2(); 7 | 8 | const mediator = new ConcreteMediator(colleague1, colleague2); 9 | 10 | colleague1.setMediator(mediator); 11 | colleague2.setMediator(mediator); 12 | 13 | colleague1.action(); 14 | colleague2.action(); -------------------------------------------------------------------------------- /mediator-pattern/example1/mediator.ts: -------------------------------------------------------------------------------- 1 | import { Colleague } from "./colleague"; 2 | 3 | export interface Mediator { 4 | notify(sender: Colleague, event: string): void; 5 | } 6 | -------------------------------------------------------------------------------- /mediator-pattern/example2/solution/actuator.ts: -------------------------------------------------------------------------------- 1 | import { IoTDevice } from "./iot-device"; 2 | import { IoTMediator } from "./iot-mediator"; 3 | 4 | export class Actuator extends IoTDevice { 5 | constructor(id: string, mediator: IoTMediator) { 6 | super(id, mediator); 7 | } 8 | 9 | // Method to receive a control signal from another device 10 | receiveControlSignal(senderId: string, signal: string): void { 11 | this.mediator.receiveControlSignal(this, senderId, signal); 12 | } 13 | } -------------------------------------------------------------------------------- /mediator-pattern/example2/solution/index.ts: -------------------------------------------------------------------------------- 1 | import { Actuator } from "./actuator"; 2 | import { ConcreteIoTMediator } from "./concrete-iot-mediator"; 3 | import { Sensor } from "./sensor"; 4 | // Create the Mediator 5 | const mediator = new ConcreteIoTMediator(); 6 | 7 | // Create IoT devices 8 | const sensor1 = new Sensor("Sensor1", mediator); 9 | const sensor2 = new Sensor("Sensor2", mediator); 10 | const actuator1 = new Actuator("Actuator1", mediator); 11 | 12 | // Example interaction between devices 13 | sensor1.sendMessage("Sensor2", "How are you?"); 14 | sensor2.sendMeasurement("Actuator1", { temperature: 25, humidity: 60 }); 15 | actuator1.receiveControlSignal("Sensor2", "Turn off"); 16 | 17 | sensor1.sendMessage("Invalid-sensor", "How are you?"); 18 | -------------------------------------------------------------------------------- /mediator-pattern/example2/solution/iot-mediator.ts: -------------------------------------------------------------------------------- 1 | import { IoTDevice } from "./iot-device"; 2 | 3 | export interface IoTMediator { 4 | registerDevice(device: IoTDevice): void; 5 | sendMessage(sender: IoTDevice, receiverId: string, message: string): void; 6 | sendMeasurement(sender: IoTDevice, receiverId: string, data: any): void; 7 | receiveMessage(receiver: IoTDevice, senderId: string, message: string): void; 8 | receiveMeasurement(receiver: IoTDevice, senderId: string, data: any): void; 9 | receiveControlSignal(receiver: IoTDevice, senderId: string, signal: string): void; 10 | } 11 | -------------------------------------------------------------------------------- /mediator-pattern/example2/solution/sensor.ts: -------------------------------------------------------------------------------- 1 | import { IoTDevice } from "./iot-device"; 2 | import { IoTMediator } from "./iot-mediator"; 3 | 4 | export class Sensor extends IoTDevice { 5 | constructor(id: string, mediator: IoTMediator) { 6 | super(id, mediator); 7 | } 8 | } -------------------------------------------------------------------------------- /mediator-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /mediator-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /mediator-pattern/nodemon-example2-solve.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /mediator-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mediator-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2:problem": "nodemon --config nodemon-example2-problem.json", 8 | "example2:solve": "nodemon --config nodemon-example2-solve.json" 9 | 10 | }, 11 | "keywords": [ 12 | "mediator-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /memoization/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Parcel Sandbox 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /null-object-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Facade (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-command/).** 4 | -------------------------------------------------------------------------------- /null-object-pattern/example1/null-object-solution-1/index.ts: -------------------------------------------------------------------------------- 1 | import { SaiyanFactory } from './saiyan-factory.class'; 2 | 3 | const saiyanFactory = new SaiyanFactory(); 4 | const saiyan1 = saiyanFactory.getSaiyan('Vegeta'); 5 | const saiyan2 = saiyanFactory.getSaiyan('Bob'); 6 | const saiyan3 = saiyanFactory.getSaiyan('Son Goku'); 7 | const saiyan4 = saiyanFactory.getSaiyan('Laura'); 8 | 9 | console.log('Saiyan'); 10 | console.log(saiyan1.toString()); 11 | console.log(saiyan2.toString()); 12 | console.log(saiyan3.toString()); 13 | console.log(saiyan4.toString()); 14 | -------------------------------------------------------------------------------- /null-object-pattern/example1/null-object-solution-1/null-saiyan.class.ts: -------------------------------------------------------------------------------- 1 | import { AbstractSaiyan } from './saiyan.class'; 2 | 3 | export class NullSaiyan extends AbstractSaiyan { 4 | public getName(): string { 5 | return 'Not Available in Saiyan Database'; 6 | } 7 | toString(): string { 8 | return 'Not Available in Saiyan Database'; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /null-object-pattern/example1/null-object-solution-1/real-saiyan.class.ts: -------------------------------------------------------------------------------- 1 | import { AbstractSaiyan } from './saiyan.class'; 2 | import { Saiyan } from './saiyan.interface'; 3 | 4 | export class RealSaiyan extends AbstractSaiyan { 5 | constructor({ name, power }: Saiyan) { 6 | super(); 7 | this.name = name; 8 | this.power = power; 9 | } 10 | 11 | getName(): string { 12 | return this.name; 13 | } 14 | toString(): string { 15 | return `${this.name} - ${this.power}`; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /null-object-pattern/example1/null-object-solution-1/saiyan-factory.class.ts: -------------------------------------------------------------------------------- 1 | import { AbstractSaiyan } from './saiyan.class'; 2 | import { NullSaiyan } from './null-saiyan.class'; 3 | import { RealSaiyan } from './real-saiyan.class'; 4 | 5 | export class SaiyanFactory { 6 | public saiyans = [ 7 | { name: 'Son Goku', power: 1000 }, 8 | { name: 'Son Gohan', power: 800 }, 9 | { name: 'Vegeta', power: 950 }, 10 | ]; 11 | 12 | public getSaiyan(name: string): AbstractSaiyan { 13 | for (const saiyan of this.saiyans) { 14 | if (saiyan.name === name) { 15 | return new RealSaiyan(saiyan); 16 | } 17 | } 18 | return new NullSaiyan(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /null-object-pattern/example1/null-object-solution-1/saiyan.class.ts: -------------------------------------------------------------------------------- 1 | export abstract class AbstractSaiyan { 2 | protected name: string; 3 | protected power: number; 4 | public abstract getName(): string; 5 | public abstract toString(): string; 6 | } 7 | -------------------------------------------------------------------------------- /null-object-pattern/example1/null-object-solution-1/saiyan.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Saiyan { 2 | name: string; 3 | power: number; 4 | } 5 | -------------------------------------------------------------------------------- /null-object-pattern/example1/problem/saiyan-factory.class.ts: -------------------------------------------------------------------------------- 1 | import { Saiyan } from './saiyan.class'; 2 | 3 | export class SaiyanFactory { 4 | public saiyans = [ 5 | { name: 'Son Goku', power: 1000 }, 6 | { name: 'Son Gohan', power: 800 }, 7 | { name: 'Vegeta', power: 950 }, 8 | ]; 9 | 10 | public getSaiyan(name: string): Saiyan | null { 11 | // Mock Database find 12 | for (const saiyan of this.saiyans) { 13 | if (saiyan.name === name) { 14 | return new Saiyan(saiyan); 15 | } 16 | } 17 | return null; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /null-object-pattern/example1/problem/saiyan.class.ts: -------------------------------------------------------------------------------- 1 | import { ISaiyan } from './saiyan.interface'; 2 | 3 | export class Saiyan { 4 | protected name: string; 5 | protected power: number; 6 | 7 | constructor({ name, power }: ISaiyan) { 8 | this.name = name; 9 | this.power = power; 10 | } 11 | getName(): string { 12 | return this.name; 13 | } 14 | 15 | public toString(): string { 16 | return `${this.name} - ${this.power}`; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /null-object-pattern/example1/problem/saiyan.interface.ts: -------------------------------------------------------------------------------- 1 | export interface ISaiyan { 2 | name: string; 3 | power: number; 4 | } 5 | -------------------------------------------------------------------------------- /null-object-pattern/nodemon-example1-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /null-object-pattern/nodemon-example1-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/null-object-solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /null-object-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "null-object-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1-problem": "nodemon --config nodemon-example1-problem.json", 7 | "example1-solution-1": "nodemon --config nodemon-example1-solution-1.json" 8 | }, 9 | "keywords": [ 10 | "pattern", 11 | "clean-code" 12 | ], 13 | "author": "Carlos Caballero", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "nodemon": "^1.18.10", 17 | "ts-node": "^8.5.4", 18 | "typescript": "^3.3.3333" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /observer-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Observer (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/).** 4 | -------------------------------------------------------------------------------- /observer-pattern/example1/concrete-observerA.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteSubject } from "./concrete-subject"; 2 | import { Observer } from "./observer.interface"; 3 | import { Subject } from "./subject.interface"; 4 | 5 | export class ConcreteObserverA implements Observer { 6 | public update(subject: Subject): void { 7 | if (subject instanceof ConcreteSubject && subject.state < 3) { 8 | console.log("ConcreteObserverA: Reacted to the event."); 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /observer-pattern/example1/concrete-observerB.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteSubject } from "./concrete-subject"; 2 | import { Observer } from "./observer.interface"; 3 | import { Subject } from "./subject.interface"; 4 | 5 | export class ConcreteObserverB implements Observer { 6 | public update(subject: Subject): void { 7 | if ( 8 | subject instanceof ConcreteSubject && 9 | (subject.state === 0 || subject.state >= 2) 10 | ) { 11 | console.log("ConcreteObserverB: Reacted to the event."); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /observer-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteObserverA } from "./concrete-observerA"; 2 | import { ConcreteObserverB } from "./concrete-observerB"; 3 | import { ConcreteSubject } from "./concrete-subject"; 4 | 5 | const subject = new ConcreteSubject(); 6 | 7 | const observer1 = new ConcreteObserverA(); 8 | subject.attach(observer1); 9 | 10 | const observer2 = new ConcreteObserverB(); 11 | subject.attach(observer2); 12 | 13 | subject.operation(); 14 | subject.operation(); 15 | 16 | subject.detach(observer2); 17 | 18 | subject.operation(); 19 | -------------------------------------------------------------------------------- /observer-pattern/example1/observer.interface.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from "./subject.interface"; 2 | 3 | export interface Observer { 4 | update(subject: Subject): void; 5 | } 6 | -------------------------------------------------------------------------------- /observer-pattern/example1/subject.interface.ts: -------------------------------------------------------------------------------- 1 | import { Observer } from "./observer.interface"; 2 | 3 | export interface Subject { 4 | attach(observer: Observer): void; 5 | detach(observer: Observer): void; 6 | notify(): void; 7 | } 8 | -------------------------------------------------------------------------------- /observer-pattern/example2/agent.interface.ts: -------------------------------------------------------------------------------- 1 | import { Auctioneer } from "./auctioneer.interface"; 2 | 3 | export interface Agent { 4 | subscribe(auctioneer: Auctioneer): void; 5 | unsubscribe(auctioneer: Auctioneer): void; 6 | notify(): void; 7 | } 8 | -------------------------------------------------------------------------------- /observer-pattern/example2/auctioneer.interface.ts: -------------------------------------------------------------------------------- 1 | import { Agent } from "./agent.interface"; 2 | 3 | export interface Auctioneer { 4 | name: string; 5 | MAX_LIMIT: number; 6 | update(agent: Agent): void; 7 | } 8 | -------------------------------------------------------------------------------- /observer-pattern/example2/product.model.ts: -------------------------------------------------------------------------------- 1 | import { Auctioneer } from "./auctioneer.interface"; 2 | 3 | export class Product { 4 | public price; 5 | public name; 6 | public auctionner: Auctioneer = null; 7 | 8 | constructor(product) { 9 | this.price = product.price || 10; 10 | this.name = product.name || "Unknown"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /observer-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /observer-pattern/nodemon-example2.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /observer-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "observer-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2": "nodemon --config nodemon-example2.json", 8 | "example3": "nodemon --config nodemon-example3.json", 9 | "example4": "nodemon --config nodemon-example4.json" 10 | }, 11 | "keywords": [ 12 | "observer-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /observer-pattern/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "es7"], 4 | 5 | "target": "es5" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /prototype-pattern/example1/concrete-prototype2.ts: -------------------------------------------------------------------------------- 1 | import { Prototype } from "./prototype"; 2 | 3 | export class ConcretePrototype2 implements Prototype { 4 | operation() { 5 | console.log("Operation from ConcretePrototype2"); 6 | } 7 | 8 | clone(): Prototype { 9 | return new ConcretePrototype2(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /prototype-pattern/example1/concrete.prototype1.ts: -------------------------------------------------------------------------------- 1 | import { Prototype } from "./prototype"; 2 | 3 | export class ConcretePrototype1 implements Prototype { 4 | operation() { 5 | console.log("Operation from ConcretePrototype1"); 6 | } 7 | 8 | clone(): Prototype { 9 | return new ConcretePrototype1(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /prototype-pattern/example1/index.ts: -------------------------------------------------------------------------------- 1 | import { ConcretePrototype1 } from "./concrete.prototype1"; 2 | import { ConcretePrototype2 } from "./concrete-prototype2"; 3 | 4 | // Create instances of ConcretePrototype1 and ConcretePrototype2 5 | const concretePrototype1 = new ConcretePrototype1(); 6 | const concretePrototype2 = new ConcretePrototype2(); 7 | 8 | // The operation method is called on the instances 9 | concretePrototype1.operation(); 10 | concretePrototype2.operation(); 11 | 12 | // Cloning the instances 13 | const clonedPrototype1 = concretePrototype1.clone(); 14 | const clonedPrototype2 = concretePrototype2.clone(); 15 | 16 | // Show the information of the cloned instances 17 | clonedPrototype1.operation(); 18 | clonedPrototype2.operation(); 19 | 20 | -------------------------------------------------------------------------------- /prototype-pattern/example1/prototype.ts: -------------------------------------------------------------------------------- 1 | export interface Prototype { 2 | operation(): void; 3 | clone(): Prototype; 4 | } -------------------------------------------------------------------------------- /prototype-pattern/example2/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { VirtualPet } from './virtual-pet'; 2 | 3 | // Create a virtual pet and initialize it 4 | const neko = new VirtualPet('Neko', 'Gray', ['Fly', 'Breath Fire', 'Invisibility']); // 3 seconds 5 | neko.displayInfo(); 6 | 7 | 8 | // Clone the virtual pet manually with all the attributes 9 | const nya = new VirtualPet('Nya', 'Yellow', [...neko.abilities]); // 3 seconds 10 | nya.displayInfo(); 11 | 12 | -------------------------------------------------------------------------------- /prototype-pattern/example2/problem/uml.puml: -------------------------------------------------------------------------------- 1 | 2 | @startuml 3 | skinparam titleBorderRoundCorner 15 4 | skinparam titleBorderThickness 2 5 | skinparam titleBorderColor blue 6 | 7 | 8 | header 9 | Warning: 10 | Educational Purposes 11 | endheader 12 | 13 | center footer Carlos Caballero ([[https://www.carloscaballero.io https://www.carloscaballero.io]]) 14 | 15 | 16 | title Prototype Pattern 17 | 18 | class VirtualPet { 19 | + name: string; 20 | + color: string; 21 | + abilities: string[]; 22 | + energyLevel: number = 0; 23 | 24 | + constructor(name: string, color: string, abilities: string[]) 25 | - calculateEnergy(milliseconds: number): void 26 | + displayInfo(): string 27 | } 28 | 29 | 30 | class Client { } 31 | 32 | Client .> VirtualPet : "<>" 33 | 34 | 35 | @enduml -------------------------------------------------------------------------------- /prototype-pattern/example2/solution/clonable.ts: -------------------------------------------------------------------------------- 1 | export interface Clonable { 2 | clone(): this; 3 | } 4 | -------------------------------------------------------------------------------- /prototype-pattern/example2/solution/index.ts: -------------------------------------------------------------------------------- 1 | import { VirtualPet } from "./virtual-pet"; 2 | 3 | // Create a virtual pet and initialize it 4 | const neko = new VirtualPet('Neko', 'Red', ['Fly', 'Breath Fire', 'Invisibility']); // 3 seconds 5 | neko.displayInfo(); 6 | 7 | // Clonar la mascota virtual usando el patrón Prototype 8 | const nya = neko.clone(); // a few milliseconds 9 | nya.name = 'Nya'; 10 | nya.color = 'Yellow'; 11 | nya.displayInfo(); 12 | 13 | 14 | -------------------------------------------------------------------------------- /prototype-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /prototype-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /prototype-pattern/nodemon-example2-solve.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /prototype-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prototype-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2:problem": "nodemon --config nodemon-example2-problem.json", 8 | "example2:solve": "nodemon --config nodemon-example2-solve.json" 9 | 10 | }, 11 | "keywords": [ 12 | "prototype-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^4.7.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /proxy-pattern/example1/uml.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | 4 | skinparam titleBorderRoundCorner 15 5 | skinparam titleBorderThickness 2 6 | skinparam titleBorderColor blue 7 | 8 | 9 | header 10 | Warning: 11 | Educational Purposes 12 | endheader 13 | 14 | center footer Carlos Caballero ([[https://www.carloscaballero.io https://www.carloscaballero.io]]) 15 | 16 | 17 | title Proxy Pattern 18 | 19 | interface Subject { 20 | +operation(): void 21 | } 22 | 23 | class RealSubject { 24 | +operation(): void 25 | } 26 | 27 | class Proxy { 28 | -realSubject: RealSubject 29 | +Proxy(realSubject: RealSubject) 30 | +operation(): void 31 | } 32 | 33 | Subject <|.. RealSubject 34 | Subject <|.. Proxy 35 | Proxy o- RealSubject 36 | 37 | Client ..> Proxy : "<>" 38 | 39 | 40 | 41 | @enduml 42 | -------------------------------------------------------------------------------- /proxy-pattern/example2/proxies/access.proxy.ts: -------------------------------------------------------------------------------- 1 | import { RealSubject, Subject } from "../subjects/"; 2 | 3 | export class AccessProxy implements Subject { 4 | private realSubject: RealSubject; 5 | private userRole: string; 6 | 7 | constructor(realSubject: RealSubject, userRole: string) { 8 | this.realSubject = realSubject; 9 | this.userRole = userRole; 10 | } 11 | 12 | operation(): void { 13 | if (!this.checkAccess()) { 14 | return console.log('AccessProxy: Access denied.'); 15 | } 16 | this.realSubject.operation(); 17 | } 18 | 19 | private checkAccess(): boolean { 20 | console.log(`AccessProxy: Checking access for role ${this.userRole}.`); 21 | return this.userRole === 'admin'; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /proxy-pattern/example2/proxies/index.ts: -------------------------------------------------------------------------------- 1 | export { AccessProxy } from './access.proxy'; 2 | export { LoggingProxy } from './logging.proxy'; 3 | export { CacheProxy } from './cache.proxy'; 4 | export { LazyProxy } from './lazy.proxy'; 5 | export { RemoteProxy } from './remote.proxy'; -------------------------------------------------------------------------------- /proxy-pattern/example2/proxies/lazy.proxy.ts: -------------------------------------------------------------------------------- 1 | import { RealSubject, Subject } from "../subjects/"; 2 | 3 | export class LazyProxy implements Subject { 4 | private realSubject: RealSubject | null = null; 5 | 6 | operation(): void { 7 | if (this.realSubject === null) { 8 | this.realSubject = new RealSubject(); 9 | } 10 | this.realSubject.operation(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /proxy-pattern/example2/proxies/logging.proxy.ts: -------------------------------------------------------------------------------- 1 | import { RealSubject, Subject } from "../subjects/"; 2 | 3 | export class LoggingProxy implements Subject { 4 | private realSubject: RealSubject; 5 | 6 | constructor(realSubject: RealSubject) { 7 | this.realSubject = realSubject; 8 | } 9 | 10 | operation(): void { 11 | this.logAccess(); 12 | this.realSubject.operation(); 13 | } 14 | 15 | private logAccess(): void { 16 | console.log('LoggingProxy: Logging access to RealSubject.'); 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /proxy-pattern/example2/subjects/index.ts: -------------------------------------------------------------------------------- 1 | export { Subject } from './subject.interface'; 2 | export { RealSubject } from './real-subject'; 3 | export { SubjectCache } from './subject-cache.interface'; 4 | export { RealSubjectCache } from './real-subject-cache'; -------------------------------------------------------------------------------- /proxy-pattern/example2/subjects/real-subject-cache.ts: -------------------------------------------------------------------------------- 1 | import { SubjectCache } from './subject-cache.interface'; 2 | 3 | export class RealSubjectCache implements SubjectCache { 4 | async request(resource: string): Promise { 5 | // Simulate fetching resource from external API 6 | console.log(`RealSubject: Fetching resource from ${resource}`); 7 | // Simulate network delay 8 | await new Promise(resolve => setTimeout(resolve, 1000)); 9 | return `Response from ${resource}`; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /proxy-pattern/example2/subjects/real-subject.ts: -------------------------------------------------------------------------------- 1 | import { Subject } from "./subject.interface"; 2 | 3 | export class RealSubject implements Subject { 4 | operation(): void { 5 | console.log('RealSubject: Handling operation.'); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /proxy-pattern/example2/subjects/subject-cache.interface.ts: -------------------------------------------------------------------------------- 1 | // Common Interface 2 | export interface SubjectCache { 3 | request(resource: string): Promise; 4 | } -------------------------------------------------------------------------------- /proxy-pattern/example2/subjects/subject.interface.ts: -------------------------------------------------------------------------------- 1 | // Common Interface 2 | export interface Subject { 3 | operation(): void; 4 | } 5 | -------------------------------------------------------------------------------- /proxy-pattern/nodemon-example1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /proxy-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /proxy-pattern/nodemon-example2-solve.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /proxy-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proxy-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1": "nodemon --config nodemon-example1.json", 7 | "example2:problem": "nodemon --config nodemon-example2-problem.json", 8 | "example2:solve": "nodemon --config nodemon-example2-solve.json" 9 | 10 | }, 11 | "keywords": [ 12 | "proxy-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^9.1.1", 20 | "typescript": "^3.9.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /refactoring-caesar/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "refactoring-caesar", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "refactoring-caesar-original.js", 6 | "scripts": { 7 | "original": "node refactoring-caesar-original", 8 | "step1": "node refactoring-caesar-step1", 9 | "step2": "node refactoring-caesar-step2", 10 | "step3": "node refactoring-caesar-step3", 11 | "step4": "node refactoring-caesar-step4", 12 | "step5": "node refactoring-caesar-step5", 13 | "step6": "node refactoring-caesar-step6", 14 | "step7": "node refactoring-caesar-step7", 15 | "step8": "node refactoring-caesar-step8" 16 | }, 17 | "keywords": [], 18 | "author": "", 19 | "license": "MIT", 20 | "dependencies": { 21 | "ts-node": "^8.8.1", 22 | "typescript": "^3.8.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /singleton-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Singleton (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-singleton/).** 4 | -------------------------------------------------------------------------------- /singleton-pattern/example1/problem/client1.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnection } from './database-connection'; 2 | 3 | export class Client1 { 4 | private databaseConnection: DatabaseConnection; 5 | constructor() { 6 | console.log('Client1...'); 7 | console.log('DatabaseConnection created...'); 8 | this.databaseConnection = new DatabaseConnection(); 9 | } 10 | getUniqueIdentificatorConnection(): number { 11 | return this.databaseConnection.getUniqueIdentificator; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /singleton-pattern/example1/problem/client2.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnection } from './database-connection'; 2 | 3 | export class Client2 { 4 | private databaseConnection: DatabaseConnection; 5 | constructor() { 6 | console.log('Client2...'); 7 | console.log('DatabaseConnection created...'); 8 | this.databaseConnection = new DatabaseConnection(); 9 | } 10 | getUniqueIdentificatorConnection(): number { 11 | return this.databaseConnection.getUniqueIdentificator; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /singleton-pattern/example1/problem/database-connection.ts: -------------------------------------------------------------------------------- 1 | interface ConnectionConfiguration { 2 | host: string; 3 | user: string; 4 | pass: string; 5 | name: string; 6 | } 7 | 8 | export class DatabaseConnection { 9 | private configuration: ConnectionConfiguration = { 10 | host: 'localhost', 11 | user: 'db user-name', 12 | pass: 'db password', 13 | name: 'db name' 14 | }; 15 | getUniqueIdentificator = Math.round(Math.random() * 10000); 16 | constructor() {} 17 | } 18 | -------------------------------------------------------------------------------- /singleton-pattern/example1/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { Client1 } from './client1'; 2 | import { Client2 } from './client2'; 3 | 4 | const client1 = new Client1(); 5 | console.log( 6 | 'DatabaseConnection ID:', 7 | client1.getUniqueIdentificatorConnection() 8 | ); 9 | 10 | const client2 = new Client2(); 11 | console.log( 12 | 'DatabaseConnection ID:', 13 | client2.getUniqueIdentificatorConnection() 14 | ); 15 | -------------------------------------------------------------------------------- /singleton-pattern/example1/singleton-solution-1/client1.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnection } from './database-connection'; 2 | 3 | export class Client1 { 4 | private databaseConnection: DatabaseConnection; 5 | constructor() { 6 | console.log('Client1...'); 7 | console.log('DatabaseConnection created...'); 8 | this.databaseConnection = DatabaseConnection.getDatabaseConnection(); 9 | } 10 | getUniqueIdentificatorConnection(): number { 11 | return this.databaseConnection.getUniqueIdentificator; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /singleton-pattern/example1/singleton-solution-1/client2.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnection } from './database-connection'; 2 | 3 | export class Client2 { 4 | private databaseConnection: DatabaseConnection; 5 | constructor() { 6 | console.log('Client2...'); 7 | console.log('DatabaseConnection created...'); 8 | this.databaseConnection = DatabaseConnection.getDatabaseConnection(); 9 | } 10 | getUniqueIdentificatorConnection(): number { 11 | return this.databaseConnection.getUniqueIdentificator; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /singleton-pattern/example1/singleton-solution-1/database-connection.ts: -------------------------------------------------------------------------------- 1 | interface ConnectionConfiguration { 2 | host: string; 3 | user: string; 4 | pass: string; 5 | name: string; 6 | } 7 | 8 | export class DatabaseConnection { 9 | private static instance: DatabaseConnection; 10 | private configuration: ConnectionConfiguration = { 11 | host: 'localhost', 12 | user: 'db user-name', 13 | pass: 'db password', 14 | name: 'db name' 15 | }; 16 | getUniqueIdentificator = Math.round(Math.random() * 10000); 17 | private constructor() {} 18 | public static getDatabaseConnection(): DatabaseConnection { 19 | if (!DatabaseConnection.instance) { 20 | this.instance = new DatabaseConnection(); 21 | } 22 | return DatabaseConnection.instance; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /singleton-pattern/example1/singleton-solution-1/index.ts: -------------------------------------------------------------------------------- 1 | import { Client1 } from './client1'; 2 | import { Client2 } from './client2'; 3 | 4 | const client1 = new Client1(); 5 | console.log( 6 | 'DatabaseConnection ID:', 7 | client1.getUniqueIdentificatorConnection() 8 | ); 9 | 10 | const client2 = new Client2(); 11 | console.log( 12 | 'DatabaseConnection ID:', 13 | client2.getUniqueIdentificatorConnection() 14 | ); 15 | -------------------------------------------------------------------------------- /singleton-pattern/example2/problem/client1.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnection } from './database-connection'; 2 | 3 | export class Client1 { 4 | private databaseConnection: DatabaseConnection; 5 | constructor() { 6 | console.log('Client1...'); 7 | console.log('DatabaseConnection created...'); 8 | this.databaseConnection = new DatabaseConnection(); 9 | } 10 | getUniqueIdentificatorConnection(): number { 11 | return this.databaseConnection.getUniqueIdentificator; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /singleton-pattern/example2/problem/client2.ts: -------------------------------------------------------------------------------- 1 | import { DatabaseConnection } from './database-connection'; 2 | 3 | export class Client2 { 4 | private databaseConnection: DatabaseConnection; 5 | constructor() { 6 | console.log('Client2...'); 7 | console.log('DatabaseConnection created...'); 8 | this.databaseConnection = new DatabaseConnection(); 9 | } 10 | getUniqueIdentificatorConnection(): number { 11 | return this.databaseConnection.getUniqueIdentificator; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /singleton-pattern/example2/problem/database-connection.ts: -------------------------------------------------------------------------------- 1 | interface ConnectionConfiguration { 2 | host: string; 3 | user: string; 4 | pass: string; 5 | name: string; 6 | } 7 | 8 | export class DatabaseConnection { 9 | private configuration: ConnectionConfiguration = { 10 | host: 'localhost', 11 | user: 'db user-name', 12 | pass: 'db password', 13 | name: 'db name' 14 | }; 15 | getUniqueIdentificator = Math.round(Math.random() * 10000); 16 | constructor() {} 17 | } 18 | -------------------------------------------------------------------------------- /singleton-pattern/example2/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { Client1 } from './client1'; 2 | import { Client2 } from './client2'; 3 | 4 | const client1 = new Client1(); 5 | console.log( 6 | 'DatabaseConnection ID:', 7 | client1.getUniqueIdentificatorConnection() 8 | ); 9 | 10 | const client2 = new Client2(); 11 | console.log( 12 | 'DatabaseConnection ID:', 13 | client2.getUniqueIdentificatorConnection() 14 | ); 15 | -------------------------------------------------------------------------------- /singleton-pattern/example2/singleton-solution/batman.ts: -------------------------------------------------------------------------------- 1 | import { HeroBase } from './hero-base.class'; 2 | 3 | export class Batman extends HeroBase { 4 | private static instance: Batman; 5 | 6 | private constructor() { 7 | super({ 8 | name: 'Bruce Wayne', 9 | city: 'Gotham City' 10 | }); 11 | } 12 | public static getHero(): Batman { 13 | if (!Batman.instance) { 14 | this.instance = new Batman(); 15 | } 16 | return Batman.instance; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /singleton-pattern/example2/singleton-solution/client1.ts: -------------------------------------------------------------------------------- 1 | import { Batman } from './batman'; 2 | import { Spiderman } from './spiderman'; 3 | 4 | export class Client1 { 5 | private batman: Batman; 6 | private spiderman: Spiderman; 7 | constructor() { 8 | console.log('Client1...'); 9 | console.log('Calling to Heroes...'); 10 | this.batman = Batman.getHero(); 11 | this.spiderman = Spiderman.getHero(); 12 | } 13 | showHero(hero: string): string { 14 | return this[hero].toString(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /singleton-pattern/example2/singleton-solution/client2.ts: -------------------------------------------------------------------------------- 1 | import { Batman } from './batman'; 2 | import { Spiderman } from './spiderman'; 3 | 4 | export class Client2 { 5 | private batman: Batman; 6 | private spiderman: Spiderman; 7 | constructor() { 8 | console.log('Client2...'); 9 | console.log('Calling to Heroes...'); 10 | this.batman = Batman.getHero(); 11 | this.spiderman = Spiderman.getHero(); 12 | } 13 | showHero(hero: string): string { 14 | return this[hero].toString(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /singleton-pattern/example2/singleton-solution/hero-base.class.ts: -------------------------------------------------------------------------------- 1 | import { Hero } from './hero.interface'; 2 | 3 | export abstract class HeroBase { 4 | protected hero: Hero; 5 | protected _getUniqueIdentificator = Math.round(Math.random() * 10000); 6 | 7 | constructor(hero: Hero) { 8 | this.hero = hero; 9 | } 10 | public toString() { 11 | return `${this.hero.name} - ${this.hero.city} - ${ 12 | this._getUniqueIdentificator 13 | }`; 14 | } 15 | public getUniqueIdentificator() { 16 | return this._getUniqueIdentificator; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /singleton-pattern/example2/singleton-solution/hero.interface.ts: -------------------------------------------------------------------------------- 1 | export interface Hero { 2 | name: string; 3 | city: string; 4 | } 5 | -------------------------------------------------------------------------------- /singleton-pattern/example2/singleton-solution/index.ts: -------------------------------------------------------------------------------- 1 | import { Client1 } from './client1'; 2 | import { Client2 } from './client2'; 3 | 4 | const client1 = new Client1(); 5 | console.log( 6 | `Batman: ${client1.showHero('batman')} 7 | Spiderman: ${client1.showHero('spiderman')} 8 | ` 9 | ); 10 | 11 | const client2 = new Client2(); 12 | console.log( 13 | `Batman: ${client2.showHero('batman')} 14 | Spiderman: ${client2.showHero('spiderman')} 15 | ` 16 | ); 17 | -------------------------------------------------------------------------------- /singleton-pattern/example2/singleton-solution/spiderman.ts: -------------------------------------------------------------------------------- 1 | import { HeroBase } from './hero-base.class'; 2 | 3 | export class Spiderman extends HeroBase { 4 | private static instance: Spiderman; 5 | 6 | private constructor() { 7 | super({ 8 | name: 'Peter Parker', 9 | city: 'New City' 10 | }); 11 | } 12 | public static getHero(): Spiderman { 13 | if (!Spiderman.instance) { 14 | this.instance = new Spiderman(); 15 | } 16 | return Spiderman.instance; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /singleton-pattern/nodemon-example1-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /singleton-pattern/nodemon-example1-singleton-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/singleton-solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /singleton-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /singleton-pattern/nodemon-example2-singleton-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/singleton-solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /state-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: State (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-state/).** -------------------------------------------------------------------------------- /state-pattern/example1/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "./context"; 2 | 3 | const context = new Context(); 4 | context.request('request1'); 5 | context.request('request2'); -------------------------------------------------------------------------------- /state-pattern/example1/problem/state.enum.ts: -------------------------------------------------------------------------------- 1 | export enum State { 2 | stateA = "concrete-state-A", 3 | stateB = "concrete-state-B", 4 | } -------------------------------------------------------------------------------- /state-pattern/example1/state-solution-1/concrete-state-A.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteStateB } from "./concrete-state-B"; 2 | import { State } from "./state"; 3 | 4 | export class ConcreteStateA extends State { 5 | public handle1(): void { 6 | console.log('ConcreteStateA handles request1.'); 7 | console.log('ConcreteStateA wants to change the state of the context.'); 8 | this.context.transitionTo(new ConcreteStateB()); 9 | } 10 | 11 | public handle2(): void { 12 | console.log('ConcreteStateA handles request2.'); 13 | } 14 | } -------------------------------------------------------------------------------- /state-pattern/example1/state-solution-1/concrete-state-B.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteStateA } from "./concrete-state-A"; 2 | import { State } from "./state"; 3 | 4 | export class ConcreteStateB extends State { 5 | public handle1(): void { 6 | console.log('ConcreteStateB handles request1.'); 7 | } 8 | 9 | public handle2(): void { 10 | console.log('ConcreteStateB handles request2.'); 11 | console.log('ConcreteStateB wants to change the state of the context.'); 12 | this.context.transitionTo(new ConcreteStateA()); 13 | } 14 | } -------------------------------------------------------------------------------- /state-pattern/example1/state-solution-1/context.ts: -------------------------------------------------------------------------------- 1 | import { State } from "./state"; 2 | 3 | export class Context { 4 | private state: State; 5 | 6 | constructor(state: State) { 7 | this.transitionTo(state); 8 | } 9 | 10 | public transitionTo(state: State): void { 11 | console.log(`Context: Transition to ${state.constructor.name}.`); 12 | this.state = state; 13 | this.state.setContext(this); 14 | } 15 | 16 | public request1(): void { 17 | this.state.handle1(); 18 | } 19 | 20 | public request2(): void { 21 | this.state.handle2(); 22 | } 23 | } -------------------------------------------------------------------------------- /state-pattern/example1/state-solution-1/index.ts: -------------------------------------------------------------------------------- 1 | import { ConcreteStateA } from "./concrete-state-A"; 2 | import { Context } from "./context"; 3 | 4 | const context = new Context(new ConcreteStateA()); // Initial State 5 | context.request1(); 6 | context.request2(); 7 | -------------------------------------------------------------------------------- /state-pattern/example1/state-solution-1/state.ts: -------------------------------------------------------------------------------- 1 | import { Context } from "./context"; 2 | 3 | export abstract class State { 4 | protected context: Context; 5 | 6 | public setContext(context: Context) { 7 | this.context = context; 8 | } 9 | 10 | public abstract handle1(): void; 11 | public abstract handle2(): void; 12 | } -------------------------------------------------------------------------------- /state-pattern/example2/problem/index.ts: -------------------------------------------------------------------------------- 1 | import { Freeza } from "./freeza"; 2 | import { State } from "./state.enum"; 3 | 4 | const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); 5 | const freeza = new Freeza(State.TRANSFORMATION1); // Initial State 6 | 7 | (async () => { 8 | while(freeza.isAlive()){ 9 | freeza.attack(); 10 | await sleep(1000); 11 | freeza.defend(10); 12 | await sleep(1000); 13 | } 14 | })(); 15 | -------------------------------------------------------------------------------- /state-pattern/example2/problem/state.enum.ts: -------------------------------------------------------------------------------- 1 | export enum State { 2 | TRANSFORMATION1 = 'transformation1', 3 | TRANSFORMATION2 = 'transformation2', 4 | TRANSFORMATION3 = 'transformation3', 5 | TRANSFORMATION4 = 'transformation4', 6 | GOLDEN_FREEZER = 'golden_freezer', 7 | } -------------------------------------------------------------------------------- /state-pattern/example2/solution/index.ts: -------------------------------------------------------------------------------- 1 | import { Freeza } from "./freeza"; 2 | import { Transformation1 } from "./states/transformation1"; 3 | 4 | const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); 5 | const freeza = new Freeza(new Transformation1()); // Initial State 6 | 7 | (async () => { 8 | while(freeza.isAlive()){ 9 | freeza.attack(); 10 | await sleep(10); 11 | freeza.defend(10); 12 | await sleep(10); 13 | } 14 | })(); 15 | -------------------------------------------------------------------------------- /state-pattern/example2/solution/state.ts: -------------------------------------------------------------------------------- 1 | import { Freeza } from "./freeza"; 2 | 3 | export abstract class State { 4 | 5 | abstract power: number; 6 | abstract energy: number; 7 | protected freeza: Freeza; 8 | 9 | 10 | public setFreeza(freeza: Freeza) { 11 | this.freeza = freeza; 12 | } 13 | public getEnergy() { 14 | return this.energy; 15 | } 16 | 17 | 18 | public abstract attack(): void; 19 | public abstract defend(value: number): void 20 | } -------------------------------------------------------------------------------- /state-pattern/nodemon-example1-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /state-pattern/nodemon-example1-state-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example1"], 3 | "ext": "ts", 4 | "ignore": ["example1/**/*.spec.ts"], 5 | "exec": "ts-node ./example1/solution-1/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /state-pattern/nodemon-example2-problem.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/problem/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /state-pattern/nodemon-example2-state-solution-1.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["example2"], 3 | "ext": "ts", 4 | "ignore": ["example2/**/*.spec.ts"], 5 | "exec": "ts-node ./example2/solution/index.ts" 6 | } 7 | -------------------------------------------------------------------------------- /state-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "state-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "example1-problem": "nodemon --config nodemon-example1-problem.json", 7 | "example1-state-solution-1": "nodemon --config nodemon-example1-state-solution-1.json", 8 | "example2-problem": "nodemon --config nodemon-example2-problem.json", 9 | "example2-state-solution-1": "nodemon --config nodemon-example2-state-solution-1.json" 10 | }, 11 | "keywords": [ 12 | "state-pattern", 13 | "clean-code" 14 | ], 15 | "author": "Carlos Caballero", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "nodemon": "^1.19.4", 19 | "ts-node": "^10.9.1", 20 | "typescript": "^3.9.10" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /state-pattern/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["es2015", "dom"], 5 | "strict": false, 6 | "declaration": true 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /strategy-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Strategy (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/stategy-pattern-in-javascript-typescript/).** -------------------------------------------------------------------------------- /strategy-pattern/strategy1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /strategy-pattern/strategy2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /template-method-pattern/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Template Method (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-template-method/).** -------------------------------------------------------------------------------- /template-method-pattern/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template-method-pattern", 3 | "version": "0.0.1", 4 | "description": "", 5 | "scripts": { 6 | "step0": "nodemon template-method0.js", 7 | "step1": "nodemon template-method1.js" 8 | }, 9 | "keywords": [ 10 | "template-method pattern", 11 | "pattern design" 12 | ], 13 | "author": "Carlos Caballero", 14 | "license": "MIT", 15 | "dependencies": { 16 | "nodemon": "^1.18.10" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /understanding-this-in-js/01-global-browser/global-browser.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /understanding-this-in-js/01-global-browser/global-browser.js: -------------------------------------------------------------------------------- 1 | console.log('browser - global - this'); 2 | console.log(this) 3 | 4 | /* browser */ 5 | //So, this, at the module level, is actually the window object. 6 | console.log('browser - global this is window at the module level'); 7 | console.log(this === window); 8 | 9 | function anyFunction() { 10 | // this is a global window object 11 | console.log('browser - global this is window object inside any function'); 12 | console.log(this); 13 | } 14 | 15 | anyFunction(); 16 | 17 | (function () { 18 | console.log('browser - global this is a window object inside any function (included IIFE)'); 19 | console.log(this) 20 | })(); 21 | -------------------------------------------------------------------------------- /understanding-this-in-js/02-global-nodejs/global-nodejs.js: -------------------------------------------------------------------------------- 1 | console.log('node.js - global - this'); 2 | console.log(this) 3 | 4 | /* nodeJS */ 5 | //So, this, at the module level, is actually the exports object. 6 | console.log('node.js - global this is module.exports at the module level'); 7 | console.log(this === module.exports); 8 | 9 | function anyFunction() { 10 | // this is a global nodeJS object 11 | console.log('node.js - global this is a node.js object inside any function'); 12 | console.log(this === global); 13 | } 14 | 15 | anyFunction(); 16 | 17 | (function () { 18 | console.log('node.js - global this is a node.js object inside any function (included IIFE'); 19 | console.log(this === global) 20 | })(); 21 | -------------------------------------------------------------------------------- /understanding-this-in-js/03-keyword-new/new-01.js: -------------------------------------------------------------------------------- 1 | function Dog() { 2 | //this context is not windows 3 | console.log('this is not a global scope') 4 | console.log(this === module.exports); 5 | } 6 | 7 | new Dog(); 8 | -------------------------------------------------------------------------------- /understanding-this-in-js/03-keyword-new/new-02.js: -------------------------------------------------------------------------------- 1 | function Dog(name, job) { 2 | this.name = name; 3 | this.job = job; 4 | 5 | this.displayName = function () { 6 | console.log(`this is in the Dog context:: ${this.name} ${this.job}`); 7 | } 8 | } 9 | 10 | const turbot = new Dog('Turbot', 'Police'); 11 | const rubble = new Dog('Rubble', 'Worker'); 12 | turbot.displayName(); 13 | rubble.displayName(); -------------------------------------------------------------------------------- /understanding-this-in-js/03-keyword-new/new-03.js: -------------------------------------------------------------------------------- 1 | /* Using prototype */ 2 | function Dog(name, job) { 3 | this.name = name; 4 | this.job = job; 5 | } 6 | Dog.prototype.displayName = function () { 7 | console.log(`this is in the Dog context:: ${this.name} ${this.job}`); 8 | } 9 | 10 | const turbot = new Dog('Turbot', 'Police'); 11 | const rubble = new Dog('Rubble', 'Worker'); 12 | turbot.displayName(); 13 | rubble.displayName(); 14 | -------------------------------------------------------------------------------- /understanding-this-in-js/04-invoker-object/invoker-object.js: -------------------------------------------------------------------------------- 1 | function anyFunction() { 2 | console.log("any function"); 3 | console.log(this === global); 4 | } 5 | 6 | const user = { 7 | name: 'Carlos Caballero', 8 | anyFunction: anyFunction, 9 | otherFunction: function () { 10 | console.log(this === global); 11 | } 12 | } 13 | 14 | anyFunction(); // Prints true because "this" referes to global object 15 | user.anyFunction() // Prints false because now “this” refers to user object instead of global object. 16 | const fun1 = user.otherFunction; 17 | fun1() // Prints true as this method is invoked as a simple function. 18 | user.otherFunction() // Prints false on console as otherFunction is invoked as a object’s method -------------------------------------------------------------------------------- /understanding-this-in-js/05-call-apply/call-apply.js: -------------------------------------------------------------------------------- 1 | function Dog(name, job) { 2 | this.name = name; 3 | this.job = job; 4 | } 5 | Dog.prototype.displayName = function () { 6 | console.log(`Dog:: ${this.name} - ${this.job}`); 7 | } 8 | 9 | 10 | const turbot = new Dog('Turbot', 'Police'); 11 | const rubble = new Dog('Rubble', 'Worker'); 12 | turbot.displayName(); // Prints Turbot info 13 | rubble.displayName(); // Prints Rubble info 14 | 15 | turbot.displayName.call(rubble); // Here we are setting value of this to be rubble object 16 | //Prints Rubble info -------------------------------------------------------------------------------- /understanding-this-in-js/06-bind/bind.js: -------------------------------------------------------------------------------- 1 | function Dog(name, job) { 2 | this.name = name; 3 | this.job = job; 4 | } 5 | Dog.prototype.displayName = function () { 6 | console.log(`this is in the Dog context:: ${this.name} ${this.job}`); 7 | } 8 | 9 | const turbot = new Dog('Turbot', 'Police'); 10 | const rubble = new Dog('Rubble', 'Worker'); 11 | turbot.displayName(); 12 | rubble.displayName(); 13 | 14 | const newDisplayName = turbot.displayName.bind(rubble); // / Creates new function with value of “this” equals to rubble's object 15 | newDisplayName();//Prints Turbo info -------------------------------------------------------------------------------- /understanding-this-in-js/07-fat-arrow/fat-arrow-bad.js: -------------------------------------------------------------------------------- 1 | // Option bad 2 | 3 | function Dog(name, job) { 4 | this.name = name; 5 | this.job = job; 6 | this.age = 0; 7 | 8 | this.displayName = function () { 9 | console.log(`Name: ${this.name} ${this.job}`); 10 | } 11 | 12 | setInterval(function growUp() { 13 | this.age++; // this is growUp "this" value 14 | console.log(this.age); 15 | }, 1000); 16 | } 17 | 18 | const turbot = new Dog("Turbo", "Police"); -------------------------------------------------------------------------------- /understanding-this-in-js/07-fat-arrow/fat-arrow.js: -------------------------------------------------------------------------------- 1 | 2 | function Dog(name, job) { 3 | this.name = name; 4 | this.job = job; 5 | this.age = 0; 6 | 7 | this.displayName = function () { 8 | console.log(`Name: ${this.name} ${this.job}`); 9 | } 10 | 11 | setInterval(() => { 12 | this.age++; // this is Dog "this" value 13 | console.log(this.age); 14 | }, 1000); 15 | } 16 | 17 | const turbot = new Dog("Turbo", "Police"); 18 | 19 | 20 | -------------------------------------------------------------------------------- /understanding-this-in-js/08-class/class.js: -------------------------------------------------------------------------------- 1 | class Dog { 2 | constructor(name, job) { 3 | this.name = name; 4 | this.job = job; 5 | this.age = 0; 6 | } 7 | 8 | displayName() { 9 | console.log(`Name: ${this.name}`); 10 | 11 | function innerDisplay() { 12 | console.log(`Job: ${this.job}`); 13 | } 14 | innerDisplay(); // undefined 15 | innerDisplay.apply(this); // Job: Police 16 | } 17 | } 18 | 19 | const turbot = new Dog("Turbo", "Police"); 20 | turbot.displayName(); -------------------------------------------------------------------------------- /understanding-this-in-js/09 - exercises/exercise-01.js: -------------------------------------------------------------------------------- 1 | function multiply(p, q, callback) { 2 | callback(p * q); 3 | } 4 | 5 | let user = { 6 | a: 2, 7 | b:3, 8 | findMultiply: function() { 9 | multiply(this.a, this.b, function(total) { 10 | console.log(total); 11 | console.log(this === window); 12 | }) 13 | } 14 | } 15 | 16 | user.findMultiply(); 17 | //Prints 6 18 | //Prints true -------------------------------------------------------------------------------- /understanding-this-in-js/README.md: -------------------------------------------------------------------------------- 1 | # Design Patterns: Singleton (Design Patterns series) 2 | 3 | **To read an explanation of this design pattern, check out the [blogpost](https://www.carloscaballero.io/design-patterns-singleton/).** 4 | -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Operations 7 | 8 | 9 | 10 |

Operations

11 | 12 | 13 |
14 | 15 | 16 |
17 | 18 |

19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Guess number 7 | 8 | 9 | 10 |

Guess the number between 1 and 7

11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem2/main.js: -------------------------------------------------------------------------------- 1 | let randomNumber; 2 | let life; 3 | function start() { 4 | configure(); 5 | play(); 6 | } 7 | function configure() { 8 | randomNumber = parseInt(Math.random() * 6) + 1; 9 | life = 3; 10 | } 11 | function play() { 12 | /* Para pedir: prompt("Adivina el numero:");*/ 13 | /* Para enviar mensaje: alert("Acertado");*/ 14 | /* TODO */ 15 | } 16 | -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem3/css/img/campo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/problems/Problem3/css/img/campo.jpeg -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem3/css/img/rope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/problems/Problem3/css/img/rope.png -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem3/css/rope.css: -------------------------------------------------------------------------------- 1 | .field { 2 | width: 75%; 3 | height: 75%; 4 | position: absolute; 5 | } 6 | .rope { 7 | position: absolute; 8 | width: 30%; 9 | padding: 0%; 10 | margin: 0; 11 | } 12 | -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem3/rope.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | RopeGame 10 | 11 | 12 | 13 |

A(left),L(right)

14 |
15 | Smiley face 16 | Smiley face 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem4/balon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/problems/Problem4/balon.gif -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem4/field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/problems/Problem4/field.png -------------------------------------------------------------------------------- /workshop-javascript/problems/Problem4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Football 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Operations 7 | 8 | 9 | 10 | 11 |

Operations

12 | 13 | 14 |
15 | 16 | 17 |
18 | 19 |

20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem1/quadratic-equations.html: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/solutions/Problem1/quadratic-equations.html -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Guess number 7 | 8 | 9 | 10 |

Guess the number between 1 and 7

11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem2/main.js: -------------------------------------------------------------------------------- 1 | let randomNumber; 2 | let life; 3 | function start() { 4 | configure(); 5 | play(); 6 | } 7 | function configure() { 8 | randomNumber = parseInt(Math.random() * 6) + 1; 9 | life = 3; 10 | } 11 | function play() { 12 | let correctNumber = false; 13 | do { 14 | let n = prompt("Adivina el numero:"); 15 | if (n == randomNumber) { 16 | correctNumber = true; 17 | } 18 | --life; 19 | } while (!correctNumber && life > 0); 20 | if (correctNumber) { 21 | document.write("Correcto lo adivinastes"); 22 | } else { 23 | document.write("Se te agotaron los intentos"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem3/css/img/campo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/solutions/Problem3/css/img/campo.jpeg -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem3/css/img/rope.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/solutions/Problem3/css/img/rope.png -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem3/css/rope.css: -------------------------------------------------------------------------------- 1 | .field { 2 | width: 75%; 3 | height: 75%; 4 | position: absolute; 5 | } 6 | .rope { 7 | position: absolute; 8 | width: 30%; 9 | padding: 0%; 10 | margin: 0; 11 | } 12 | -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem3/rope.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | RopeGame 10 | 11 | 12 | 13 |

A(left),L(right)

14 |
15 | Smiley face 16 | Smiley face 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem4/balon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/solutions/Problem4/balon.gif -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem4/field.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Caballerog/blog/3329167ef767e8ece9cf4131481afa92b08a8b0f/workshop-javascript/solutions/Problem4/field.png -------------------------------------------------------------------------------- /workshop-javascript/solutions/Problem4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Football 9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 | 21 | 22 | 23 | --------------------------------------------------------------------------------