├── .gitignore ├── .vscode └── launch.json ├── LICENSE.txt ├── README.md ├── behavioral-patterns ├── chain-of-responsibility │ ├── lazy-supporter.ts │ ├── limited-supporter.ts │ ├── main.ts │ ├── moody-supporter.ts │ ├── special-supporter.ts │ ├── supporter.ts │ ├── trouble.ts │ └── tsconfig.json ├── command │ ├── app-main.ts │ ├── command.ts │ ├── history-command.ts │ ├── index.html │ ├── main.ts │ ├── painting-canvas.ts │ ├── painting-command.ts │ ├── painting-target.ts │ └── tsconfig.json ├── interpreter │ ├── action.ts │ ├── command-list.ts │ ├── command.ts │ ├── context.ts │ ├── main.ts │ ├── node.ts │ ├── program.ts │ ├── program.txt │ ├── repeat.ts │ └── tsconfig.json ├── iterator │ ├── aggregate.ts │ ├── book-shelf-iterator.ts │ ├── book-shelf.ts │ ├── book.ts │ ├── iterator.ts │ ├── main.ts │ └── tsconfig.json ├── mediator │ ├── app-login.ts │ ├── colleague-button.ts │ ├── colleague-radio-button.ts │ ├── colleague-text-field.ts │ ├── colleague.ts │ ├── index.html │ ├── main.ts │ ├── mediator.ts │ └── tsconfig.json ├── memento │ ├── gamer.ts │ ├── main.ts │ ├── memento.ts │ └── tsconfig.json ├── observer │ ├── bar-chart-observer.ts │ ├── digit-observer.ts │ ├── main.ts │ ├── number-subject.ts │ ├── observer.ts │ ├── subject.ts │ └── tsconfig.json ├── state │ ├── app-safe.ts │ ├── context.ts │ ├── daytime-state.ts │ ├── index.html │ ├── main.ts │ ├── night-state.ts │ ├── state.ts │ └── tsconfig.json ├── strategy │ ├── game-result-type.ts │ ├── hand-signal.ts │ ├── main.ts │ ├── mirror-strategy.ts │ ├── player.ts │ ├── random-strategy.ts │ ├── strategy.ts │ └── tsconfig.json ├── template-method │ ├── abstract-display.ts │ ├── char-display.ts │ ├── main.ts │ ├── string-display.ts │ └── tsconfig.json └── visitor │ ├── directory-element.ts │ ├── element.ts │ ├── file-element.ts │ ├── file-system-element.ts │ ├── list-visitor.ts │ ├── main.ts │ ├── tsconfig.json │ └── visitor.ts ├── creational-patterns ├── abstract-factory │ ├── factory │ │ ├── data.ts │ │ ├── factory.ts │ │ ├── item.ts │ │ ├── link.ts │ │ └── page.ts │ ├── list-factory │ │ ├── list-data.ts │ │ ├── list-factory.ts │ │ ├── list-link.ts │ │ └── list-page.ts │ ├── main.ts │ ├── table-factory │ │ ├── table-data.ts │ │ ├── table-factory.ts │ │ ├── table-link.ts │ │ └── table-page.ts │ └── tsconfig.json ├── builder │ ├── builder.ts │ ├── director.ts │ ├── html-builder.ts │ ├── main.ts │ ├── plain-text-builder.ts │ └── tsconfig.json ├── factory-method │ ├── credit-card │ │ ├── credit-card-factory.ts │ │ └── credit-card.ts │ ├── framework │ │ ├── factory.ts │ │ └── product.ts │ ├── main.ts │ └── tsconfig.json ├── prototype │ ├── frame-display.ts │ ├── framework │ │ ├── display.ts │ │ └── manager.ts │ ├── main.ts │ ├── tsconfig.json │ └── underline-display.ts └── singleton │ ├── main.ts │ ├── singleton.ts │ └── tsconfig.json ├── design-pattern-examples-in-typescript.code-workspace ├── model ├── DesignPatternExamplesInTypescript.asta └── m_plus.conf ├── screenshots ├── AllPatterns.svg ├── BuildError.png ├── CompileAndRun.gif ├── CompositePattern.svg ├── GenerateCode.gif └── GoLiveButton.png └── structural-patterns ├── adapter ├── main.ts ├── message-display.ts ├── print-message-display.ts ├── print.ts └── tsconfig.json ├── bridge ├── display-impl.ts ├── display.ts ├── main.ts ├── multi-line-display.ts ├── text-display-impl.ts └── tsconfig.json ├── composite ├── directory-element.ts ├── file-element.ts ├── file-system-element.ts ├── main.ts └── tsconfig.json ├── decorator ├── display.ts ├── frame.ts ├── full-frame.ts ├── main.ts ├── message-display.ts ├── side-frame.ts └── tsconfig.json ├── facade ├── addressbook.txt ├── data-library.ts ├── html-writer.ts ├── main.ts ├── page-creator.ts └── tsconfig.json ├── flyweight ├── big0.txt ├── big1.txt ├── big2.txt ├── big3.txt ├── big4.txt ├── big5.txt ├── big6.txt ├── big7.txt ├── big8.txt ├── big9.txt ├── large-size-char-factory.ts ├── large-size-char.ts ├── large-size-string.ts ├── main.ts └── tsconfig.json └── proxy ├── main.ts ├── printer.ts ├── proxy-printer.ts ├── real-printer.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | *.asta.lock 2 | *.js 3 | *.map 4 | node_modules/ 5 | package.json 6 | package-lock.json 7 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "node", 6 | "request": "launch", 7 | "name": "Launch behavioral-patterns/chain-of-responsibility", 8 | "program": "${workspaceFolder}/behavioral-patterns/chain-of-responsibility/main.ts", 9 | "cwd": "${workspaceFolder}/behavioral-patterns/chain-of-responsibility", 10 | "preLaunchTask": "tsc: build - behavioral-patterns/chain-of-responsibility/tsconfig.json", 11 | "console": "integratedTerminal", 12 | "skipFiles": [ 13 | "${workspaceFolder}/node_modules/**/*.js", 14 | "/**/*.js" 15 | ] 16 | }, 17 | { 18 | "type": "chrome", 19 | "request": "launch", 20 | "name": "Launch behavioral-patterns/command", 21 | "url": "http://localhost:5500/behavioral-patterns/command/index.html", 22 | "webRoot": "${workspaceFolder}", 23 | "preLaunchTask": "tsc: build - behavioral-patterns/command/tsconfig.json", 24 | "skipFiles": [ 25 | "${workspaceFolder}/node_modules/**/*.js", 26 | "/**/*.js" 27 | ] 28 | }, 29 | { 30 | "type": "node", 31 | "request": "launch", 32 | "name": "Launch behavioral-patterns/interpreter", 33 | "program": "${workspaceFolder}/behavioral-patterns/interpreter/main.ts", 34 | "cwd": "${workspaceFolder}/behavioral-patterns/interpreter", 35 | "preLaunchTask": "tsc: build - behavioral-patterns/interpreter/tsconfig.json", 36 | "console": "integratedTerminal", 37 | "skipFiles": [ 38 | "${workspaceFolder}/node_modules/**/*.js", 39 | "/**/*.js" 40 | ] 41 | }, 42 | { 43 | "type": "node", 44 | "request": "launch", 45 | "name": "Launch behavioral-patterns/iterator", 46 | "program": "${workspaceFolder}/behavioral-patterns/iterator/main.ts", 47 | "cwd": "${workspaceFolder}/behavioral-patterns/iterator", 48 | "preLaunchTask": "tsc: build - behavioral-patterns/iterator/tsconfig.json", 49 | "console": "integratedTerminal", 50 | "skipFiles": [ 51 | "${workspaceFolder}/node_modules/**/*.js", 52 | "/**/*.js" 53 | ] 54 | }, 55 | { 56 | "type": "chrome", 57 | "request": "launch", 58 | "name": "Launch behavioral-patterns/mediator", 59 | "url": "http://localhost:5500/behavioral-patterns/mediator/index.html", 60 | "webRoot": "${workspaceFolder}", 61 | "preLaunchTask": "tsc: build - behavioral-patterns/mediator/tsconfig.json", 62 | "skipFiles": [ 63 | "${workspaceFolder}/node_modules/**/*.js", 64 | "/**/*.js" 65 | ] 66 | }, 67 | { 68 | "type": "node", 69 | "request": "launch", 70 | "name": "Launch behavioral-patterns/memento", 71 | "program": "${workspaceFolder}/behavioral-patterns/memento/main.ts", 72 | "cwd": "${workspaceFolder}/behavioral-patterns/memento", 73 | "preLaunchTask": "tsc: build - behavioral-patterns/memento/tsconfig.json", 74 | "console": "integratedTerminal", 75 | "skipFiles": [ 76 | "${workspaceFolder}/node_modules/**/*.js", 77 | "/**/*.js" 78 | ] 79 | }, 80 | { 81 | "type": "node", 82 | "request": "launch", 83 | "name": "Launch behavioral-patterns/observer", 84 | "program": "${workspaceFolder}/behavioral-patterns/observer/main.ts", 85 | "cwd": "${workspaceFolder}/behavioral-patterns/observer", 86 | "preLaunchTask": "tsc: build - behavioral-patterns/observer/tsconfig.json", 87 | "console": "integratedTerminal", 88 | "skipFiles": [ 89 | "${workspaceFolder}/node_modules/**/*.js", 90 | "/**/*.js" 91 | ] 92 | }, 93 | { 94 | "type": "chrome", 95 | "request": "launch", 96 | "name": "Launch behavioral-patterns/state", 97 | "url": "http://localhost:5500/behavioral-patterns/state/index.html", 98 | "webRoot": "${workspaceFolder}", 99 | "preLaunchTask": "tsc: build - behavioral-patterns/state/tsconfig.json", 100 | "skipFiles": [ 101 | "${workspaceFolder}/node_modules/**/*.js", 102 | "/**/*.js" 103 | ] 104 | }, 105 | { 106 | "type": "node", 107 | "request": "launch", 108 | "name": "Launch behavioral-patterns/strategy", 109 | "program": "${workspaceFolder}/behavioral-patterns/strategy/main.ts", 110 | "cwd": "${workspaceFolder}/behavioral-patterns/strategy", 111 | "preLaunchTask": "tsc: build - behavioral-patterns/strategy/tsconfig.json", 112 | "console": "integratedTerminal", 113 | "skipFiles": [ 114 | "${workspaceFolder}/node_modules/**/*.js", 115 | "/**/*.js" 116 | ] 117 | }, 118 | { 119 | "type": "node", 120 | "request": "launch", 121 | "name": "Launch behavioral-patterns/template-method", 122 | "program": "${workspaceFolder}/behavioral-patterns/template-method/main.ts", 123 | "cwd": "${workspaceFolder}/behavioral-patterns/template-method", 124 | "preLaunchTask": "tsc: build - behavioral-patterns/template-method/tsconfig.json", 125 | "console": "integratedTerminal", 126 | "skipFiles": [ 127 | "${workspaceFolder}/node_modules/**/*.js", 128 | "/**/*.js" 129 | ] 130 | }, 131 | { 132 | "type": "node", 133 | "request": "launch", 134 | "name": "Launch behavioral-patterns/visitor", 135 | "program": "${workspaceFolder}/behavioral-patterns/visitor/main.ts", 136 | "cwd": "${workspaceFolder}/behavioral-patterns/visitor", 137 | "preLaunchTask": "tsc: build - behavioral-patterns/visitor/tsconfig.json", 138 | "console": "integratedTerminal", 139 | "skipFiles": [ 140 | "${workspaceFolder}/node_modules/**/*.js", 141 | "/**/*.js" 142 | ] 143 | }, 144 | { 145 | "type": "node", 146 | "request": "launch", 147 | "name": "Launch creational-patterns/abstract-factory", 148 | "program": "${workspaceFolder}/creational-patterns/abstract-factory/main.ts", 149 | "cwd": "${workspaceFolder}/creational-patterns/abstract-factory", 150 | "preLaunchTask": "tsc: build - creational-patterns/abstract-factory/tsconfig.json", 151 | "console": "integratedTerminal", 152 | "skipFiles": [ 153 | "${workspaceFolder}/node_modules/**/*.js", 154 | "/**/*.js" 155 | ] 156 | }, 157 | { 158 | "type": "node", 159 | "request": "launch", 160 | "name": "Launch creational-patterns/builder", 161 | "program": "${workspaceFolder}/creational-patterns/builder/main.ts", 162 | "cwd": "${workspaceFolder}/creational-patterns/builder", 163 | "preLaunchTask": "tsc: build - creational-patterns/builder/tsconfig.json", 164 | "console": "integratedTerminal", 165 | "skipFiles": [ 166 | "${workspaceFolder}/node_modules/**/*.js", 167 | "/**/*.js" 168 | ] 169 | }, 170 | { 171 | "type": "node", 172 | "request": "launch", 173 | "name": "Launch creational-patterns/factory-method", 174 | "program": "${workspaceFolder}/creational-patterns/factory-method/main.ts", 175 | "cwd": "${workspaceFolder}/creational-patterns/factory-method", 176 | "preLaunchTask": "tsc: build - creational-patterns/factory-method/tsconfig.json", 177 | "console": "integratedTerminal", 178 | "skipFiles": [ 179 | "${workspaceFolder}/node_modules/**/*.js", 180 | "/**/*.js" 181 | ] 182 | }, 183 | { 184 | "type": "node", 185 | "request": "launch", 186 | "name": "Launch creational-patterns/prototype", 187 | "program": "${workspaceFolder}/creational-patterns/prototype/main.ts", 188 | "cwd": "${workspaceFolder}/creational-patterns/prototype", 189 | "preLaunchTask": "tsc: build - creational-patterns/prototype/tsconfig.json", 190 | "console": "integratedTerminal", 191 | "skipFiles": [ 192 | "${workspaceFolder}/node_modules/**/*.js", 193 | "/**/*.js" 194 | ] 195 | }, 196 | { 197 | "type": "node", 198 | "request": "launch", 199 | "name": "Launch creational-patterns/singleton", 200 | "program": "${workspaceFolder}/creational-patterns/singleton/main.ts", 201 | "cwd": "${workspaceFolder}/creational-patterns/singleton", 202 | "preLaunchTask": "tsc: build - creational-patterns/singleton/tsconfig.json", 203 | "console": "integratedTerminal", 204 | "skipFiles": [ 205 | "${workspaceFolder}/node_modules/**/*.js", 206 | "/**/*.js" 207 | ] 208 | }, 209 | { 210 | "type": "node", 211 | "request": "launch", 212 | "name": "Launch structural-patterns/adapter", 213 | "program": "${workspaceFolder}/structural-patterns/adapter/main.ts", 214 | "cwd": "${workspaceFolder}/structural-patterns/adapter", 215 | "preLaunchTask": "tsc: build - structural-patterns/adapter/tsconfig.json", 216 | "console": "integratedTerminal", 217 | "skipFiles": [ 218 | "${workspaceFolder}/node_modules/**/*.js", 219 | "/**/*.js" 220 | ] 221 | }, 222 | { 223 | "type": "node", 224 | "request": "launch", 225 | "name": "Launch structural-patterns/bridge", 226 | "program": "${workspaceFolder}/structural-patterns/bridge/main.ts", 227 | "cwd": "${workspaceFolder}/structural-patterns/bridge", 228 | "preLaunchTask": "tsc: build - structural-patterns/bridge/tsconfig.json", 229 | "console": "integratedTerminal", 230 | "skipFiles": [ 231 | "${workspaceFolder}/node_modules/**/*.js", 232 | "/**/*.js" 233 | ] 234 | }, 235 | { 236 | "type": "node", 237 | "request": "launch", 238 | "name": "Launch structural-patterns/composite", 239 | "program": "${workspaceFolder}/structural-patterns/composite/main.ts", 240 | "cwd": "${workspaceFolder}/structural-patterns/composite", 241 | "preLaunchTask": "tsc: build - structural-patterns/composite/tsconfig.json", 242 | "console": "integratedTerminal", 243 | "skipFiles": [ 244 | "${workspaceFolder}/node_modules/**/*.js", 245 | "/**/*.js" 246 | ] 247 | }, 248 | { 249 | "type": "node", 250 | "request": "launch", 251 | "name": "Launch structural-patterns/decorator", 252 | "program": "${workspaceFolder}/structural-patterns/decorator/main.ts", 253 | "cwd": "${workspaceFolder}/structural-patterns/decorator", 254 | "preLaunchTask": "tsc: build - structural-patterns/decorator/tsconfig.json", 255 | "console": "integratedTerminal", 256 | "skipFiles": [ 257 | "${workspaceFolder}/node_modules/**/*.js", 258 | "/**/*.js" 259 | ] 260 | }, 261 | { 262 | "type": "node", 263 | "request": "launch", 264 | "name": "Launch structural-patterns/facade", 265 | "program": "${workspaceFolder}/structural-patterns/facade/main.ts", 266 | "cwd": "${workspaceFolder}/structural-patterns/facade", 267 | "preLaunchTask": "tsc: build - structural-patterns/facade/tsconfig.json", 268 | "console": "integratedTerminal", 269 | "skipFiles": [ 270 | "${workspaceFolder}/node_modules/**/*.js", 271 | "/**/*.js" 272 | ] 273 | }, 274 | { 275 | "type": "node", 276 | "request": "launch", 277 | "name": "Launch structural-patterns/flyweight", 278 | "program": "${workspaceFolder}/structural-patterns/flyweight/main.ts", 279 | "cwd": "${workspaceFolder}/structural-patterns/flyweight", 280 | "preLaunchTask": "tsc: build - structural-patterns/flyweight/tsconfig.json", 281 | "console": "integratedTerminal", 282 | "skipFiles": [ 283 | "${workspaceFolder}/node_modules/**/*.js", 284 | "/**/*.js" 285 | ] 286 | }, 287 | { 288 | "type": "node", 289 | "request": "launch", 290 | "name": "Launch structural-patterns/proxy", 291 | "program": "${workspaceFolder}/structural-patterns/proxy/main.ts", 292 | "cwd": "${workspaceFolder}/structural-patterns/proxy", 293 | "preLaunchTask": "tsc: build - structural-patterns/proxy/tsconfig.json", 294 | "console": "integratedTerminal", 295 | "skipFiles": [ 296 | "${workspaceFolder}/node_modules/**/*.js", 297 | "/**/*.js" 298 | ] 299 | } 300 | ] 301 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [](https://raw.githubusercontent.com/takaakit/design-pattern-examples-in-typescript/master/screenshots/AllPatterns.svg) 2 | 3 | Design Pattern Examples in TypeScript 4 | === 5 | 6 | Model and code examples of GoF Design Patterns for TypeScript. 7 | This project is available for the following objectives: 8 | 9 | * To understand GoF Design Pattern examples in TypeScript. 10 | * To understand the mapping between UML model and TypeScript code. 11 | * To try model-driven development (MDD) using Astah and m plus plug-in. 12 | 13 | > UML model example: 14 | 15 | ![](./screenshots/CompositePattern.svg "Composite Pattern") 16 | 17 | 18 | > TypeScript code example: 19 | 20 | ```typescript 21 | // ˅ 22 | 'use strict'; 23 | 24 | import { FileSystemElement } from './file-system-element'; 25 | 26 | // ˄ 27 | 28 | export class FileElement extends FileSystemElement { 29 | // ˅ 30 | 31 | // ˄ 32 | 33 | private readonly _name: string; 34 | 35 | private readonly _size: number; 36 | 37 | constructor(name: string, size: number) { 38 | // ˅ 39 | super(); 40 | this._name = name; 41 | this._size = size; 42 | // ˄ 43 | } 44 | 45 | get name(): string { 46 | // ˅ 47 | return this._name; 48 | // ˄ 49 | } 50 | 51 | get size(): number { 52 | // ˅ 53 | return this._size; 54 | // ˄ 55 | } 56 | 57 | // Print this element with the "upperPath". 58 | print(upperPath: string): void { 59 | // ˅ 60 | console.log(`${upperPath}/${this.toString()}`); 61 | // ˄ 62 | } 63 | 64 | // ˅ 65 | 66 | // ˄ 67 | } 68 | 69 | // ˅ 70 | 71 | // ˄ 72 | ``` 73 | 74 | Installation 75 | ------------ 76 | **UML Modeling Tool** 77 | * Download the modeling tool [Astah](https://astah.net/download) UML/Professional **ver.10.0.0** or higher, and install. 78 | * Download [m plus](https://sites.google.com/view/m-plus-plugin/download) plug-in **ver.3.1.3-preview.1** or higher, and add it to Astah. 79 | [How to add plugins to Astah](https://astahblog.com/2014/12/15/astah_plugins/) 80 | 81 | **TypeScript Development Environment** 82 | 1. Install [Visual Studio Code](https://code.visualstudio.com/download) **ver.1.46** or higher. 83 | 2. Install [Live Server](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer) extensions for VS Code. 84 | 3. Install [Chrome](https://www.google.com/intl/en/chrome/) browser. 85 | 4. Install [Node.js](https://nodejs.org/en/) **ver.12.3.1** or higher. 86 | 5. Install TypeScript and type definitions with the following commands using npm. 87 | `cd ` 88 | `npm install typescript @types/node --save-dev` 89 | 90 | Usage 91 | ----- 92 | **Code Generation from UML** 93 | 1. Open the Astah file (model/DesignPatternExamplesInTypescript.asta). 94 | 2. Select model elements on the model browser of Astah. 95 | 3. Click the **Generate Code** button. 96 | ![](./screenshots/GenerateCode.gif "Generate Code") 97 | The generated code has **User Code Area**. The User Code Area is the area enclosed by "˅" and "˄". Handwritten code written in the User Code Area remains after a re-generation. [View code example](#code-example). 98 | For detailed usage of the tools, please see [Astah Manual](https://astah.net/manual) and [m plus plug-in tips](https://sites.google.com/view/m-plus-plugin-tips). 99 | 100 | **Compile and Run** 101 | 1. Open the workspace file (design-pattern-examples-in-typescript.code-workspace) in VS Code. 102 | 2. Select the **Run** icon in the Activity Bar on the side of VS Code. 103 | 3. Select the configuration named Launch Program using the Configuration drop-down in the Run view. 104 | 4. Click the **Start Debugging** button. 105 | ![](./screenshots/CompileAndRun.gif "Compile and Run") 106 | 107 | *Note1*: Before running patterns **Command**, **Mediator**, and **State**, you need to click to Go Live from the status bar to turn a server on. After running those patterns, click again to turn the server off. 108 | ![](./screenshots/GoLiveButton.png "Go Live button") 109 | 110 | *Note2*: If you encounter these error messages, change the VS Code display language to "en" and restart VS Code. 111 | [How to change the display language](https://code.visualstudio.com/docs/getstarted/locales#_changing-the-display-language) 112 | ![](./screenshots/BuildError.png "Build Error") 113 | 114 | For detailed usage of the tools, please see [Astah Manual](https://astah.net/manual) and [m plus plug-in tips](https://sites.google.com/view/m-plus-plugin-tips). 115 | 116 | References 117 | ---------- 118 | * Gamma, E. et al. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994 119 | * Hiroshi Yuki. Learning Design Patterns in Java [In Japanese Language], Softbank publishing, 2004 120 | 121 | License 122 | ------- 123 | This project is licensed under the Creative Commons Zero (CC0) license. The model and code are completely free to use. 124 | 125 | [![CC0](https://i.creativecommons.org/p/zero/1.0/88x31.png "CC0")](https://creativecommons.org/publicdomain/zero/1.0/deed) 126 | 127 | Other Language Examples 128 | ----------------------- 129 | [C++](https://github.com/takaakit/design-pattern-examples-in-cpp), [C#](https://github.com/takaakit/design-pattern-examples-in-csharp), [Crystal](https://github.com/takaakit/design-pattern-examples-in-crystal), [Go](https://github.com/takaakit/design-pattern-examples-in-golang), [Java](https://github.com/takaakit/design-pattern-examples-in-java), [JavaScript](https://github.com/takaakit/design-pattern-examples-in-javascript), [Kotlin](https://github.com/takaakit/design-pattern-examples-in-kotlin), [Python](https://github.com/takaakit/design-pattern-examples-in-python), [Ruby](https://github.com/takaakit/design-pattern-examples-in-ruby), [Scala](https://github.com/takaakit/design-pattern-examples-in-scala), [Swift](https://github.com/takaakit/design-pattern-examples-in-swift) 130 | -------------------------------------------------------------------------------- /behavioral-patterns/chain-of-responsibility/lazy-supporter.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Supporter } from './supporter'; 5 | import { Trouble } from './trouble'; 6 | 7 | // ˄ 8 | 9 | export class LazySupporter extends Supporter { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | constructor(name: string) { 15 | // ˅ 16 | super(name); 17 | // ˄ 18 | } 19 | 20 | // No troubles are handled. 21 | protected canHandle(trouble: Trouble): boolean { 22 | // ˅ 23 | return false; 24 | // ˄ 25 | } 26 | 27 | // ˅ 28 | 29 | // ˄ 30 | } 31 | 32 | // ˅ 33 | 34 | // ˄ 35 | -------------------------------------------------------------------------------- /behavioral-patterns/chain-of-responsibility/limited-supporter.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Supporter } from './supporter'; 5 | import { Trouble } from './trouble'; 6 | 7 | // ˄ 8 | 9 | export class LimitedSupporter extends Supporter { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | private readonly limitId: number; 15 | 16 | constructor(name: string, limitId: number) { 17 | // ˅ 18 | super(name); 19 | this.limitId = limitId; 20 | // ˄ 21 | } 22 | 23 | // Troubles with an ID below the limit are handled. 24 | protected canHandle(trouble: Trouble): boolean { 25 | // ˅ 26 | return trouble.id <= this.limitId; 27 | // ˄ 28 | } 29 | 30 | // ˅ 31 | 32 | // ˄ 33 | } 34 | 35 | // ˅ 36 | 37 | // ˄ 38 | -------------------------------------------------------------------------------- /behavioral-patterns/chain-of-responsibility/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | A trouble is turned around among supporters, and the trouble will be handled by the supporter who can handle it. 3 | There are four types of supporters below: 4 | * LazySupporter doesn't handle any trouble 5 | * MoodySupporter handles odd ID troubles 6 | * SpecialSupporter handles a trouble of the target ID. 7 | * LimitedSupporter handles troubles below the limit ID. 8 | */ 9 | 'use strict'; 10 | 11 | import { Supporter } from './supporter'; 12 | import { LazySupporter } from './lazy-supporter'; 13 | import { MoodySupporter } from './moody-supporter'; 14 | import { SpecialSupporter } from './special-supporter'; 15 | import { LimitedSupporter } from './limited-supporter'; 16 | import { Trouble } from './trouble'; 17 | 18 | const emily: Supporter = new LazySupporter(`Emily`); 19 | const william: Supporter = new MoodySupporter(`William`); 20 | const amelia: Supporter = new SpecialSupporter(`Amelia`, 6); 21 | const joseph: Supporter = new LimitedSupporter(`Joseph`, 5); 22 | 23 | // Make a chain. 24 | emily.setNext(william).setNext(amelia).setNext(joseph); 25 | 26 | // Various troubles occurred. 27 | for (let i = 0; i < 10; i++) { 28 | emily.support(new Trouble(i)); 29 | } 30 | -------------------------------------------------------------------------------- /behavioral-patterns/chain-of-responsibility/moody-supporter.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Supporter } from './supporter'; 5 | import { Trouble } from './trouble'; 6 | 7 | // ˄ 8 | 9 | export class MoodySupporter extends Supporter { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | constructor(name: string) { 15 | // ˅ 16 | super(name); 17 | // ˄ 18 | } 19 | 20 | // Troubles with an odd ID are handled. 21 | protected canHandle(trouble: Trouble): boolean { 22 | // ˅ 23 | return trouble.id % 2 === 1; 24 | // ˄ 25 | } 26 | 27 | // ˅ 28 | 29 | // ˄ 30 | } 31 | 32 | // ˅ 33 | 34 | // ˄ 35 | -------------------------------------------------------------------------------- /behavioral-patterns/chain-of-responsibility/special-supporter.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Supporter } from './supporter'; 5 | import { Trouble } from './trouble'; 6 | 7 | // ˄ 8 | 9 | export class SpecialSupporter extends Supporter { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | private readonly targetId: number; 15 | 16 | constructor(name: string, targetId: number) { 17 | // ˅ 18 | super(name); 19 | this.targetId = targetId; 20 | // ˄ 21 | } 22 | 23 | // Troubles with the specific ID are handled. 24 | protected canHandle(trouble: Trouble): boolean { 25 | // ˅ 26 | return trouble.id === this.targetId; 27 | // ˄ 28 | } 29 | 30 | // ˅ 31 | 32 | // ˄ 33 | } 34 | 35 | // ˅ 36 | 37 | // ˄ 38 | -------------------------------------------------------------------------------- /behavioral-patterns/chain-of-responsibility/supporter.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Trouble } from './trouble'; 5 | 6 | // ˄ 7 | 8 | export abstract class Supporter { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | // Supporter name 14 | private readonly name: string; 15 | 16 | // Next supporter 17 | private next: Supporter; 18 | 19 | constructor(name: string) { 20 | // ˅ 21 | this.next = null; 22 | this.name = name; 23 | // ˄ 24 | } 25 | 26 | // Trouble support 27 | // Troubles are sent around. 28 | support(trouble: Trouble): void { 29 | // ˅ 30 | if (this.canHandle(trouble)) { 31 | this.supported(trouble); 32 | } 33 | else if (this.next !== null) { 34 | this.next.support(trouble); 35 | } 36 | else { 37 | this.unsupported(trouble); 38 | } 39 | // ˄ 40 | } 41 | 42 | // Set a next supporter and return it. 43 | setNext(next: Supporter): Supporter { 44 | // ˅ 45 | this.next = next; 46 | return this.next; 47 | // ˄ 48 | } 49 | 50 | protected abstract canHandle(trouble: Trouble): boolean; 51 | 52 | // Trouble was supported. 53 | private supported(trouble: Trouble): void { 54 | // ˅ 55 | console.log(`${trouble.toString()} was handled by ${this.name}.`); 56 | // ˄ 57 | } 58 | 59 | // Trouble was unsupported. 60 | private unsupported(trouble: Trouble): void { 61 | // ˅ 62 | console.log(`${trouble.toString()} was not handled.`); 63 | // ˄ 64 | } 65 | 66 | // ˅ 67 | 68 | // ˄ 69 | } 70 | 71 | // ˅ 72 | 73 | // ˄ 74 | -------------------------------------------------------------------------------- /behavioral-patterns/chain-of-responsibility/trouble.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export class Trouble { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | // Trouble number 12 | readonly id: number; 13 | 14 | constructor(id: number) { 15 | // ˅ 16 | this.id = id; 17 | // ˄ 18 | } 19 | 20 | toString(): string { 21 | // ˅ 22 | return `[Trouble ${this.id}]`; 23 | // ˄ 24 | } 25 | 26 | // ˅ 27 | 28 | // ˄ 29 | } 30 | 31 | // ˅ 32 | 33 | // ˄ 34 | -------------------------------------------------------------------------------- /behavioral-patterns/chain-of-responsibility/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/command/app-main.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Command } from './command'; 5 | import { PaintingCommand } from './painting-command'; 6 | import { HistoryCommand } from './history-command'; 7 | import { PaintingCanvas } from './painting-canvas'; 8 | 9 | // ˄ 10 | 11 | export class AppMain { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private isDragging: boolean; 17 | 18 | // Painting history 19 | private readonly history: HistoryCommand; 20 | 21 | private readonly paintingCanvas: PaintingCanvas; 22 | 23 | private readonly canvas: HTMLCanvasElement; 24 | 25 | private readonly undoButton: HTMLButtonElement; 26 | 27 | private readonly clearButton: HTMLButtonElement; 28 | 29 | constructor() { 30 | // ˅ 31 | this.isDragging = false; 32 | this.canvas = document.getElementById(`canvas`); 33 | this.undoButton = document.getElementById(`undoButton`) 34 | this.clearButton = document.getElementById(`clearButton`) 35 | this.history = new HistoryCommand(); 36 | this.paintingCanvas = new PaintingCanvas(this.canvas.getContext(`2d`), this.canvas.width, this.canvas.height); 37 | 38 | this.canvas.addEventListener(`mousedown`, () => this.onMouseDown()); 39 | this.canvas.addEventListener(`mousemove`, (event) => this.onMouseMove(event)); 40 | this.canvas.addEventListener(`mouseup`, () => this.onMouseUp()); 41 | this.undoButton.addEventListener(`click`, () => this.onUndoButtonClick()); 42 | this.clearButton.addEventListener(`click`, () => this.onClearButtonClick()); 43 | 44 | this.paintingCanvas.clear(); 45 | // ˄ 46 | } 47 | 48 | private onMouseDown(): void { 49 | // ˅ 50 | this.isDragging = true; 51 | // ˄ 52 | } 53 | 54 | private onMouseMove(event: MouseEvent): void { 55 | // ˅ 56 | if(this.isDragging){ 57 | const paintingPosX = event.clientX - this.canvas.getBoundingClientRect().left; 58 | const paintingPosY = event.clientY - this.canvas.getBoundingClientRect().top ; 59 | const command: Command = new PaintingCommand(this.paintingCanvas, paintingPosX, paintingPosY); 60 | this.history.add(command); 61 | command.execute(); 62 | } 63 | // ˄ 64 | } 65 | 66 | private onMouseUp(): void { 67 | // ˅ 68 | this.isDragging = false; 69 | // ˄ 70 | } 71 | 72 | private onUndoButtonClick(): void { 73 | // ˅ 74 | this.paintingCanvas.clear(); 75 | this.history.undo(); 76 | this.history.execute(); 77 | // ˄ 78 | } 79 | 80 | private onClearButtonClick(): void { 81 | // ˅ 82 | this.paintingCanvas.clear(); 83 | this.history.clear(); 84 | // ˄ 85 | } 86 | 87 | // ˅ 88 | 89 | // ˄ 90 | } 91 | 92 | // ˅ 93 | 94 | // ˄ 95 | -------------------------------------------------------------------------------- /behavioral-patterns/command/command.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface Command { 7 | 8 | execute(): void; 9 | 10 | // ˅ 11 | 12 | // ˄ 13 | } 14 | 15 | // ˅ 16 | 17 | // ˄ 18 | -------------------------------------------------------------------------------- /behavioral-patterns/command/history-command.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Command } from './command'; 5 | 6 | // ˄ 7 | 8 | // Holder of the past commands 9 | export class HistoryCommand implements Command { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | // A set of past commands 15 | private readonly pastCommands: Array; 16 | 17 | constructor() { 18 | // ˅ 19 | this.pastCommands = new Array(); 20 | // ˄ 21 | } 22 | 23 | // Execute all past commands 24 | execute(): void { 25 | // ˅ 26 | for (let command of this.pastCommands) { 27 | command.execute(); 28 | } 29 | // ˄ 30 | } 31 | 32 | add(command: Command): void { 33 | // ˅ 34 | this.pastCommands.push(command); 35 | // ˄ 36 | } 37 | 38 | // Delete the last command 39 | undo(): void { 40 | // ˅ 41 | if (this.pastCommands.length !== 0) { 42 | this.pastCommands.pop(); 43 | } 44 | // ˄ 45 | } 46 | 47 | // Delete all past commands 48 | clear(): void { 49 | // ˅ 50 | this.pastCommands.length = 0; 51 | // ˄ 52 | } 53 | 54 | // ˅ 55 | 56 | // ˄ 57 | } 58 | 59 | // ˅ 60 | 61 | // ˄ 62 | -------------------------------------------------------------------------------- /behavioral-patterns/command/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | Command Example 9 | 10 | 11 | 12 |
13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /behavioral-patterns/command/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Simple drawing application: 3 | * Draw a path with points by dragging the mouse. 4 | * Revert to one previous drawing by pressing the Undo button. 5 | * Erase all drawing by pressing the Clear button. 6 | */ 7 | 'use strict'; 8 | 9 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 10 | // Before launching this Command project, click to "Go Live" from the status bar of VSCode to turn a server on. // 11 | ////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 12 | 13 | import { AppMain } from './app-main'; 14 | new AppMain(); 15 | -------------------------------------------------------------------------------- /behavioral-patterns/command/painting-canvas.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { PaintingTarget } from './painting-target'; 5 | 6 | // ˄ 7 | 8 | export class PaintingCanvas implements PaintingTarget { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly COLOR: string; 14 | 15 | // Radius of the painting point 16 | private readonly POINT_RADIUS: number; 17 | 18 | private readonly width: number; 19 | 20 | private readonly height: number; 21 | 22 | private readonly context: CanvasRenderingContext2D; 23 | 24 | constructor(context: CanvasRenderingContext2D, width: number, height: number) { 25 | // ˅ 26 | this.COLOR = `lightgreen`; 27 | this.POINT_RADIUS = 10; 28 | this.context = context; 29 | this.width = width; 30 | this.height = height; 31 | // ˄ 32 | } 33 | 34 | paint(x: number, y: number): void { 35 | // ˅ 36 | this.context.beginPath(); 37 | this.context.arc(x, y, this.POINT_RADIUS, 0, Math.PI*2, false); 38 | this.context.fillStyle = this.COLOR; 39 | this.context.fill(); 40 | // ˄ 41 | } 42 | 43 | clear(): void { 44 | // ˅ 45 | this.context.clearRect(0, 0, this.width, this.height); 46 | // ˄ 47 | } 48 | 49 | // ˅ 50 | 51 | // ˄ 52 | } 53 | 54 | // ˅ 55 | 56 | // ˄ 57 | -------------------------------------------------------------------------------- /behavioral-patterns/command/painting-command.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Command } from './command'; 5 | import { PaintingTarget } from './painting-target'; 6 | 7 | // ˄ 8 | 9 | // Command to paint a single point 10 | export class PaintingCommand implements Command { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | // Painting position x 16 | private readonly paintingPosX: number; 17 | 18 | // Painting position y 19 | private readonly paintingPosY: number; 20 | 21 | private paintingTarget: PaintingTarget; 22 | 23 | constructor(paintingObject: PaintingTarget, paintingPosX: number, paintingPosY: number) { 24 | // ˅ 25 | this.paintingPosX = paintingPosX; 26 | this.paintingPosY = paintingPosY; 27 | this.paintingTarget = paintingObject; 28 | // ˄ 29 | } 30 | 31 | execute(): void { 32 | // ˅ 33 | this.paintingTarget.paint(this.paintingPosX, this.paintingPosY); 34 | // ˄ 35 | } 36 | 37 | // ˅ 38 | 39 | // ˄ 40 | } 41 | 42 | // ˅ 43 | 44 | // ˄ 45 | -------------------------------------------------------------------------------- /behavioral-patterns/command/painting-target.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface PaintingTarget { 7 | 8 | paint(x: number, y: number): void; 9 | 10 | clear(): void; 11 | 12 | // ˅ 13 | 14 | // ˄ 15 | } 16 | 17 | // ˅ 18 | 19 | // ˄ 20 | -------------------------------------------------------------------------------- /behavioral-patterns/command/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "umd", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/action.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Node } from './node'; 5 | import { Context } from './context'; 6 | 7 | // ˄ 8 | 9 | // A node corresponding to "forward", "left", and "right". 10 | export class Action implements Node { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | private name: string; 16 | 17 | constructor() { 18 | // ˅ 19 | 20 | // ˄ 21 | } 22 | 23 | parse(context: Context): void { 24 | // ˅ 25 | const currentToken: string = context.getToken(); 26 | if (currentToken !== `forward` && currentToken !== `right` && currentToken !== `left`) { 27 | throw new Error(`${currentToken} is unknown`); 28 | } 29 | 30 | this.name = currentToken; 31 | 32 | context.slideToken(currentToken); 33 | // ˄ 34 | } 35 | 36 | toString(): string { 37 | // ˅ 38 | return this.name; 39 | // ˄ 40 | } 41 | 42 | // ˅ 43 | 44 | // ˄ 45 | } 46 | 47 | // ˅ 48 | 49 | // ˄ 50 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/command-list.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Node } from './node'; 5 | import { Context } from './context'; 6 | import { Command } from './command'; 7 | 8 | // ˄ 9 | 10 | export class CommandList implements Node { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | private readonly nodes: Array; 16 | 17 | constructor() { 18 | // ˅ 19 | this.nodes = new Array(); 20 | // ˄ 21 | } 22 | 23 | parse(context: Context): void { 24 | // ˅ 25 | while (true) { 26 | if (context.getToken() === null) { 27 | throw new Error(`Missing "end"`); 28 | } 29 | else if (context.getToken() === `end`) { 30 | context.slideToken(`end`); 31 | break; 32 | } 33 | else { 34 | const aNode: Node = new Command(); 35 | aNode.parse(context); 36 | 37 | this.nodes.push(aNode); // Hold the parsed node 38 | } 39 | } 40 | // ˄ 41 | } 42 | 43 | toString(): string { 44 | // ˅ 45 | return `[${this.nodes.join(`, `)}]`; 46 | // ˄ 47 | } 48 | 49 | // ˅ 50 | 51 | // ˄ 52 | } 53 | 54 | // ˅ 55 | 56 | // ˄ 57 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/command.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Node } from './node'; 5 | import { Context } from './context'; 6 | import { Repeat } from './repeat'; 7 | import { Action } from './action'; 8 | 9 | // ˄ 10 | 11 | export class Command implements Node { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private node: Node; 17 | 18 | constructor() { 19 | // ˅ 20 | this.node = null; 21 | // ˄ 22 | } 23 | 24 | parse(context: Context): void { 25 | // ˅ 26 | let aNode: Node; 27 | if (context.getToken() === `repeat`) { 28 | aNode = new Repeat(); 29 | aNode.parse(context); 30 | } 31 | else { 32 | aNode = new Action(); 33 | aNode.parse(context); 34 | } 35 | 36 | this.node = aNode; // Hold the parsed node 37 | // ˄ 38 | } 39 | 40 | toString(): string { 41 | // ˅ 42 | return this.node.toString(); 43 | // ˄ 44 | } 45 | 46 | // ˅ 47 | 48 | // ˄ 49 | } 50 | 51 | // ˅ 52 | 53 | // ˄ 54 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/context.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | // Hold data which will be interpreted. 7 | export class Context { 8 | // ˅ 9 | 10 | // ˄ 11 | 12 | private tokens: Array; 13 | 14 | private currentIndex: number; 15 | 16 | constructor(text: string) { 17 | // ˅ 18 | this.tokens = text.split(/\s+/); 19 | this.currentIndex = 0; 20 | // ˄ 21 | } 22 | 23 | nextToken(): string { 24 | // ˅ 25 | if (this.currentIndex < this.tokens.length) { 26 | return this.tokens[this.currentIndex++]; 27 | } 28 | else { 29 | return null; 30 | } 31 | // ˄ 32 | } 33 | 34 | getToken(): string { 35 | // ˅ 36 | return this.tokens[this.currentIndex]; 37 | // ˄ 38 | } 39 | 40 | slideToken(token: string): void { 41 | // ˅ 42 | if (token !== this.getToken()) { 43 | throw new Error(`WARNING: ${token} is expected but ${this.getToken()} was found.`); 44 | } 45 | this.nextToken(); 46 | // ˄ 47 | } 48 | 49 | getNumber(): number { 50 | // ˅ 51 | try { 52 | return parseInt(this.getToken()); 53 | } 54 | catch { 55 | throw new Error(`WARNING: ${this.getToken()}`); 56 | } 57 | // ˄ 58 | } 59 | 60 | // ˅ 61 | 62 | // ˄ 63 | } 64 | 65 | // ˅ 66 | 67 | // ˄ 68 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | An interpreter for mini language to operate radio controlled car. It parses the following syntax 3 | composed of "forward", "left", "right", and "repeat" commands: 4 | ``` 5 | ::= program 6 | ::= * end 7 | ::= | 8 | ::= repeat 9 | ::= forward | right | left 10 | ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 11 | ``` 12 | ___ 13 | Examples before and after syntax analysis. 14 | 15 | Ex.1 16 | ``` 17 | Before parsing : program end 18 | After parsing : [program []] 19 | ``` 20 | 21 | Ex.2 22 | ``` 23 | Before parsing : program forward right left end 24 | After parsing : [program [forward, right, left]] 25 | ``` 26 | 27 | Ex.3 28 | ``` 29 | Before parsing : program repeat 4 forward right end end 30 | After parsing : [program [repeat 4 [forward, right]]] 31 | ``` 32 | */ 33 | 'use strict'; 34 | 35 | import { Node } from './node'; 36 | import { Program } from './program'; 37 | import { Context } from './context'; 38 | import * as fs from 'fs'; 39 | import * as readline from 'readline'; 40 | 41 | // Reads commands line by line from the "program.txt" and parses them. 42 | const stream = fs.createReadStream(process.cwd() + `/program.txt`, `utf8`); 43 | const reader = readline.createInterface({ input: stream }); 44 | reader.on(`line`, (line: string) => { 45 | console.log(`Before parsing : ${line}`); 46 | const node: Node = new Program(); 47 | node.parse(new Context(line)); 48 | console.log(`After parsing : ${node}`); 49 | }); 50 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/node.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Context } from './context'; 5 | 6 | // ˄ 7 | 8 | // Node in the syntax tree. 9 | export interface Node { 10 | 11 | parse(context: Context): void; 12 | 13 | toString(): string; 14 | 15 | // ˅ 16 | 17 | // ˄ 18 | } 19 | 20 | // ˅ 21 | 22 | // ˄ 23 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/program.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Node } from './node'; 5 | import { Context } from './context'; 6 | import { CommandList } from './command-list'; 7 | 8 | // ˄ 9 | 10 | // A node corresponding to "program". 11 | export class Program implements Node { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private commandList: Node; 17 | 18 | constructor() { 19 | // ˅ 20 | this.commandList = null; 21 | // ˄ 22 | } 23 | 24 | parse(context: Context): void { 25 | // ˅ 26 | context.slideToken(`program`); 27 | 28 | const aCommandList: CommandList = new CommandList(); 29 | aCommandList.parse(context); 30 | 31 | this.commandList = aCommandList; // Hold the parsed command list 32 | // ˄ 33 | } 34 | 35 | toString(): string { 36 | // ˅ 37 | return (`[program ${this.commandList}]`); 38 | // ˄ 39 | } 40 | 41 | // ˅ 42 | 43 | // ˄ 44 | } 45 | 46 | // ˅ 47 | 48 | // ˄ 49 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/program.txt: -------------------------------------------------------------------------------- 1 | program end 2 | program forward right left end 3 | program forward right forward right forward right forward right end 4 | program repeat 4 forward right end end 5 | program repeat 2 repeat 2 forward right forward right end end end 6 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/repeat.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Node } from './node'; 5 | import { Context } from './context'; 6 | import { CommandList } from './command-list'; 7 | 8 | // ˄ 9 | 10 | // A node corresponding to "repeat". 11 | export class Repeat implements Node { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private number: number; 17 | 18 | private commandList: Node; 19 | 20 | constructor() { 21 | // ˅ 22 | this.number = 0; 23 | this.commandList = null; 24 | // ˄ 25 | } 26 | 27 | parse(context: Context): void { 28 | // ˅ 29 | context.slideToken(`repeat`); 30 | 31 | this.number = context.getNumber(); 32 | context.slideToken(String(this.number)); 33 | 34 | const aCommandList = new CommandList(); 35 | aCommandList.parse(context); 36 | 37 | this.commandList = aCommandList; // Hold the parsed command list 38 | // ˄ 39 | } 40 | 41 | toString(): string { 42 | // ˅ 43 | return `repeat ${this.number} ${this.commandList}`; 44 | // ˄ 45 | } 46 | 47 | // ˅ 48 | 49 | // ˄ 50 | } 51 | 52 | // ˅ 53 | 54 | // ˄ 55 | -------------------------------------------------------------------------------- /behavioral-patterns/interpreter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/iterator/aggregate.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Iterator } from './iterator'; 5 | 6 | // ˄ 7 | 8 | export interface Aggregate { 9 | 10 | iterator(): Iterator; 11 | 12 | // ˅ 13 | 14 | // ˄ 15 | } 16 | 17 | // ˅ 18 | 19 | // ˄ 20 | -------------------------------------------------------------------------------- /behavioral-patterns/iterator/book-shelf-iterator.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Iterator } from './iterator'; 5 | import { BookShelf } from './book-shelf'; 6 | import { Book } from './book'; 7 | 8 | // ˄ 9 | 10 | export class BookShelfIterator implements Iterator { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | private index: number; 16 | 17 | private readonly bookShelf: BookShelf; 18 | 19 | constructor(bookShelf: BookShelf) { 20 | // ˅ 21 | this.index = 0; 22 | this.bookShelf = bookShelf; 23 | // ˄ 24 | } 25 | 26 | hasNext(): boolean { 27 | // ˅ 28 | return this.index < this.bookShelf.numberOfBooks; 29 | // ˄ 30 | } 31 | 32 | next(): any { 33 | // ˅ 34 | const book: Book = this.bookShelf.getAt(this.index); 35 | this.index++; 36 | return book; 37 | // ˄ 38 | } 39 | 40 | // ˅ 41 | 42 | // ˄ 43 | } 44 | 45 | // ˅ 46 | 47 | // ˄ 48 | -------------------------------------------------------------------------------- /behavioral-patterns/iterator/book-shelf.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Aggregate } from './aggregate'; 5 | import { Iterator } from './iterator'; 6 | import { Book } from './book'; 7 | import { BookShelfIterator } from './book-shelf-iterator'; 8 | 9 | // ˄ 10 | 11 | export class BookShelf implements Aggregate { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private _numberOfBooks: number; 17 | 18 | private readonly books: Array; 19 | 20 | constructor(maxsize: number) { 21 | // ˅ 22 | this.books = new Array(maxsize); 23 | this._numberOfBooks = 0; 24 | // ˄ 25 | } 26 | 27 | iterator(): Iterator { 28 | // ˅ 29 | return new BookShelfIterator(this); 30 | // ˄ 31 | } 32 | 33 | getAt(index: number): Book { 34 | // ˅ 35 | return this.books[index]; 36 | // ˄ 37 | } 38 | 39 | add(book: Book): void { 40 | // ˅ 41 | this.books[this._numberOfBooks] = book; 42 | this._numberOfBooks++; 43 | // ˄ 44 | } 45 | 46 | get numberOfBooks(): number { 47 | // ˅ 48 | return this._numberOfBooks; 49 | // ˄ 50 | } 51 | 52 | // ˅ 53 | 54 | // ˄ 55 | } 56 | 57 | // ˅ 58 | 59 | // ˄ 60 | -------------------------------------------------------------------------------- /behavioral-patterns/iterator/book.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export class Book { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | readonly title: string; 12 | 13 | constructor(title: string) { 14 | // ˅ 15 | this.title = title; 16 | // ˄ 17 | } 18 | 19 | // ˅ 20 | 21 | // ˄ 22 | } 23 | 24 | // ˅ 25 | 26 | // ˄ 27 | -------------------------------------------------------------------------------- /behavioral-patterns/iterator/iterator.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface Iterator { 7 | 8 | hasNext(): boolean; 9 | 10 | next(): any; 11 | 12 | // ˅ 13 | 14 | // ˄ 15 | } 16 | 17 | // ˅ 18 | 19 | // ˄ 20 | -------------------------------------------------------------------------------- /behavioral-patterns/iterator/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Add books in a bookshelf and display the names of the book in turn. 3 | */ 4 | 'use strict'; 5 | 6 | import { Iterator } from './iterator'; 7 | import { BookShelf } from './book-shelf'; 8 | import { Book } from './book'; 9 | 10 | const bookShelf: BookShelf = new BookShelf(5); 11 | bookShelf.add(new Book(`Design Patterns: Elements of Reusable Object-Oriented Software`)); 12 | bookShelf.add(new Book(`The Object Primer: Agile Model-Driven Development with UML 2.0`)); 13 | bookShelf.add(new Book(`Software Systems Architecture: Working With Stakeholders Using Viewpoints and Perspectives`)); 14 | bookShelf.add(new Book(`A Practical Guide to SysML: The Systems Modeling Language`)); 15 | bookShelf.add(new Book(`A Pattern Language: Towns, Buildings, Construction`)); 16 | 17 | const iterator: Iterator = bookShelf.iterator(); 18 | while (iterator.hasNext()) { 19 | const book: Book = iterator.next(); 20 | console.log(book.title); 21 | } 22 | -------------------------------------------------------------------------------- /behavioral-patterns/iterator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/app-login.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Mediator } from './mediator'; 5 | import { ColleagueRadioButton } from './colleague-radio-button'; 6 | import { ColleagueTextField } from './colleague-text-field'; 7 | import { ColleagueButton } from './colleague-button'; 8 | 9 | // ˄ 10 | 11 | export class AppLogin implements Mediator { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private radioLogin: ColleagueRadioButton; 17 | 18 | private radioGuest: ColleagueRadioButton; 19 | 20 | private textUsername: ColleagueTextField; 21 | 22 | private textPassword: ColleagueTextField; 23 | 24 | private buttonOk: ColleagueButton; 25 | 26 | private buttonCancel: ColleagueButton; 27 | 28 | constructor() { 29 | // ˅ 30 | // Create TextField, Button and RadioButton 31 | this.createColleagues(); 32 | 33 | // Set mediators 34 | this.radioGuest.mediator = this; 35 | this.radioLogin.mediator = this; 36 | this.textUsername.mediator = this; 37 | this.textPassword.mediator = this; 38 | this.buttonOk.mediator = this; 39 | this.buttonCancel.mediator = this; 40 | 41 | // Generate a click event of the Guest radio button. 42 | (document.getElementById(`radioGuest`)).click(); 43 | // ˄ 44 | } 45 | 46 | createColleagues(): void { 47 | // ˅ 48 | this.radioGuest = new ColleagueRadioButton(document.getElementById(`radioGuest`)); 49 | this.radioLogin = new ColleagueRadioButton(document.getElementById(`radioLogin`)); 50 | this.textUsername = new ColleagueTextField(document.getElementById(`textUsername`)); 51 | this.textPassword = new ColleagueTextField(document.getElementById(`textPassword`)); 52 | this.buttonOk = new ColleagueButton(document.getElementById(`buttonOk`)); 53 | this.buttonCancel = new ColleagueButton(document.getElementById(`buttonCancel`)); 54 | // ˄ 55 | } 56 | 57 | // Change enable/disable of the Colleagues when notified from the Mediators. 58 | colleagueChanged(): void { 59 | // ˅ 60 | if (this.buttonOk.isPressed() || this.buttonCancel.isPressed()) { 61 | document.body.innerHTML = `

Dialog terminated.

` // Display a termination message 62 | } 63 | else { 64 | if (this.radioGuest.isSelected()) { // Guest mode 65 | this.textUsername.setActivation(false); 66 | this.textPassword.setActivation(false); 67 | this.buttonOk.setActivation(true); 68 | } 69 | else { // Login mode 70 | this.textUsername.setActivation(true); 71 | this.textPassword.setActivation(true); 72 | 73 | // Judge whether the changed Colleage is enabled or disabled 74 | if (this.textUsername.isEmpty() === false 75 | && this.textPassword.isEmpty() === false) { 76 | this.buttonOk.setActivation(true); 77 | } 78 | else { 79 | this.buttonOk.setActivation(false); 80 | } 81 | } 82 | } 83 | // ˄ 84 | } 85 | 86 | // ˅ 87 | 88 | // ˄ 89 | } 90 | 91 | // ˅ 92 | 93 | // ˄ 94 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/colleague-button.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Colleague } from './colleague'; 5 | 6 | // ˄ 7 | 8 | export class ColleagueButton extends Colleague { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private _isPressed: boolean; 14 | 15 | private readonly button: HTMLButtonElement; 16 | 17 | constructor(button: HTMLButtonElement) { 18 | // ˅ 19 | super(); 20 | this.button = button; 21 | this._isPressed = false; 22 | this.button.addEventListener(`mousedown`, (event) => this.pressedButton(event)); 23 | this.button.addEventListener(`mouseup`, (event) => this.releasedButton(event)); 24 | // ˄ 25 | } 26 | 27 | // Set enable/disable from the Mediator 28 | setActivation(isEnable: boolean): void { 29 | // ˅ 30 | this.button.disabled = !isEnable; 31 | // ˄ 32 | } 33 | 34 | isPressed(): boolean { 35 | // ˅ 36 | return this._isPressed; 37 | // ˄ 38 | } 39 | 40 | private pressedButton(event: MouseEvent): void { 41 | // ˅ 42 | this._isPressed = true; 43 | // ˄ 44 | } 45 | 46 | private releasedButton(event: MouseEvent): void { 47 | // ˅ 48 | this._mediator.colleagueChanged(); 49 | this._isPressed = false; 50 | // ˄ 51 | } 52 | 53 | // ˅ 54 | 55 | // ˄ 56 | } 57 | 58 | // ˅ 59 | 60 | // ˄ 61 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/colleague-radio-button.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Colleague } from './colleague'; 5 | 6 | // ˄ 7 | 8 | export class ColleagueRadioButton extends Colleague { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly radioButton: HTMLInputElement; 14 | 15 | constructor(radioButton: HTMLInputElement) { 16 | // ˅ 17 | super(); 18 | this.radioButton = radioButton; 19 | this.radioButton.addEventListener(`click`, (event) => this.clickRadioButton(event)); 20 | // ˄ 21 | } 22 | 23 | // Set enable/disable from the Mediator 24 | setActivation(isEnable: boolean): void { 25 | // ˅ 26 | this.radioButton.disabled = !isEnable; 27 | // ˄ 28 | } 29 | 30 | isSelected(): boolean { 31 | // ˅ 32 | return this.radioButton.checked; 33 | // ˄ 34 | } 35 | 36 | private clickRadioButton(event: MouseEvent): void { 37 | // ˅ 38 | this._mediator.colleagueChanged(); 39 | // ˄ 40 | } 41 | 42 | // ˅ 43 | 44 | // ˄ 45 | } 46 | 47 | // ˅ 48 | 49 | // ˄ 50 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/colleague-text-field.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Colleague } from './colleague'; 5 | 6 | // ˄ 7 | 8 | export class ColleagueTextField extends Colleague { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly textArea: HTMLTextAreaElement; 14 | 15 | constructor(textArea: HTMLTextAreaElement) { 16 | // ˅ 17 | super(); 18 | this.textArea = textArea; 19 | this.textArea.addEventListener(`input`, (event) => this.inputTextArea(event)); 20 | // ˄ 21 | } 22 | 23 | // Set enable/disable from the Mediator 24 | setActivation(isEnable: boolean): void { 25 | // ˅ 26 | this.textArea.disabled = !isEnable; 27 | // ˄ 28 | } 29 | 30 | isEmpty(): boolean { 31 | // ˅ 32 | return this.textArea.value.length === 0 33 | // ˄ 34 | } 35 | 36 | private inputTextArea(event: Event): void { 37 | // ˅ 38 | this._mediator.colleagueChanged(); 39 | // ˄ 40 | } 41 | 42 | // ˅ 43 | 44 | // ˄ 45 | } 46 | 47 | // ˅ 48 | 49 | // ˄ 50 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/colleague.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Mediator } from './mediator'; 5 | 6 | // ˄ 7 | 8 | export abstract class Colleague { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | protected _mediator: Mediator; 14 | 15 | constructor() { 16 | // ˅ 17 | this._mediator = null; 18 | // ˄ 19 | } 20 | 21 | // Set enable/disable from the Mediator 22 | abstract setActivation(isEnable: boolean): void; 23 | 24 | set mediator(mediator: Mediator) { 25 | // ˅ 26 | this._mediator = mediator; 27 | // ˄ 28 | } 29 | 30 | // ˅ 31 | 32 | // ˄ 33 | } 34 | 35 | // ˅ 36 | 37 | // ˄ 38 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | Mediator Example 9 | 10 | 11 | 12 | Guest 13 | Login 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
UserName :
Password :
24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Show a login dialog for entering a username and password. The dialog has the following elements: 3 | * "Guest" and "Login" radio buttons 4 | * "Username" and "Password" text fields 5 | * "OK" and "Cancel" buttons 6 | 7 | And change the editable properties of the elements depending on the state of the radio buttons and text fields. 8 | */ 9 | 'use strict'; 10 | 11 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 12 | // Before launching this Mediator project, click to "Go Live" from the status bar of VSCode to turn a server on. // 13 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 14 | 15 | import { AppLogin } from './app-login'; 16 | new AppLogin(); 17 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/mediator.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface Mediator { 7 | 8 | createColleagues(): void; 9 | 10 | colleagueChanged(): void; 11 | 12 | // ˅ 13 | 14 | // ˄ 15 | } 16 | 17 | // ˅ 18 | 19 | // ˄ 20 | -------------------------------------------------------------------------------- /behavioral-patterns/mediator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "umd", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/memento/gamer.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Memento } from './memento'; 5 | 6 | // ˄ 7 | 8 | export class Gamer { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | // Gamer's money 14 | private _money: number; 15 | 16 | constructor(money: number) { 17 | // ˅ 18 | this._money = money; 19 | // ˄ 20 | } 21 | 22 | createMemento(): Memento { 23 | // ˅ 24 | return new Memento(this._money); 25 | // ˄ 26 | } 27 | 28 | setMemento(memento: Memento): void { 29 | // ˅ 30 | this._money = memento.money; 31 | // ˄ 32 | } 33 | 34 | // Play a game 35 | play(): void { 36 | // ˅ 37 | const dice: number = Math.floor(Math.random() * 6) + 1; // Shake a dice 38 | console.log(`The number of dice is ${dice}.`); 39 | 40 | const preMoney = this._money; 41 | switch (dice) { 42 | case 1: 43 | case 3: 44 | case 5: 45 | // In case of odd...Money is halved 46 | this._money = this._money / 2; 47 | console.log(`Gamer's money is halved: ${preMoney} -> ${this._money}`); 48 | break; 49 | case 2: 50 | case 4: 51 | case 6: 52 | // In case of even...Money doubles 53 | this._money = this._money * 2; 54 | console.log(`Gamer's money doubles: ${preMoney} -> ${this._money}`); 55 | break; 56 | default: 57 | // Other...Exit 58 | console.error(`Unexpected value.`); 59 | process.exit(1); 60 | } 61 | // ˄ 62 | } 63 | 64 | get money(): number { 65 | // ˅ 66 | return this._money; 67 | // ˄ 68 | } 69 | 70 | toString(): string { 71 | // ˅ 72 | return `[money = ${this._money}]`; 73 | // ˄ 74 | } 75 | 76 | // ˅ 77 | 78 | // ˄ 79 | } 80 | 81 | // ˅ 82 | 83 | // ˄ 84 | -------------------------------------------------------------------------------- /behavioral-patterns/memento/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | A dice game in which money increases and decreases: 3 | * A gamer shakes a dice and the number determine the next state. 4 | * If the number of dice is even, gamer's money doubles, and if it is odd, gamer's money is halved. 5 | * If the gamer's money is less than half of the highest amount, it returns to the highest amount. 6 | * The game is repeated. 7 | */ 8 | 'use strict'; 9 | 10 | import { Gamer } from './gamer'; 11 | import { Memento } from './memento'; 12 | 13 | const gamer: Gamer = new Gamer(100); // The initial money is 100 14 | let memento: Memento = gamer.createMemento(); // Save the initial state 15 | 16 | (async () => { // Note: Define an anonymous function with async keyword for using await keyword. 17 | for (let i = 0; i < 10; i++) { 18 | console.log(`==== Turn ${i + 1}`); // Display count 19 | 20 | gamer.play(); // Play a game 21 | 22 | // Determine the behavior of the Memento 23 | if (gamer.money > memento.money) { 24 | console.log(`(Gamer's money is the highest ever, so record the current state.)`); 25 | memento = gamer.createMemento(); 26 | } 27 | else if (gamer.money < memento.money / 2) { 28 | console.log(`(Gamer's money is less than half of the highest amount, so return to the recorded state.)`); 29 | gamer.setMemento(memento); 30 | console.log(`Gamer's money returns to ${gamer.money}.`); 31 | } 32 | 33 | console.log(); 34 | 35 | await new Promise(r => setTimeout(r, 1000)); // Wait 1s 36 | } 37 | })(); 38 | -------------------------------------------------------------------------------- /behavioral-patterns/memento/memento.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export class Memento { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | // Money 12 | private _money: number; 13 | 14 | constructor(money: number) { 15 | // ˅ 16 | this._money = money; 17 | // ˄ 18 | } 19 | 20 | get money(): number { 21 | // ˅ 22 | return this._money; 23 | // ˄ 24 | } 25 | 26 | // ˅ 27 | 28 | // ˄ 29 | } 30 | 31 | // ˅ 32 | 33 | // ˄ 34 | -------------------------------------------------------------------------------- /behavioral-patterns/memento/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/observer/bar-chart-observer.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Subject } from './subject'; 5 | import { NumberSubject } from './number-subject'; 6 | import { Observer } from './observer'; 7 | 8 | // ˄ 9 | 10 | // Display values as a bar chart. 11 | export class BarChartObserver implements Observer { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private numberSubject: NumberSubject; 17 | 18 | constructor(numberSubject: NumberSubject) { 19 | // ˅ 20 | this.numberSubject = numberSubject; 21 | 22 | // ˄ 23 | } 24 | 25 | update(changedSubject: Subject): void { 26 | // ˅ 27 | // Before processing, it checks to make sure the changed subject is the subject held. 28 | if (changedSubject === this.numberSubject) { 29 | console.log(`Bar chart: ${`*`.repeat(this.numberSubject.value)}`); 30 | } 31 | // ˄ 32 | } 33 | 34 | // ˅ 35 | 36 | // ˄ 37 | } 38 | 39 | // ˅ 40 | 41 | // ˄ 42 | -------------------------------------------------------------------------------- /behavioral-patterns/observer/digit-observer.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Subject } from './subject'; 5 | import { NumberSubject } from './number-subject'; 6 | import { Observer } from './observer'; 7 | 8 | // ˄ 9 | 10 | // Display values as a number. 11 | export class DigitObserver implements Observer { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private numberSubject: NumberSubject; 17 | 18 | constructor(numberSubject: NumberSubject) { 19 | // ˅ 20 | this.numberSubject = numberSubject; 21 | 22 | // ˄ 23 | } 24 | 25 | update(changedSubject: Subject): void { 26 | // ˅ 27 | // Before processing, it checks to make sure the changed subject is the subject held. 28 | if (changedSubject === this.numberSubject) { 29 | console.log(`Digit : ${this.numberSubject.value}`); 30 | } 31 | // ˄ 32 | } 33 | 34 | // ˅ 35 | 36 | // ˄ 37 | } 38 | 39 | // ˅ 40 | 41 | // ˄ 42 | -------------------------------------------------------------------------------- /behavioral-patterns/observer/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Observers observe a Subject object holding a numerical value and display the value. 3 | The display formats are digits and bar charts. 4 | */ 5 | 'use strict'; 6 | 7 | import { DigitObserver } from './digit-observer'; 8 | import { BarChartObserver } from './bar-chart-observer'; 9 | import { NumberSubject } from './number-subject'; 10 | 11 | let numberSubject: NumberSubject = new NumberSubject(); 12 | numberSubject.attach(new DigitObserver(numberSubject)); 13 | numberSubject.attach(new BarChartObserver(numberSubject)); 14 | 15 | (async () => { // Note: Define an anonymous function with async keyword for using await keyword. 16 | for (let i = 0; i < 20; i++) { 17 | numberSubject.value = Math.floor(Math.random() * 50); 18 | await new Promise(r => setTimeout(r, 200)); // Wait 200ms 19 | } 20 | })(); 21 | -------------------------------------------------------------------------------- /behavioral-patterns/observer/number-subject.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Subject } from './subject'; 5 | 6 | // ˄ 7 | 8 | // Holds a value and notifies observers when the value is set. 9 | export class NumberSubject extends Subject { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | private _value: number; 15 | 16 | constructor() { 17 | // ˅ 18 | super(); 19 | // ˄ 20 | } 21 | 22 | get value(): number { 23 | // ˅ 24 | return this._value; 25 | // ˄ 26 | } 27 | 28 | set value(value: number) { 29 | // ˅ 30 | // Notify observers when the value is set. 31 | this._value = value; 32 | this.notifyObservers(); 33 | // ˄ 34 | } 35 | 36 | // ˅ 37 | 38 | // ˄ 39 | } 40 | 41 | // ˅ 42 | 43 | // ˄ 44 | -------------------------------------------------------------------------------- /behavioral-patterns/observer/observer.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Subject } from './subject'; 5 | 6 | // ˄ 7 | 8 | // Defines an updating interface for objects that should be notified of changes in a subject. 9 | export interface Observer { 10 | 11 | update(changedSubject: Subject): void; 12 | 13 | // ˅ 14 | 15 | // ˄ 16 | } 17 | 18 | // ˅ 19 | 20 | // ˄ 21 | -------------------------------------------------------------------------------- /behavioral-patterns/observer/subject.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Observer } from './observer'; 5 | 6 | // ˄ 7 | 8 | // Provides an interface for attaching and detaching Observer objects. 9 | export class Subject { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | private observers: Array; 15 | 16 | constructor() { 17 | // ˅ 18 | this.observers = new Array(); 19 | // ˄ 20 | } 21 | 22 | attach(observer: Observer): void { 23 | // ˅ 24 | this.observers.push(observer); 25 | // ˄ 26 | } 27 | 28 | detach(observer: Observer): void { 29 | // ˅ 30 | for (let i = 0; i < this.observers.length; i++) { 31 | if(this.observers[i] === observer){ 32 | this.observers.splice(i, 1); 33 | } 34 | } 35 | // ˄ 36 | } 37 | 38 | notifyObservers(): void { 39 | // ˅ 40 | for (let observer of this.observers) { 41 | observer.update(this); 42 | } 43 | // ˄ 44 | } 45 | 46 | // ˅ 47 | 48 | // ˄ 49 | } 50 | 51 | // ˅ 52 | 53 | // ˄ 54 | -------------------------------------------------------------------------------- /behavioral-patterns/observer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/state/app-safe.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Context } from './context'; 5 | import { State } from './state'; 6 | import { DaytimeState } from './daytime-state'; 7 | 8 | // ˄ 9 | 10 | // Safe security system that the security status changes with time. 11 | export class AppSafe implements Context { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private hour: number; 17 | 18 | private readonly timerId: number; 19 | 20 | // Current state 21 | private state: State; 22 | 23 | private readonly textClock: HTMLTextAreaElement; 24 | 25 | private readonly textMessage: HTMLTextAreaElement; 26 | 27 | private readonly useButton: HTMLButtonElement; 28 | 29 | private readonly alarmButton: HTMLButtonElement; 30 | 31 | private readonly phoneButton: HTMLButtonElement; 32 | 33 | constructor() { 34 | // ˅ 35 | this.state = DaytimeState.getInstance(); 36 | this.hour = 0; 37 | this.textClock = document.getElementById(`textTime`); 38 | this.textMessage = document.getElementById(`textMessage`); 39 | this.useButton = document.getElementById(`buttonUse`); 40 | this.alarmButton = document.getElementById(`buttonAlarm`); 41 | this.phoneButton = document.getElementById(`buttonPhone`); 42 | 43 | this.useButton.addEventListener(`click`, () => this.pressedUseButton()); 44 | this.alarmButton.addEventListener(`click`, () => this.pressedAlarmButton()); 45 | this.phoneButton.addEventListener(`click`, () => this.pressedPhoneButton()); 46 | 47 | this.timerId = window.setInterval(this.setTime.bind(this), 1000); // Set interval timer 48 | // ˄ 49 | } 50 | 51 | // Set time 52 | setTime(): void { 53 | // ˅ 54 | let clockTime: string; 55 | if (this.hour < 10) { 56 | clockTime = `0${this.hour}:00`; 57 | } 58 | else { 59 | clockTime = `${this.hour}:00`; 60 | } 61 | 62 | console.log(clockTime); 63 | this.textClock.value = clockTime; 64 | this.state.setTime(this, this.hour); 65 | 66 | this.hour++; 67 | if (this.hour >= 24) { 68 | this.hour = 0; 69 | } 70 | // ˄ 71 | } 72 | 73 | // Change state 74 | changeState(state: State): void { 75 | // ˅ 76 | console.log(`The state changed from ${this.state.toString()} to ${state.toString()}.`); 77 | this.state = state; 78 | // ˄ 79 | } 80 | 81 | // Call a security guard room 82 | callSecurityGuardsRoom(message: string): void { 83 | // ˅ 84 | this.textMessage.value += `call! ${message}\n`; 85 | this.textMessage.scrollTop = this.textMessage.scrollHeight; // Scroll to the bottom 86 | // ˄ 87 | } 88 | 89 | // Record security log 90 | recordSecurityLog(message: string): void { 91 | // ˅ 92 | this.textMessage.value += `record ... ${message}\n`; 93 | this.textMessage.scrollTop = this.textMessage.scrollHeight; // Scroll to the bottom 94 | // ˄ 95 | } 96 | 97 | private pressedUseButton(): void { 98 | // ˅ 99 | this.state.use(this); 100 | // ˄ 101 | } 102 | 103 | private pressedAlarmButton(): void { 104 | // ˅ 105 | this.state.alarm(this); 106 | // ˄ 107 | } 108 | 109 | private pressedPhoneButton(): void { 110 | // ˅ 111 | this.state.phone(this); 112 | // ˄ 113 | } 114 | 115 | // ˅ 116 | 117 | // ˄ 118 | } 119 | 120 | // ˅ 121 | 122 | // ˄ 123 | -------------------------------------------------------------------------------- /behavioral-patterns/state/context.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { State } from './state'; 5 | 6 | // ˄ 7 | 8 | export interface Context { 9 | 10 | // Set time 11 | setTime(): void; 12 | 13 | // Change state 14 | changeState(state: State): void; 15 | 16 | // Call a security guard room 17 | callSecurityGuardsRoom(message: string): void; 18 | 19 | // Record security log 20 | recordSecurityLog(message: string): void; 21 | 22 | // ˅ 23 | 24 | // ˄ 25 | } 26 | 27 | // ˅ 28 | 29 | // ˄ 30 | -------------------------------------------------------------------------------- /behavioral-patterns/state/daytime-state.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { State } from './state'; 5 | import { Context } from './context'; 6 | import { NightState } from './night-state'; 7 | 8 | // ˄ 9 | 10 | export class DaytimeState implements State { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | private static instance: DaytimeState = new DaytimeState(); 16 | 17 | static getInstance(): DaytimeState { 18 | // ˅ 19 | return this.instance; 20 | // ˄ 21 | } 22 | 23 | private constructor() { 24 | // ˅ 25 | 26 | // ˄ 27 | } 28 | 29 | setTime(context: Context, hour: number): void { 30 | // ˅ 31 | if (hour < 9 || 17 <= hour) { 32 | context.changeState(NightState.getInstance()); 33 | } 34 | // ˄ 35 | } 36 | 37 | use(context: Context): void { 38 | // ˅ 39 | context.recordSecurityLog(`Use a safe in the daytime`); 40 | // ˄ 41 | } 42 | 43 | alarm(context: Context): void { 44 | // ˅ 45 | context.callSecurityGuardsRoom(`Sound an emergency bell in the daytime`); 46 | // ˄ 47 | } 48 | 49 | phone(context: Context): void { 50 | // ˅ 51 | context.callSecurityGuardsRoom(`Make a normal call in the daytime`); 52 | // ˄ 53 | } 54 | 55 | toString(): string { 56 | // ˅ 57 | return `[Daytime]`; 58 | // ˄ 59 | } 60 | 61 | // ˅ 62 | 63 | // ˄ 64 | } 65 | 66 | // ˅ 67 | 68 | // ˄ 69 | -------------------------------------------------------------------------------- /behavioral-patterns/state/index.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | State Example 9 | 10 | 11 | 12 | Current Time:
13 |
14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /behavioral-patterns/state/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Safe security system that the security status changes with time. When you press a button in a dialog, 3 | the message displayed will change depending on whether the time is day or night. 4 | The internal time of the dialog advances one hour for every second of real time. 5 | */ 6 | 'use strict'; 7 | 8 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 9 | // Before launching this State project, click to "Go Live" from the status bar of VSCode to turn a server on. // 10 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 11 | 12 | import { AppSafe } from './app-safe'; 13 | new AppSafe(); 14 | -------------------------------------------------------------------------------- /behavioral-patterns/state/night-state.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { State } from './state'; 5 | import { Context } from './context'; 6 | import { DaytimeState } from './daytime-state'; 7 | 8 | // ˄ 9 | 10 | export class NightState implements State { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | private static instance: NightState = new NightState(); 16 | 17 | static getInstance(): NightState { 18 | // ˅ 19 | return this.instance; 20 | // ˄ 21 | } 22 | 23 | private constructor() { 24 | // ˅ 25 | 26 | // ˄ 27 | } 28 | 29 | setTime(context: Context, hour: number): void { 30 | // ˅ 31 | if (9 <= hour && hour < 17) { 32 | context.changeState(DaytimeState.getInstance()); 33 | } 34 | // ˄ 35 | } 36 | 37 | use(context: Context): void { 38 | // ˅ 39 | context.callSecurityGuardsRoom(`Emergency: Use a safe at night!`); 40 | // ˄ 41 | } 42 | 43 | alarm(context: Context): void { 44 | // ˅ 45 | context.callSecurityGuardsRoom(`Sound an emergency bell at night`); 46 | // ˄ 47 | } 48 | 49 | phone(context: Context): void { 50 | // ˅ 51 | context.recordSecurityLog(`Record a night call`); 52 | // ˄ 53 | } 54 | 55 | toString(): string { 56 | // ˅ 57 | return `[Night]`; 58 | // ˄ 59 | } 60 | 61 | // ˅ 62 | 63 | // ˄ 64 | } 65 | 66 | // ˅ 67 | 68 | // ˄ 69 | -------------------------------------------------------------------------------- /behavioral-patterns/state/state.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Context } from './context'; 5 | 6 | // ˄ 7 | 8 | export interface State { 9 | 10 | setTime(context: Context, hour: number): void; 11 | 12 | use(context: Context): void; 13 | 14 | alarm(context: Context): void; 15 | 16 | phone(context: Context): void; 17 | 18 | toString(): string; 19 | 20 | // ˅ 21 | 22 | // ˄ 23 | } 24 | 25 | // ˅ 26 | 27 | // ˄ 28 | -------------------------------------------------------------------------------- /behavioral-patterns/state/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "umd", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/strategy/game-result-type.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export enum GameResultType { 7 | 8 | Win, 9 | 10 | Loss, 11 | 12 | Draw 13 | 14 | // ˅ 15 | 16 | // ˄ 17 | } 18 | 19 | // ˅ 20 | 21 | // ˄ 22 | -------------------------------------------------------------------------------- /behavioral-patterns/strategy/hand-signal.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export class HandSignal { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | // Rock 12 | static readonly ROCK: number = 0; 13 | 14 | // Scissors 15 | static readonly SCISSORS: number = 1; 16 | 17 | // Paper 18 | static readonly PAPER: number = 2; 19 | 20 | // Hands of rock-scissors-paper 21 | private static readonly handSignals: Array = [new HandSignal(HandSignal.ROCK), new HandSignal(HandSignal.SCISSORS), new HandSignal(HandSignal.PAPER)]; 22 | 23 | // Characters of the hands 24 | private static readonly handName: Array = [`Rock`, `Scissors`, `Paper`]; 25 | 26 | // Values of rock, scissors and paper. 27 | private readonly value: number; 28 | 29 | // Get an instance of the hand 30 | static getHand(handValue: number): HandSignal { 31 | // ˅ 32 | return this.handSignals[handValue]; 33 | // ˄ 34 | } 35 | 36 | constructor(value: number) { 37 | // ˅ 38 | this.value = value; 39 | // ˄ 40 | } 41 | 42 | // Return true if "this" is stronger than "hand". 43 | isStrongerThan(hand: HandSignal): boolean { 44 | // ˅ 45 | return this.judgeGame(hand) === 1; 46 | // ˄ 47 | } 48 | 49 | // Return false if "this" is weaker than "hand". 50 | isWeakerThan(hand: HandSignal): boolean { 51 | // ˅ 52 | return this.judgeGame(hand) === -1; 53 | // ˄ 54 | } 55 | 56 | toString(): string { 57 | // ˅ 58 | return HandSignal.handName[this.value]; 59 | // ˄ 60 | } 61 | 62 | // The draw is 0. "this" win is 1. "hand" win is -1. 63 | private judgeGame(hand: HandSignal): number { 64 | // ˅ 65 | if (this === hand) { 66 | return 0; 67 | } 68 | else if ((this.value + 1) % 3 === hand.value) { 69 | return 1; 70 | } 71 | else { 72 | return -1; 73 | } 74 | // ˄ 75 | } 76 | 77 | // ˅ 78 | 79 | // ˄ 80 | } 81 | 82 | // ˅ 83 | 84 | // ˄ 85 | -------------------------------------------------------------------------------- /behavioral-patterns/strategy/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | A game of rock-scissors-paper. Two strategies are available: 3 | * Random Strategy: showing a random hand signal. 4 | * Mirror Strategy: showing a hand signal from the previous opponent's hand signal. 5 | */ 6 | 'use strict'; 7 | 8 | import { Player } from './player'; 9 | import { RandomStrategy } from './random-strategy'; 10 | import { MirrorStrategy } from './mirror-strategy'; 11 | import { GameResultType } from './game-result-type'; 12 | 13 | const player1 = new Player(`Emily`, new RandomStrategy()); 14 | const player2 = new Player(`James`, new MirrorStrategy()); 15 | 16 | for (let i = 0; i < 100; i++) { 17 | const handOfPlayer1 = player1.showHandSignal(); 18 | const handOfPlayer2 = player2.showHandSignal(); 19 | 20 | // Judge win, loss, or draw 21 | let resultOfPlayer1: GameResultType; 22 | let resultOfPlayer2: GameResultType; 23 | if (handOfPlayer1.isStrongerThan(handOfPlayer2)) { 24 | console.log(`Winner: ${player1.toString()}`); 25 | resultOfPlayer1 = GameResultType.Win; 26 | resultOfPlayer2 = GameResultType.Loss; 27 | } 28 | else if (handOfPlayer2.isStrongerThan(handOfPlayer1)) { 29 | console.log(`Winner: ${player2.toString()}`); 30 | resultOfPlayer1 = GameResultType.Loss; 31 | resultOfPlayer2 = GameResultType.Win; 32 | } 33 | else { 34 | console.log(`Draw...`); 35 | resultOfPlayer1 = GameResultType.Draw; 36 | resultOfPlayer2 = GameResultType.Draw; 37 | } 38 | 39 | player1.notifyGameResult(resultOfPlayer1, handOfPlayer1, handOfPlayer2); 40 | player2.notifyGameResult(resultOfPlayer2, handOfPlayer2, handOfPlayer1); 41 | } 42 | console.log(`RESULT:`); 43 | console.log(player1.toString()); 44 | console.log(player2.toString()); 45 | -------------------------------------------------------------------------------- /behavioral-patterns/strategy/mirror-strategy.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { HandSignal } from './hand-signal'; 5 | import { GameResultType } from './game-result-type'; 6 | import { Strategy } from './strategy'; 7 | 8 | // ˄ 9 | 10 | // Mirror Strategy: showing a hand signal from the previous opponent's hand signal. 11 | export class MirrorStrategy implements Strategy { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private preOpponentsHand: HandSignal; 17 | 18 | constructor() { 19 | // ˅ 20 | this.preOpponentsHand = HandSignal.getHand(HandSignal.ROCK); 21 | // ˄ 22 | } 23 | 24 | showHandSignal(): HandSignal { 25 | // ˅ 26 | return this.preOpponentsHand; 27 | // ˄ 28 | } 29 | 30 | notifyGameResult(result: GameResultType, ownHand: HandSignal, opponentsHand: HandSignal): void { 31 | // ˅ 32 | this.preOpponentsHand = opponentsHand; 33 | // ˄ 34 | } 35 | 36 | // ˅ 37 | 38 | // ˄ 39 | } 40 | 41 | // ˅ 42 | 43 | // ˄ 44 | -------------------------------------------------------------------------------- /behavioral-patterns/strategy/player.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Strategy } from './strategy'; 5 | import { HandSignal } from './hand-signal'; 6 | import { GameResultType } from './game-result-type'; 7 | 8 | // ˄ 9 | 10 | export class Player { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | private readonly name: string; 16 | 17 | private winCount: number; 18 | 19 | private lossCount: number; 20 | 21 | private gameCount: number; 22 | 23 | private readonly strategy: Strategy; 24 | 25 | constructor(name: string, strategy: Strategy) { 26 | // ˅ 27 | this.name = name; 28 | this.winCount = 0; 29 | this.lossCount = 0; 30 | this.gameCount = 0; 31 | this.strategy = strategy; 32 | // ˄ 33 | } 34 | 35 | // Show a hand signal from the strategy. 36 | showHandSignal(): HandSignal { 37 | // ˅ 38 | return this.strategy.showHandSignal(); 39 | // ˄ 40 | } 41 | 42 | // Notify a game result. 43 | notifyGameResult(result: GameResultType, ownHand: HandSignal, opponentsHand: HandSignal): void { 44 | // ˅ 45 | switch (result) { 46 | case GameResultType.Win: 47 | this.winCount++; 48 | this.gameCount++; 49 | break; 50 | case GameResultType.Loss: 51 | this.lossCount++; 52 | this.gameCount++; 53 | break; 54 | case GameResultType.Draw: 55 | this.gameCount++; 56 | break; 57 | } 58 | 59 | this.strategy.notifyGameResult(result, ownHand, opponentsHand); 60 | // ˄ 61 | } 62 | 63 | toString(): string { 64 | // ˅ 65 | return `${this.name} [${this.gameCount} games, ${this.winCount} won, ${this.lossCount} lost, ${this.gameCount - this.winCount - this.lossCount} drew]`; 66 | // ˄ 67 | } 68 | 69 | // ˅ 70 | 71 | // ˄ 72 | } 73 | 74 | // ˅ 75 | 76 | // ˄ 77 | -------------------------------------------------------------------------------- /behavioral-patterns/strategy/random-strategy.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { HandSignal } from './hand-signal'; 5 | import { GameResultType } from './game-result-type'; 6 | import { Strategy } from './strategy'; 7 | 8 | // ˄ 9 | 10 | // Random Strategy: showing a random hand signal. 11 | export class RandomStrategy implements Strategy { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | constructor() { 17 | // ˅ 18 | 19 | // ˄ 20 | } 21 | 22 | showHandSignal(): HandSignal { 23 | // ˅ 24 | return HandSignal.getHand(Math.floor(Math.random() * 3)); 25 | // ˄ 26 | } 27 | 28 | notifyGameResult(result: GameResultType, ownHand: HandSignal, opponentsHand: HandSignal): void { 29 | // ˅ 30 | // Do nothing 31 | // ˄ 32 | } 33 | 34 | // ˅ 35 | 36 | // ˄ 37 | } 38 | 39 | // ˅ 40 | 41 | // ˄ 42 | -------------------------------------------------------------------------------- /behavioral-patterns/strategy/strategy.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { HandSignal } from './hand-signal'; 5 | import { GameResultType } from './game-result-type'; 6 | 7 | // ˄ 8 | 9 | export interface Strategy { 10 | 11 | // Show a hand signal. 12 | showHandSignal(): HandSignal; 13 | 14 | // Notify a game result. 15 | notifyGameResult(result: GameResultType, ownHand: HandSignal, opponentsHand: HandSignal): void; 16 | 17 | // ˅ 18 | 19 | // ˄ 20 | } 21 | 22 | // ˅ 23 | 24 | // ˄ 25 | -------------------------------------------------------------------------------- /behavioral-patterns/strategy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/template-method/abstract-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export abstract class AbstractDisplay { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | abstract open(): void; 12 | 13 | abstract write(): void; 14 | 15 | abstract close(): void; 16 | 17 | output(): void { 18 | // ˅ 19 | this.open(); 20 | for (let i = 0; i < 5; i++) { // Repeat write 5 times 21 | this.write(); 22 | } 23 | this.close(); 24 | // ˄ 25 | } 26 | 27 | // ˅ 28 | 29 | // ˄ 30 | } 31 | 32 | // ˅ 33 | 34 | // ˄ 35 | -------------------------------------------------------------------------------- /behavioral-patterns/template-method/char-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { AbstractDisplay } from './abstract-display'; 5 | 6 | // ˄ 7 | 8 | export class CharDisplay extends AbstractDisplay { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly charValue: string; 14 | 15 | constructor(charValue: string) { 16 | // ˅ 17 | super(); 18 | this.charValue = charValue; 19 | // ˄ 20 | } 21 | 22 | open(): void { 23 | // ˅ 24 | process.stdout.write(`<<`); // Display '<<' in the start characters. 25 | // ˄ 26 | } 27 | 28 | write(): void { 29 | // ˅ 30 | process.stdout.write(this.charValue); // Display the character. 31 | // ˄ 32 | } 33 | 34 | close(): void { 35 | // ˅ 36 | console.log(`>>`); // Display '>>' in the end characters. 37 | // ˄ 38 | } 39 | 40 | // ˅ 41 | 42 | // ˄ 43 | } 44 | 45 | // ˅ 46 | 47 | // ˄ 48 | -------------------------------------------------------------------------------- /behavioral-patterns/template-method/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Display a character or string repeatedly 5 times. 3 | */ 4 | 'use strict'; 5 | 6 | import { AbstractDisplay } from './abstract-display'; 7 | import { CharDisplay } from './char-display'; 8 | import { StringDisplay } from './string-display'; 9 | 10 | const display1: AbstractDisplay = new CharDisplay(`H`); 11 | display1.output(); 12 | 13 | const display2: AbstractDisplay = new StringDisplay(`Hello world.`); 14 | display2.output(); 15 | -------------------------------------------------------------------------------- /behavioral-patterns/template-method/string-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { AbstractDisplay } from './abstract-display'; 5 | 6 | // ˄ 7 | 8 | export class StringDisplay extends AbstractDisplay { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly stringValue: string; 14 | 15 | // String width 16 | private readonly width: number; 17 | 18 | constructor(stringValue: string) { 19 | // ˅ 20 | super(); 21 | this.stringValue = stringValue; 22 | this.width = stringValue.length; 23 | // ˄ 24 | } 25 | 26 | open(): void { 27 | // ˅ 28 | this.writeLine(); // Write a line 29 | // ˄ 30 | } 31 | 32 | write(): void { 33 | // ˅ 34 | console.log(`|${this.stringValue}|`); // Display the character with '|' 35 | // ˄ 36 | } 37 | 38 | close(): void { 39 | // ˅ 40 | this.writeLine(); // Write a line 41 | // ˄ 42 | } 43 | 44 | private writeLine(): void { 45 | // ˅ 46 | process.stdout.write(`+`); // Display an end mark '+' 47 | process.stdout.write(`-`.repeat(this.width)); // Display a line '-' 48 | console.log(`+`); // Display an end mark '+' 49 | // ˄ 50 | } 51 | 52 | // ˅ 53 | 54 | // ˄ 55 | } 56 | 57 | // ˅ 58 | 59 | // ˄ 60 | -------------------------------------------------------------------------------- /behavioral-patterns/template-method/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/visitor/directory-element.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { FileSystemElement } from './file-system-element'; 5 | import { Visitor } from './visitor'; 6 | 7 | // ˄ 8 | 9 | export class DirectoryElement extends FileSystemElement { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | private readonly _name: string; 15 | 16 | // Collection of elements 17 | private readonly elements: Array; 18 | 19 | constructor(name: string) { 20 | // ˅ 21 | super(); 22 | this._name = name; 23 | this.elements = new Array(); 24 | // ˄ 25 | } 26 | 27 | // Accept a visitor 28 | accept(visitor: Visitor): void { 29 | // ˅ 30 | visitor.visitDirectory(this); 31 | // ˄ 32 | } 33 | 34 | // Directory element name 35 | get name(): string { 36 | // ˅ 37 | return this._name; 38 | // ˄ 39 | } 40 | 41 | get size(): number { 42 | // ˅ 43 | let size: number = 0; 44 | for (let element of this.elements) { 45 | size += element.size; 46 | } 47 | return size; 48 | // ˄ 49 | } 50 | 51 | // Add an element 52 | add(element: FileSystemElement): void { 53 | // ˅ 54 | this.elements.push(element); 55 | // ˄ 56 | } 57 | 58 | // Get the iterator 59 | iterator(): IterableIterator { 60 | // ˅ 61 | return this.elements[Symbol.iterator](); 62 | // ˄ 63 | } 64 | 65 | // ˅ 66 | 67 | // ˄ 68 | } 69 | 70 | // ˅ 71 | 72 | // ˄ 73 | -------------------------------------------------------------------------------- /behavioral-patterns/visitor/element.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Visitor } from './visitor'; 5 | 6 | // ˄ 7 | 8 | export interface Element { 9 | 10 | accept(visitor: Visitor): void; 11 | 12 | // ˅ 13 | 14 | // ˄ 15 | } 16 | 17 | // ˅ 18 | 19 | // ˄ 20 | -------------------------------------------------------------------------------- /behavioral-patterns/visitor/file-element.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { FileSystemElement } from './file-system-element'; 5 | import { Visitor } from './visitor'; 6 | 7 | // ˄ 8 | 9 | export class FileElement extends FileSystemElement { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | private readonly _name: string; 15 | 16 | private readonly _size: number; 17 | 18 | constructor(name: string, size: number) { 19 | // ˅ 20 | super(); 21 | this._name = name; 22 | this._size = size; 23 | // ˄ 24 | } 25 | 26 | accept(visitor: Visitor): void { 27 | // ˅ 28 | visitor.visitFile(this); 29 | // ˄ 30 | } 31 | 32 | // File element name 33 | get name(): string { 34 | // ˅ 35 | return this._name 36 | // ˄ 37 | } 38 | 39 | get size(): number { 40 | // ˅ 41 | return this._size; 42 | // ˄ 43 | } 44 | 45 | // ˅ 46 | 47 | // ˄ 48 | } 49 | 50 | // ˅ 51 | 52 | // ˄ 53 | -------------------------------------------------------------------------------- /behavioral-patterns/visitor/file-system-element.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Element } from './element'; 5 | import { Visitor } from './visitor'; 6 | 7 | // ˄ 8 | 9 | export abstract class FileSystemElement implements Element { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | abstract accept(visitor: Visitor): void; 15 | 16 | abstract get name(): string; 17 | 18 | abstract get size(): number; 19 | 20 | toString(): string { 21 | // ˅ 22 | return `${this.name} (${this.size})`; 23 | // ˄ 24 | } 25 | 26 | // ˅ 27 | 28 | // ˄ 29 | } 30 | 31 | // ˅ 32 | 33 | // ˄ 34 | -------------------------------------------------------------------------------- /behavioral-patterns/visitor/list-visitor.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Visitor } from './visitor'; 5 | import { FileElement } from './file-element'; 6 | import { DirectoryElement } from './directory-element'; 7 | import { FileSystemElement } from './file-system-element'; 8 | 9 | // ˄ 10 | 11 | export class ListVisitor implements Visitor { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | // Currently visited directory 17 | private currentDirectory: string; 18 | 19 | constructor() { 20 | // ˅ 21 | this.currentDirectory = ``; 22 | // ˄ 23 | } 24 | 25 | // Visit a file 26 | visitFile(file: FileElement): void { 27 | // ˅ 28 | console.log(`${this.currentDirectory}/${file.toString()}`); 29 | // ˄ 30 | } 31 | 32 | // Visit a directory 33 | visitDirectory(directory: DirectoryElement): void { 34 | // ˅ 35 | console.log(`${this.currentDirectory}/${directory.toString()}`); 36 | const visitedDirectory = this.currentDirectory; 37 | this.currentDirectory = `${this.currentDirectory}/${directory.name}`; 38 | 39 | const iterator = directory.iterator(); 40 | let result = iterator.next(); 41 | while(!result.done) { 42 | ((result.value)).accept(this); 43 | result = iterator.next(); 44 | } 45 | 46 | this.currentDirectory = visitedDirectory; 47 | // ˄ 48 | } 49 | 50 | // ˅ 51 | 52 | // ˄ 53 | } 54 | 55 | // ˅ 56 | 57 | // ˄ 58 | -------------------------------------------------------------------------------- /behavioral-patterns/visitor/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Visitor visits the file system composed of files and directories, and displays a list of files/directories. 3 | */ 4 | 'use strict'; 5 | 6 | import { DirectoryElement } from './directory-element'; 7 | import { FileElement } from './file-element'; 8 | import { ListVisitor } from './list-visitor'; 9 | 10 | console.log(`Create a file system...`); 11 | const binDir: DirectoryElement = new DirectoryElement(`bin`); 12 | const lsFile: FileElement = new FileElement(`ls`, 20); 13 | binDir.add(lsFile); 14 | const mkdirFile = new FileElement(`mkdir`, 40); 15 | binDir.add(mkdirFile); 16 | 17 | const emilyDir: DirectoryElement = new DirectoryElement(`emily`); 18 | const homeworkFile = new FileElement(`homework.doc`, 60); 19 | emilyDir.add(homeworkFile); 20 | 21 | const jamesDir: DirectoryElement = new DirectoryElement(`james`); 22 | const appFile = new FileElement(`app.exe`, 80); 23 | jamesDir.add(appFile); 24 | 25 | const homeDir: DirectoryElement = new DirectoryElement(`home`); 26 | homeDir.add(emilyDir); 27 | homeDir.add(jamesDir); 28 | 29 | const rootDir: DirectoryElement = new DirectoryElement(`root`); 30 | rootDir.add(homeDir); 31 | rootDir.add(binDir); 32 | 33 | rootDir.accept(new ListVisitor()); 34 | -------------------------------------------------------------------------------- /behavioral-patterns/visitor/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /behavioral-patterns/visitor/visitor.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { FileElement } from './file-element'; 5 | import { DirectoryElement } from './directory-element'; 6 | 7 | // ˄ 8 | 9 | export interface Visitor { 10 | 11 | visitFile(file: FileElement): void; 12 | 13 | visitDirectory(directory: DirectoryElement): void; 14 | 15 | // ˅ 16 | 17 | // ˄ 18 | } 19 | 20 | // ˅ 21 | 22 | // ˄ 23 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/factory/data.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Item } from './item'; 5 | 6 | // ˄ 7 | 8 | export abstract class Data extends Item { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | protected items: Array; 14 | 15 | constructor(name: string) { 16 | // ˅ 17 | super(name); 18 | this.items = new Array(); 19 | // ˄ 20 | } 21 | 22 | add(item: Item): void { 23 | // ˅ 24 | this.items.push(item); 25 | // ˄ 26 | } 27 | 28 | // ˅ 29 | 30 | // ˄ 31 | } 32 | 33 | // ˅ 34 | 35 | // ˄ 36 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/factory/factory.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Data } from './data'; 5 | import { Link } from './link'; 6 | import { Page } from './page'; 7 | 8 | // ˄ 9 | 10 | export interface Factory { 11 | 12 | createPage(title: string, author: string): Page; 13 | 14 | createLink(name: string, url: string): Link; 15 | 16 | createData(name: string): Data; 17 | 18 | // ˅ 19 | 20 | // ˄ 21 | } 22 | 23 | // ˅ 24 | 25 | // ˄ 26 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/factory/item.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export abstract class Item { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | protected readonly name: string; 12 | 13 | constructor(name: string) { 14 | // ˅ 15 | this.name = name; 16 | // ˄ 17 | } 18 | 19 | abstract toHTML(): string; 20 | 21 | // ˅ 22 | 23 | // ˄ 24 | } 25 | 26 | // ˅ 27 | 28 | // ˄ 29 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/factory/link.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Item } from './item'; 5 | 6 | // ˄ 7 | 8 | export abstract class Link extends Item { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | protected readonly url: string; 14 | 15 | constructor(name: string, url: string) { 16 | // ˅ 17 | super(name); 18 | this.url = url; 19 | // ˄ 20 | } 21 | 22 | // ˅ 23 | 24 | // ˄ 25 | } 26 | 27 | // ˅ 28 | 29 | // ˄ 30 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/factory/page.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import * as fs from 'fs'; 5 | import * as path from 'path'; 6 | import { Item } from './item'; 7 | 8 | // ˄ 9 | 10 | export abstract class Page { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | protected readonly title: string; 16 | 17 | protected readonly author: string; 18 | 19 | protected contents: Array; 20 | 21 | constructor(title: string, author: string) { 22 | // ˅ 23 | this.title = title; 24 | this.author = author; 25 | this.contents = new Array(); 26 | // ˄ 27 | } 28 | 29 | abstract toHTML(): string; 30 | 31 | add(item: Item): void { 32 | // ˅ 33 | this.contents.push(item); 34 | // ˄ 35 | } 36 | 37 | output(): void { 38 | // ˅ 39 | const fileName = `${this.title}.html`; 40 | try { 41 | fs.writeFileSync(fileName, this.toHTML()); 42 | } catch(e) { 43 | console.error(e); 44 | } 45 | console.log(`${fileName} has been created.`); 46 | console.log(`Output File: ${path.join(process.cwd(), fileName)}`); 47 | // ˄ 48 | } 49 | 50 | // ˅ 51 | 52 | // ˄ 53 | } 54 | 55 | // ˅ 56 | 57 | // ˄ 58 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/list-factory/list-data.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Data } from '../factory/data'; 5 | 6 | // ˄ 7 | 8 | export class ListData extends Data { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | constructor(name: string) { 14 | // ˅ 15 | super(name); 16 | // ˄ 17 | } 18 | 19 | toHTML(): string { 20 | // ˅ 21 | const htmlData: Array = new Array(); 22 | htmlData.push(`
  • ${this.name}
      `); 23 | for (let item of this.items) { 24 | htmlData.push(item.toHTML()); 25 | } 26 | htmlData.push(`
  • \n`); 27 | return htmlData.join(``); 28 | // ˄ 29 | } 30 | 31 | // ˅ 32 | 33 | // ˄ 34 | } 35 | 36 | // ˅ 37 | 38 | // ˄ 39 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/list-factory/list-factory.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Page } from '../factory/page'; 5 | import { Link } from '../factory/link'; 6 | import { Data } from '../factory/data'; 7 | import { Factory } from '../factory/factory'; 8 | import { ListData } from './list-data'; 9 | import { ListLink } from './list-link'; 10 | import { ListPage } from './list-page'; 11 | 12 | // ˄ 13 | 14 | export class ListFactory implements Factory { 15 | // ˅ 16 | 17 | // ˄ 18 | 19 | createPage(title: string, author: string): Page { 20 | // ˅ 21 | return new ListPage(title, author); 22 | // ˄ 23 | } 24 | 25 | createLink(name: string, url: string): Link { 26 | // ˅ 27 | return new ListLink(name, url); 28 | // ˄ 29 | } 30 | 31 | createData(name: string): Data { 32 | // ˅ 33 | return new ListData(name); 34 | // ˄ 35 | } 36 | 37 | // ˅ 38 | 39 | // ˄ 40 | } 41 | 42 | // ˅ 43 | 44 | // ˄ 45 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/list-factory/list-link.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Link } from '../factory/link'; 5 | 6 | // ˄ 7 | 8 | export class ListLink extends Link { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | constructor(name: string, url: string) { 14 | // ˅ 15 | super(name, url); 16 | // ˄ 17 | } 18 | 19 | toHTML(): string { 20 | // ˅ 21 | return `
  • ${this.name}
  • \n`; 22 | // ˄ 23 | } 24 | 25 | // ˅ 26 | 27 | // ˄ 28 | } 29 | 30 | // ˅ 31 | 32 | // ˄ 33 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/list-factory/list-page.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Page } from '../factory/page'; 5 | 6 | // ˄ 7 | 8 | export class ListPage extends Page { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | constructor(title: string, author: string) { 14 | // ˅ 15 | super(title, author); 16 | // ˄ 17 | } 18 | 19 | toHTML(): string { 20 | // ˅ 21 | const htmlData: Array = new Array(); 22 | htmlData.push(`${this.title}`); 23 | htmlData.push(`

    ${this.title}

    `); 24 | htmlData.push(`
      `); 25 | for (let content of this.contents) { 26 | htmlData.push(content.toHTML()); 27 | } 28 | htmlData.push(`
    `); 29 | htmlData.push(`
    ${this.author}
    `); 30 | htmlData.push(`\n`); 31 | return htmlData.join(``); 32 | // ˄ 33 | } 34 | 35 | // ˅ 36 | 37 | // ˄ 38 | } 39 | 40 | // ˅ 41 | 42 | // ˄ 43 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Create a hierarchical link collection as an HTML file. It can be created in either tabular or list format. 3 | */ 4 | 'use strict'; 5 | 6 | import { Factory } from './factory/factory'; 7 | import { Data } from './factory/data'; 8 | import { Link } from './factory/link'; 9 | import { Page } from './factory/page'; 10 | import { ListFactory } from './list-factory/list-factory'; 11 | import { TableFactory } from './table-factory/table-factory'; 12 | import * as rl from 'readline'; 13 | 14 | console.log(`Please enter a number (1 or 2):`); 15 | console.log(` 1: Create objects by using ListFactory`); 16 | console.log(` 2: Create objects by using TableFactory`); 17 | 18 | const readline = rl.createInterface({ 19 | input: process.stdin, 20 | output: process.stdout 21 | }); 22 | 23 | readline.question(``, (data: any) => { 24 | const input: number = Number(data); 25 | readline.close(); 26 | if (isNaN(input)) { 27 | console.error(`Unexpected value.`); 28 | process.exit(1); 29 | } 30 | 31 | let factory: Factory; 32 | switch (input) { 33 | case 1: 34 | factory = new ListFactory(); 35 | break; 36 | case 2: 37 | factory = new TableFactory(); 38 | break; 39 | default: 40 | console.error(`The value is not 1 or 2.`); 41 | process.exit(1); 42 | } 43 | 44 | const washingtonPost: Link = factory.createLink(`The Washington Post`, `https://www.washingtonpost.com/`); 45 | const newYorkTimes: Link = factory.createLink(`The NewYork Times`, `https://www.nytimes.com/`); 46 | const financialTimes: Link = factory.createLink(`The Financial Times`, `https://www.ft.com/`); 47 | 48 | const newspaper: Data = factory.createData(`Newspaper`); 49 | newspaper.add(washingtonPost); 50 | newspaper.add(newYorkTimes); 51 | newspaper.add(financialTimes); 52 | 53 | const yahoo: Link = factory.createLink(`Yahoo!`, `https://www.yahoo.com/`); 54 | const google: Link = factory.createLink(`Google`, `https://www.google.com/`); 55 | 56 | const searchEngine: Data = factory.createData(`Search engine`); 57 | searchEngine.add(yahoo); 58 | searchEngine.add(google); 59 | 60 | const linkPage: Page = factory.createPage(`LinkPage`, `James Smith`); 61 | linkPage.add(newspaper); 62 | linkPage.add(searchEngine); 63 | 64 | linkPage.output(); 65 | }); 66 | 67 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/table-factory/table-data.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Data } from '../factory/data'; 5 | 6 | // ˄ 7 | 8 | export class TableData extends Data { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | constructor(name: string) { 14 | // ˅ 15 | super(name); 16 | // ˄ 17 | } 18 | 19 | toHTML(): string { 20 | // ˅ 21 | const htmlData: Array = new Array(); 22 | htmlData.push(`\n`); 23 | htmlData.push(`\n`); 24 | htmlData.push(`\n`); 25 | for (let item of this.items) { 26 | htmlData.push(item.toHTML()); 27 | } 28 | htmlData.push(`\n`); 29 | htmlData.push(`
    ${this.name}
    \n`); 30 | return htmlData.join(``); 31 | // ˄ 32 | } 33 | 34 | // ˅ 35 | 36 | // ˄ 37 | } 38 | 39 | // ˅ 40 | 41 | // ˄ 42 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/table-factory/table-factory.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Page } from '../factory/page'; 5 | import { Link } from '../factory/link'; 6 | import { Data } from '../factory/data'; 7 | import { Factory } from '../factory/factory'; 8 | import { TableData } from './table-data'; 9 | import { TableLink } from './table-link'; 10 | import { TablePage } from './table-page'; 11 | 12 | // ˄ 13 | 14 | export class TableFactory implements Factory { 15 | // ˅ 16 | 17 | // ˄ 18 | 19 | createPage(title: string, author: string): Page { 20 | // ˅ 21 | return new TablePage(title, author); 22 | // ˄ 23 | } 24 | 25 | createLink(name: string, url: string): Link { 26 | // ˅ 27 | return new TableLink(name, url); 28 | // ˄ 29 | } 30 | 31 | createData(name: string): Data { 32 | // ˅ 33 | return new TableData(name); 34 | // ˄ 35 | } 36 | 37 | // ˅ 38 | 39 | // ˄ 40 | } 41 | 42 | // ˅ 43 | 44 | // ˄ 45 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/table-factory/table-link.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Link } from '../factory/link'; 5 | 6 | // ˄ 7 | 8 | export class TableLink extends Link { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | constructor(name: string, url: string) { 14 | // ˅ 15 | super(name, url); 16 | // ˄ 17 | } 18 | 19 | toHTML(): string { 20 | // ˅ 21 | return ` ${this.name}\n`; 22 | // ˄ 23 | } 24 | 25 | // ˅ 26 | 27 | // ˄ 28 | } 29 | 30 | // ˅ 31 | 32 | // ˄ 33 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/table-factory/table-page.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Page } from '../factory/page'; 5 | 6 | // ˄ 7 | 8 | export class TablePage extends Page { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | constructor(title: string, author: string) { 14 | // ˅ 15 | super(title, author); 16 | // ˄ 17 | } 18 | 19 | toHTML(): string { 20 | // ˅ 21 | const htmlData: Array = new Array(); 22 | htmlData.push(`${this.title}\n`); 23 | htmlData.push(`

    ${this.title}

    \n`); 24 | htmlData.push(`\n`); 25 | for (let content of this.contents) { 26 | htmlData.push(`${content.toHTML()}\n`); 27 | } 28 | htmlData.push(`
    \n`); 29 | htmlData.push(`
    ${this.author}
    \n`); 30 | htmlData.push(`\n`); 31 | return htmlData.join(``); 32 | // ˄ 33 | } 34 | 35 | // ˅ 36 | 37 | // ˄ 38 | } 39 | 40 | // ˅ 41 | 42 | // ˄ 43 | -------------------------------------------------------------------------------- /creational-patterns/abstract-factory/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /creational-patterns/builder/builder.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface Builder { 7 | 8 | createTitle(title: string): void; 9 | 10 | createSection(section: string): void; 11 | 12 | createItems(items: Array): void; 13 | 14 | close(): void; 15 | 16 | // ˅ 17 | 18 | // ˄ 19 | } 20 | 21 | // ˅ 22 | 23 | // ˄ 24 | -------------------------------------------------------------------------------- /creational-patterns/builder/director.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Builder } from './builder'; 5 | 6 | // ˄ 7 | 8 | export class Director { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly builder: Builder; 14 | 15 | constructor(builder: Builder) { 16 | // ˅ 17 | this.builder = builder; 18 | 19 | // ˄ 20 | } 21 | 22 | // Construct a document 23 | build(): void { 24 | // ˅ 25 | this.builder.createTitle(`Greeting`); // Title 26 | this.builder.createSection(`Morning and Afternoon`); // Section 27 | this.builder.createItems([`Good morning.`, `Hello.`]); // Items 28 | this.builder.createSection(`Evening`); // Other section 29 | this.builder.createItems([`Good evening.`, `Good night.`, `Goodbye.`]); // Other items 30 | this.builder.close(); 31 | // ˄ 32 | } 33 | 34 | // ˅ 35 | 36 | // ˄ 37 | } 38 | 39 | // ˅ 40 | 41 | // ˄ 42 | -------------------------------------------------------------------------------- /creational-patterns/builder/html-builder.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import * as fs from 'fs'; 5 | import { WriteStream } from 'fs'; 6 | import { Builder } from './builder'; 7 | 8 | // ˄ 9 | 10 | export class HTMLBuilder implements Builder { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | // File name to create 16 | private fileName: string; 17 | 18 | private writer: WriteStream; 19 | 20 | constructor() { 21 | // ˅ 22 | this.fileName = ``; 23 | this.writer = null; 24 | // ˄ 25 | } 26 | 27 | // Make a title of HTML file 28 | createTitle(title: string): void { 29 | // ˅ 30 | this.fileName = `${title}.html`; // Set a title as a file name 31 | this.writer = fs.createWriteStream(this.fileName, `utf8`); 32 | this.writer.write(`${title}\n`); // Write a title 33 | this.writer.write(`

    ${title}

    \n`); 34 | // ˄ 35 | } 36 | 37 | // Make a section of HTML file 38 | createSection(section: string): void { 39 | // ˅ 40 | this.writer.write(`

    ${section}

    \n`); // Write a section 41 | // ˄ 42 | } 43 | 44 | // Make items of HTML file 45 | createItems(items: Array): void { 46 | // ˅ 47 | this.writer.write(`
      \n`); // Write items 48 | for (let item of items) { 49 | this.writer.write(`
    • ${item}
    • \n`); 50 | } 51 | this.writer.write(`
    \n`); 52 | // ˄ 53 | } 54 | 55 | close(): void { 56 | // ˅ 57 | this.writer.write(`\n`); 58 | this.writer.end(); // Close file 59 | // ˄ 60 | } 61 | 62 | getFileName(): string { 63 | // ˅ 64 | return this.fileName; 65 | // ˄ 66 | } 67 | 68 | // ˅ 69 | 70 | // ˄ 71 | } 72 | 73 | // ˅ 74 | 75 | // ˄ 76 | -------------------------------------------------------------------------------- /creational-patterns/builder/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Create documents in HTML format and text format. It is possible to create different documents 3 | in the same construction process. 4 | */ 5 | 'use strict'; 6 | 7 | import { HTMLBuilder } from './html-builder'; 8 | import { Director } from './director'; 9 | import { PlainTextBuilder } from './plain-text-builder'; 10 | import * as path from 'path'; 11 | import * as rl from 'readline'; 12 | 13 | console.log(`Please enter "plain" or "html":`); 14 | 15 | const readline = rl.createInterface({ 16 | input: process.stdin, 17 | output: process.stdout 18 | }); 19 | 20 | readline.question(``, (data: any) => { 21 | const input: string = data; 22 | readline.close(); 23 | 24 | if (input === `plain`) { 25 | const plainTextBuilder: PlainTextBuilder = new PlainTextBuilder(); 26 | const directory: Director = new Director(plainTextBuilder); 27 | directory.build(); 28 | 29 | const content: string = plainTextBuilder.getContent(); 30 | console.log(content); 31 | } 32 | else if (input === `html`) { 33 | const htmlBuilder: HTMLBuilder = new HTMLBuilder(); 34 | const directory: Director = new Director(htmlBuilder); 35 | directory.build(); 36 | 37 | const fileName: string = htmlBuilder.getFileName(); 38 | console.log(`${fileName} has been created.`); 39 | console.log(`Output File: ${path.join(process.cwd(), fileName)}`); 40 | } 41 | else { 42 | console.error(`The value is not "plain" or "html".`); 43 | process.exit(1); 44 | } 45 | }); 46 | -------------------------------------------------------------------------------- /creational-patterns/builder/plain-text-builder.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Builder } from './builder'; 5 | 6 | // ˄ 7 | 8 | export class PlainTextBuilder implements Builder { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private builder: Array; 14 | 15 | constructor() { 16 | // ˅ 17 | this.builder = new Array(); 18 | // ˄ 19 | } 20 | 21 | // Make a title of plain text 22 | createTitle(title: string): void { 23 | // ˅ 24 | this.builder.push(`--------------------------------`); // Decoration line 25 | this.builder.push(`[${title}]`); // Title 26 | this.builder.push(``); // Blank line 27 | // ˄ 28 | } 29 | 30 | // Make a section of plain text 31 | createSection(section: string): void { 32 | // ˅ 33 | this.builder.push(`* ${section}`); // Section 34 | this.builder.push(``); // Blank line 35 | // ˄ 36 | } 37 | 38 | // Make items of plain text 39 | createItems(items: Array): void { 40 | // ˅ 41 | for (let item of items) { 42 | this.builder.push(` - ${item}`); // Items 43 | } 44 | this.builder.push(``); // Blank line 45 | // ˄ 46 | } 47 | 48 | close(): void { 49 | // ˅ 50 | this.builder.push(`--------------------------------`); // Decoration line 51 | // ˄ 52 | } 53 | 54 | // String to output 55 | getContent(): string { 56 | // ˅ 57 | return this.builder.join(`\n`); 58 | // ˄ 59 | } 60 | 61 | // ˅ 62 | 63 | // ˄ 64 | } 65 | 66 | // ˅ 67 | 68 | // ˄ 69 | -------------------------------------------------------------------------------- /creational-patterns/builder/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /creational-patterns/factory-method/credit-card/credit-card-factory.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { CreditCard } from './credit-card'; 5 | import { Factory } from '../framework/factory'; 6 | import { Product } from '../framework/product'; 7 | 8 | // ˄ 9 | 10 | export class CreditCardFactory extends Factory { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | constructor() { 16 | // ˅ 17 | super(); 18 | // ˄ 19 | } 20 | 21 | protected createProduct(owner: string): Product { 22 | // ˅ 23 | return new CreditCard(owner); 24 | // ˄ 25 | } 26 | 27 | // ˅ 28 | 29 | // ˄ 30 | } 31 | 32 | // ˅ 33 | 34 | // ˄ 35 | -------------------------------------------------------------------------------- /creational-patterns/factory-method/credit-card/credit-card.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Product } from '../framework/product'; 5 | 6 | // ˄ 7 | 8 | export class CreditCard implements Product { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly owner: string; 14 | 15 | constructor(owner: string) { 16 | // ˅ 17 | this.owner = owner; 18 | console.log(`Make ${this.owner}'s card.`); 19 | // ˄ 20 | } 21 | 22 | use(): void { 23 | // ˅ 24 | console.log(`Use ${this.owner}'s card.`); 25 | // ˄ 26 | } 27 | 28 | // ˅ 29 | 30 | // ˄ 31 | } 32 | 33 | // ˅ 34 | 35 | // ˄ 36 | -------------------------------------------------------------------------------- /creational-patterns/factory-method/framework/factory.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Product } from './product'; 5 | 6 | // ˄ 7 | 8 | export abstract class Factory { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | create(owner: string): Product { 14 | // ˅ 15 | // Write pre-creation code here. 16 | 17 | // Encapsulate the knowledge of which Product subclass to create and move this knowledge out of the framework. 18 | let product = this.createProduct(owner); 19 | 20 | // Write post-creation code here. 21 | 22 | return product; 23 | // ˄ 24 | } 25 | 26 | protected abstract createProduct(owner: string): Product; 27 | 28 | // ˅ 29 | 30 | // ˄ 31 | } 32 | 33 | // ˅ 34 | 35 | // ˄ 36 | -------------------------------------------------------------------------------- /creational-patterns/factory-method/framework/product.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface Product { 7 | 8 | use(): void; 9 | 10 | // ˅ 11 | 12 | // ˄ 13 | } 14 | 15 | // ˅ 16 | 17 | // ˄ 18 | -------------------------------------------------------------------------------- /creational-patterns/factory-method/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | The subject is a factory to make credit cards. The Factory defines how to create a credit card, 3 | but the actual credit card is created by the CreditCardFactory. 4 | The "createProduct()" is called a Factory Method, and it is responsible for manufacturing an object. 5 | */ 6 | 'use strict'; 7 | 8 | import { Factory } from './framework/factory'; 9 | import { Product } from './framework/product'; 10 | import { CreditCardFactory } from './credit-card/credit-card-factory'; 11 | 12 | const factory: Factory = new CreditCardFactory(); 13 | 14 | const jacksonCard: Product = factory.create(`Jackson`); 15 | jacksonCard.use(); 16 | 17 | const sophiaCard: Product = factory.create(`Sophia`); 18 | sophiaCard.use(); 19 | 20 | const oliviaCard: Product = factory.create(`Olivia`); 21 | oliviaCard.use(); 22 | -------------------------------------------------------------------------------- /creational-patterns/factory-method/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /creational-patterns/prototype/frame-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Display } from './framework/display'; 5 | 6 | // ˄ 7 | 8 | export class FrameDisplay implements Display { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly borderChar: string; 14 | 15 | constructor(borderChar: string) { 16 | // ˅ 17 | this.borderChar = borderChar; 18 | // ˄ 19 | } 20 | 21 | clone(): Display { 22 | // ˅ 23 | return new FrameDisplay(this.borderChar); 24 | // ˄ 25 | } 26 | 27 | show(message: string): void { 28 | // ˅ 29 | console.log(this.borderChar.repeat(message.length + 4)); 30 | console.log(`${this.borderChar} ${message} ${this.borderChar}`); 31 | console.log(this.borderChar.repeat(message.length + 4)); 32 | // ˄ 33 | } 34 | 35 | // ˅ 36 | 37 | // ˄ 38 | } 39 | 40 | // ˅ 41 | 42 | // ˄ 43 | -------------------------------------------------------------------------------- /creational-patterns/prototype/framework/display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface Display { 7 | 8 | clone(): Display; 9 | 10 | show(message: string): void; 11 | 12 | // ˅ 13 | 14 | // ˄ 15 | } 16 | 17 | // ˅ 18 | 19 | // ˄ 20 | -------------------------------------------------------------------------------- /creational-patterns/prototype/framework/manager.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Display } from './display'; 5 | 6 | // ˄ 7 | 8 | export class Manager { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly display: Map; 14 | 15 | constructor() { 16 | // ˅ 17 | this.display = new Map(); 18 | // ˄ 19 | } 20 | 21 | registerDisplay(displayName: string, display: Display): void { 22 | // ˅ 23 | this.display.set(displayName, display); 24 | // ˄ 25 | } 26 | 27 | getDisplay(displayName: string): Display { 28 | // ˅ 29 | const d: Display = this.display.get(displayName); 30 | return d.clone(); // Create a new object by asking a concrete class to clone itself. Therefore, do not need to know the concrete Display class name. 31 | // ˄ 32 | } 33 | 34 | // ˅ 35 | 36 | // ˄ 37 | } 38 | 39 | // ˅ 40 | 41 | // ˄ 42 | -------------------------------------------------------------------------------- /creational-patterns/prototype/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Display a string enclosed with a frame line, or drawn with an underline. The Client (Main) 3 | registers instances of the Display subclass in the Manager class. When necessary, 4 | the Manager class asks those registered instances to return a clone. The Client (Main) 5 | requires the returned clones to display. 6 | */ 7 | 'use strict'; 8 | 9 | import { Manager } from './framework/manager'; 10 | import { Display } from './framework/display'; 11 | import { UnderlineDisplay } from './underline-display'; 12 | import { FrameDisplay } from './frame-display'; 13 | 14 | const manager: Manager = new Manager(); 15 | 16 | // Register instances of the "Display" subclass 17 | const emphasisUnderline: Display = new UnderlineDisplay(`~`); 18 | manager.registerDisplay(`emphasis`, emphasisUnderline); 19 | const highlightFrame: Display = new FrameDisplay(`+`); 20 | manager.registerDisplay(`highlight`, highlightFrame); 21 | const warningFrame: Display = new FrameDisplay(`#`); 22 | manager.registerDisplay(`warning`, warningFrame); 23 | 24 | // Require to display 25 | const display1: Display = manager.getDisplay(`emphasis`); 26 | display1.show(`Nice to meet you.`); 27 | const display2: Display = manager.getDisplay(`highlight`); 28 | display2.show(`Nice to meet you.`); 29 | const display3: Display = manager.getDisplay(`warning`); 30 | display3.show(`Nice to meet you.`); 31 | -------------------------------------------------------------------------------- /creational-patterns/prototype/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /creational-patterns/prototype/underline-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Display } from './framework/display'; 5 | 6 | // ˄ 7 | 8 | export class UnderlineDisplay implements Display { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly underlineChar: string; 14 | 15 | constructor(underlineChar: string) { 16 | // ˅ 17 | this.underlineChar = underlineChar; 18 | // ˄ 19 | } 20 | 21 | clone(): Display { 22 | // ˅ 23 | return new UnderlineDisplay(this.underlineChar); 24 | // ˄ 25 | } 26 | 27 | show(message: string): void { 28 | // ˅ 29 | console.log(`"${message}"`); 30 | console.log(` ${this.underlineChar.repeat(message.length)}`); 31 | // ˄ 32 | } 33 | 34 | // ˅ 35 | 36 | // ˄ 37 | } 38 | 39 | // ˅ 40 | 41 | // ˄ 42 | -------------------------------------------------------------------------------- /creational-patterns/singleton/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Check whether the same instance is obtained. 3 | */ 4 | 'use strict'; 5 | 6 | import { Singleton } from './singleton'; 7 | 8 | const obj1 = Singleton.getInstance(); 9 | const obj2 = Singleton.getInstance(); 10 | if (obj1 === obj2) { 11 | console.log(`obj1 and obj2 are the same instance.`); 12 | } else { 13 | console.log(`obj1 and obj2 are different instances.`); 14 | } 15 | -------------------------------------------------------------------------------- /creational-patterns/singleton/singleton.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export class Singleton { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | private static readonly instance: Singleton = new Singleton(); 12 | 13 | static getInstance(): Singleton { 14 | // ˅ 15 | return this.instance; 16 | // ˄ 17 | } 18 | 19 | private constructor() { 20 | // ˅ 21 | 22 | // ˄ 23 | } 24 | 25 | // ˅ 26 | 27 | // ˄ 28 | } 29 | 30 | // ˅ 31 | 32 | // ˄ 33 | -------------------------------------------------------------------------------- /creational-patterns/singleton/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /design-pattern-examples-in-typescript.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "liveServer.settings.NoBrowser": true, 9 | "liveServer.settings.CustomBrowser": "chrome", 10 | "liveServer.settings.port": 5500, 11 | "code-runner.showRunCommandInEditorContextMenu": false, 12 | "code-runner.showRunCommandInExplorerContextMenu": false, 13 | "code-runner.showRunIconInEditorTitleMenu": false 14 | } 15 | } -------------------------------------------------------------------------------- /model/DesignPatternExamplesInTypescript.asta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takaakit/design-pattern-examples-in-typescript/bf47c0a4f180238bda3b9a6a8421dff092d2dc8b/model/DesignPatternExamplesInTypescript.asta -------------------------------------------------------------------------------- /model/m_plus.conf: -------------------------------------------------------------------------------- 1 | TARGET_DIRECTORY=.. 2 | PROGRAMMING_LANGUAGE=TypeScript 3 | CPP_HEADER_EXTENSION=h 4 | CPP_SOURCE_EXTENSION=cpp 5 | CSHARP_EXTENSION=cs 6 | CRYSTAL_EXTENSION=cr 7 | GO_EXTENSION=go 8 | JAVA_EXTENSION=java 9 | JAVASCRIPT_EXTENSION=mjs 10 | KOTLIN_EXTENSION=kt 11 | PYTHON_EXTENSION=py 12 | RUBY_EXTENSION=rb 13 | SCALA_EXTENSION=scala 14 | SWIFT_EXTENSION=swift 15 | TYPESCRIPT_EXTENSION=ts 16 | ALLOY_EXTENSION=als 17 | VDMPP_EXTENSION=vdmpp 18 | PYTHON_VERSION= 19 | -------------------------------------------------------------------------------- /screenshots/BuildError.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takaakit/design-pattern-examples-in-typescript/bf47c0a4f180238bda3b9a6a8421dff092d2dc8b/screenshots/BuildError.png -------------------------------------------------------------------------------- /screenshots/CompileAndRun.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takaakit/design-pattern-examples-in-typescript/bf47c0a4f180238bda3b9a6a8421dff092d2dc8b/screenshots/CompileAndRun.gif -------------------------------------------------------------------------------- /screenshots/GenerateCode.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takaakit/design-pattern-examples-in-typescript/bf47c0a4f180238bda3b9a6a8421dff092d2dc8b/screenshots/GenerateCode.gif -------------------------------------------------------------------------------- /screenshots/GoLiveButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/takaakit/design-pattern-examples-in-typescript/bf47c0a4f180238bda3b9a6a8421dff092d2dc8b/screenshots/GoLiveButton.png -------------------------------------------------------------------------------- /structural-patterns/adapter/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Display the given string as follows 3 | ``` 4 | -- Nice to meet you -- 5 | ``` 6 | or display it as follows. 7 | ``` 8 | [[ Nice to meet you ]] 9 | ``` 10 | */ 11 | 'use strict'; 12 | 13 | import { Print } from './print'; 14 | import { PrintMessageDisplay } from './print-message-display'; 15 | 16 | const p: Print = new PrintMessageDisplay(`Nice to meet you`); 17 | p.printWeak(); 18 | p.printStrong(); 19 | -------------------------------------------------------------------------------- /structural-patterns/adapter/message-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export class MessageDisplay { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | private readonly message: string; 12 | 13 | constructor(message: string) { 14 | // ˅ 15 | this.message = message; 16 | // ˄ 17 | } 18 | 19 | displayWithHyphens(): void { 20 | // ˅ 21 | console.log(`-- ${this.message} --`); 22 | // ˄ 23 | } 24 | 25 | displayWithBrackets(): void { 26 | // ˅ 27 | console.log(`[[ ${this.message} ]]`); 28 | // ˄ 29 | } 30 | 31 | // ˅ 32 | 33 | // ˄ 34 | } 35 | 36 | // ˅ 37 | 38 | // ˄ 39 | -------------------------------------------------------------------------------- /structural-patterns/adapter/print-message-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Print } from './print'; 5 | import { MessageDisplay } from './message-display'; 6 | 7 | // ˄ 8 | 9 | // Adapt the MessageDisplay interface to the Print interface. 10 | export class PrintMessageDisplay extends MessageDisplay implements Print { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | constructor(message: string) { 16 | // ˅ 17 | super(message); 18 | // ˄ 19 | } 20 | 21 | printWeak(): void { 22 | // ˅ 23 | this.displayWithHyphens(); 24 | // ˄ 25 | } 26 | 27 | printStrong(): void { 28 | // ˅ 29 | this.displayWithBrackets(); 30 | // ˄ 31 | } 32 | 33 | // ˅ 34 | 35 | // ˄ 36 | } 37 | 38 | // ˅ 39 | 40 | // ˄ 41 | -------------------------------------------------------------------------------- /structural-patterns/adapter/print.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface Print { 7 | 8 | printWeak(): void; 9 | 10 | printStrong(): void; 11 | 12 | // ˅ 13 | 14 | // ˄ 15 | } 16 | 17 | // ˅ 18 | 19 | // ˄ 20 | -------------------------------------------------------------------------------- /structural-patterns/adapter/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /structural-patterns/bridge/display-impl.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface DisplayImpl { 7 | 8 | implOpen(): void; 9 | 10 | implWrite(): void; 11 | 12 | implClose(): void; 13 | 14 | // ˅ 15 | 16 | // ˄ 17 | } 18 | 19 | // ˅ 20 | 21 | // ˄ 22 | -------------------------------------------------------------------------------- /structural-patterns/bridge/display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { DisplayImpl } from './display-impl'; 5 | 6 | // ˄ 7 | 8 | export class Display { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly impl: DisplayImpl; 14 | 15 | constructor(impl: DisplayImpl) { 16 | // ˅ 17 | this.impl = impl; 18 | // ˄ 19 | } 20 | 21 | output(): void { 22 | // ˅ 23 | this.open(); 24 | this.write(); 25 | this.close(); 26 | // ˄ 27 | } 28 | 29 | protected open(): void { 30 | // ˅ 31 | this.impl.implOpen(); 32 | // ˄ 33 | } 34 | 35 | protected write(): void { 36 | // ˅ 37 | this.impl.implWrite(); 38 | // ˄ 39 | } 40 | 41 | protected close(): void { 42 | // ˅ 43 | this.impl.implClose(); 44 | // ˄ 45 | } 46 | 47 | // ˅ 48 | 49 | // ˄ 50 | } 51 | 52 | // ˅ 53 | 54 | // ˄ 55 | -------------------------------------------------------------------------------- /structural-patterns/bridge/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Display only one line or display the specified number of lines. 3 | */ 4 | 'use strict'; 5 | 6 | import { Display } from './display'; 7 | import { MultiLineDisplay } from './multi-line-display'; 8 | import { TextDisplayImpl } from './text-display-impl'; 9 | 10 | const d1: Display = new Display(new TextDisplayImpl(`Japan`)); 11 | d1.output(); 12 | 13 | const d2: MultiLineDisplay = new MultiLineDisplay(new TextDisplayImpl(`The United States of America`)); 14 | d2.output(); 15 | d2.outputMultiple(3); 16 | -------------------------------------------------------------------------------- /structural-patterns/bridge/multi-line-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Display } from './display'; 5 | import { DisplayImpl } from './display-impl'; 6 | 7 | // ˄ 8 | 9 | export class MultiLineDisplay extends Display { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | constructor(impl: DisplayImpl) { 15 | // ˅ 16 | super(impl); 17 | // ˄ 18 | } 19 | 20 | // Repeat display for the specified number of times 21 | outputMultiple(times: number): void { 22 | // ˅ 23 | this.open(); 24 | for (let i = 0; i < times; i++) { 25 | this.write(); 26 | } 27 | this.close(); 28 | // ˄ 29 | } 30 | 31 | // ˅ 32 | 33 | // ˄ 34 | } 35 | 36 | // ˅ 37 | 38 | // ˄ 39 | -------------------------------------------------------------------------------- /structural-patterns/bridge/text-display-impl.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { DisplayImpl } from './display-impl'; 5 | 6 | // ˄ 7 | 8 | export class TextDisplayImpl implements DisplayImpl { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | // A string to display 14 | private readonly text: string; 15 | 16 | // A number of characters in bytes 17 | private readonly width: number; 18 | 19 | constructor(text: string) { 20 | // ˅ 21 | this.text = text; 22 | this.width = text.length; // Set the number of characters in bytes. 23 | // ˄ 24 | } 25 | 26 | implOpen(): void { 27 | // ˅ 28 | this.printLine(); 29 | // ˄ 30 | } 31 | 32 | implWrite(): void { 33 | // ˅ 34 | console.log(`:${this.text}:`); // Enclose a text with ":" and display it. 35 | // ˄ 36 | } 37 | 38 | implClose(): void { 39 | // ˅ 40 | this.printLine(); 41 | // ˄ 42 | } 43 | 44 | private printLine(): void { 45 | // ˅ 46 | process.stdout.write(`*`); // Display "*" mark at the beginning of a frame. 47 | process.stdout.write(`.`.repeat(this.width)); // Display "." for the number of "width". 48 | console.log(`*`); // Display "*" mark at the end of a frame. 49 | // ˄ 50 | } 51 | 52 | // ˅ 53 | 54 | // ˄ 55 | } 56 | 57 | // ˅ 58 | 59 | // ˄ 60 | -------------------------------------------------------------------------------- /structural-patterns/bridge/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /structural-patterns/composite/directory-element.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { FileSystemElement } from './file-system-element'; 5 | 6 | // ˄ 7 | 8 | export class DirectoryElement extends FileSystemElement { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly _name: string; 14 | 15 | private readonly elements: Array; 16 | 17 | constructor(name: string) { 18 | // ˅ 19 | super(); 20 | this._name = name; 21 | this.elements = new Array(); 22 | // ˄ 23 | } 24 | 25 | get name(): string { 26 | // ˅ 27 | return this._name; 28 | // ˄ 29 | } 30 | 31 | get size(): number { 32 | // ˅ 33 | let size: number = 0; 34 | for (let element of this.elements) { 35 | size += element.size; 36 | } 37 | return size; 38 | // ˄ 39 | } 40 | 41 | // Print this element with the "upperPath". 42 | print(upperPath: string): void { 43 | // ˅ 44 | console.log(`${upperPath}/${this.toString()}`); 45 | for (let element of this.elements) { 46 | element.print(`${upperPath}/${this.name}`); 47 | } 48 | // ˄ 49 | } 50 | 51 | // Add an element 52 | add(element: FileSystemElement): void { 53 | // ˅ 54 | this.elements.push(element); 55 | // ˄ 56 | } 57 | 58 | // ˅ 59 | 60 | // ˄ 61 | } 62 | 63 | // ˅ 64 | 65 | // ˄ 66 | -------------------------------------------------------------------------------- /structural-patterns/composite/file-element.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { FileSystemElement } from './file-system-element'; 5 | 6 | // ˄ 7 | 8 | export class FileElement extends FileSystemElement { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly _name: string; 14 | 15 | private readonly _size: number; 16 | 17 | constructor(name: string, size: number) { 18 | // ˅ 19 | super(); 20 | this._name = name; 21 | this._size = size; 22 | // ˄ 23 | } 24 | 25 | get name(): string { 26 | // ˅ 27 | return this._name; 28 | // ˄ 29 | } 30 | 31 | get size(): number { 32 | // ˅ 33 | return this._size; 34 | // ˄ 35 | } 36 | 37 | // Print this element with the "upperPath". 38 | print(upperPath: string): void { 39 | // ˅ 40 | console.log(`${upperPath}/${this.toString()}`); 41 | // ˄ 42 | } 43 | 44 | // ˅ 45 | 46 | // ˄ 47 | } 48 | 49 | // ˅ 50 | 51 | // ˄ 52 | -------------------------------------------------------------------------------- /structural-patterns/composite/file-system-element.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export abstract class FileSystemElement { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | abstract get name(): string; 12 | 13 | abstract get size(): number; 14 | 15 | // Print this element with the "upperPath". 16 | abstract print(upperPath: string): void; 17 | 18 | toString(): string { 19 | // ˅ 20 | return `${this.name} (${this.size})`; 21 | // ˄ 22 | } 23 | 24 | // ˅ 25 | 26 | // ˄ 27 | } 28 | 29 | // ˅ 30 | 31 | // ˄ 32 | -------------------------------------------------------------------------------- /structural-patterns/composite/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Represents a file system composed of files and directories. FileSystemElement makes 3 | it possible to treat File and Directory uniformly. 4 | */ 5 | 'use strict'; 6 | 7 | import { DirectoryElement } from './directory-element'; 8 | import { FileElement } from './file-element'; 9 | 10 | console.log(`Create a file system...`); 11 | 12 | const binDir: DirectoryElement = new DirectoryElement(`bin`); 13 | const lsFile = new FileElement(`ls`, 20); 14 | binDir.add(lsFile); 15 | const mkdirFile = new FileElement(`mkdir`, 40); 16 | binDir.add(mkdirFile); 17 | 18 | const emilyDir: DirectoryElement = new DirectoryElement(`emily`); 19 | const homeworkFile = new FileElement(`homework.doc`, 60); 20 | emilyDir.add(homeworkFile); 21 | 22 | const jamesDir: DirectoryElement = new DirectoryElement(`james`); 23 | const appFile = new FileElement(`app.exe`, 80); 24 | jamesDir.add(appFile); 25 | 26 | const homeDir: DirectoryElement = new DirectoryElement(`home`); 27 | homeDir.add(emilyDir); 28 | homeDir.add(jamesDir); 29 | 30 | const rootDir: DirectoryElement = new DirectoryElement(`root`); 31 | rootDir.add(homeDir); 32 | rootDir.add(binDir); 33 | 34 | rootDir.print(``); 35 | -------------------------------------------------------------------------------- /structural-patterns/composite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /structural-patterns/decorator/display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export abstract class Display { 7 | // ˅ 8 | 9 | // ˄ 10 | 11 | // Column width 12 | abstract get columns(): number; 13 | 14 | // Number of rows 15 | abstract get rows(): number; 16 | 17 | abstract getLineText(row: number): string; 18 | 19 | // Show all 20 | show(): void { 21 | // ˅ 22 | for (let i = 0; i < this.rows; i++) { 23 | console.log(this.getLineText(i)); 24 | } 25 | // ˄ 26 | } 27 | 28 | // ˅ 29 | 30 | // ˄ 31 | } 32 | 33 | // ˅ 34 | 35 | // ˄ 36 | -------------------------------------------------------------------------------- /structural-patterns/decorator/frame.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Display } from './display'; 5 | 6 | // ˄ 7 | 8 | export abstract class Frame extends Display { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | protected display: Display; 14 | 15 | protected constructor(display: Display) { 16 | // ˅ 17 | super(); 18 | this.display = display; 19 | // ˄ 20 | } 21 | 22 | // ˅ 23 | 24 | // ˄ 25 | } 26 | 27 | // ˅ 28 | 29 | // ˄ 30 | -------------------------------------------------------------------------------- /structural-patterns/decorator/full-frame.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Frame } from './frame'; 5 | import { Display } from './display'; 6 | 7 | // ˄ 8 | 9 | export class FullFrame extends Frame { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | constructor(display: Display) { 15 | // ˅ 16 | super(display); 17 | // ˄ 18 | } 19 | 20 | // Number of characters added left and right decoration characters 21 | get columns(): number { 22 | // ˅ 23 | return 1 + this.display.columns + 1; 24 | // ˄ 25 | } 26 | 27 | // Number of rows added the upper and lower decoration lines 28 | get rows(): number { 29 | // ˅ 30 | return 1 + this.display.rows + 1; 31 | // ˄ 32 | } 33 | 34 | getLineText(row: number): string { 35 | // ˅ 36 | if (row === 0) { 37 | return `+${this.createLine('-', this.display.columns)}+`; // Upper frame 38 | } 39 | else if (row === this.display.rows + 1) { 40 | return `+${this.createLine('-', this.display.columns)}+`; // Bottom frame 41 | } 42 | else { 43 | return `|${this.display.getLineText(row - 1)}|`; // Other 44 | } 45 | // ˄ 46 | } 47 | 48 | private createLine(ch: string, size: number): string { 49 | // ˅ 50 | return ch.repeat(size); 51 | // ˄ 52 | } 53 | 54 | // ˅ 55 | 56 | // ˄ 57 | } 58 | 59 | // ˅ 60 | 61 | // ˄ 62 | -------------------------------------------------------------------------------- /structural-patterns/decorator/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Display a string with decorative frames. The frames can be combined arbitrarily. 3 | */ 4 | 'use strict'; 5 | 6 | import { Display } from './display'; 7 | import { MessageDisplay } from './message-display'; 8 | import { SideFrame } from './side-frame'; 9 | import { FullFrame } from './full-frame'; 10 | 11 | const displayA: Display = new MessageDisplay(`Nice to meet you.`); 12 | displayA.show(); 13 | 14 | const displayB: Display = new SideFrame(new MessageDisplay(`Nice to meet you.`), `!`); 15 | displayB.show(); 16 | 17 | const displayC: Display = new FullFrame(new SideFrame(new MessageDisplay(`Nice to meet you.`), `!`)); 18 | displayC.show(); 19 | -------------------------------------------------------------------------------- /structural-patterns/decorator/message-display.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Display } from './display'; 5 | 6 | // ˄ 7 | 8 | export class MessageDisplay extends Display { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | // Message to be displayed 14 | private readonly message: string; 15 | 16 | constructor(message: string) { 17 | // ˅ 18 | super(); 19 | this.message = message; 20 | // ˄ 21 | } 22 | 23 | // Number of characters 24 | get columns(): number { 25 | // ˅ 26 | return this.message.length; 27 | // ˄ 28 | } 29 | 30 | // The number of rows is 1 31 | get rows(): number { 32 | // ˅ 33 | return 1; 34 | // ˄ 35 | } 36 | 37 | getLineText(row: number): string { 38 | // ˅ 39 | if (row === 0) { 40 | return this.message; 41 | } 42 | else { 43 | return null; 44 | } 45 | // ˄ 46 | } 47 | 48 | // ˅ 49 | 50 | // ˄ 51 | } 52 | 53 | // ˅ 54 | 55 | // ˄ 56 | -------------------------------------------------------------------------------- /structural-patterns/decorator/side-frame.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Frame } from './frame'; 5 | import { Display } from './display'; 6 | 7 | // ˄ 8 | 9 | export class SideFrame extends Frame { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | // Decoration character 15 | private readonly frameChar: string; 16 | 17 | constructor(display: Display, frameChar: string) { 18 | // ˅ 19 | super(display); 20 | 21 | if (frameChar.length !== 1) { 22 | console.error(`Only one character is allowed in a side frame.`); 23 | process.exit(1); 24 | } 25 | this.frameChar = frameChar; 26 | // ˄ 27 | } 28 | 29 | // Number of characters added left and right decoration characters 30 | get columns(): number { 31 | // ˅ 32 | return 1 + this.display.columns + 1; 33 | // ˄ 34 | } 35 | 36 | // Number of lines 37 | get rows(): number { 38 | // ˅ 39 | return this.display.rows; 40 | // ˄ 41 | } 42 | 43 | getLineText(row: number): string { 44 | // ˅ 45 | return this.frameChar + this.display.getLineText(row) + this.frameChar; 46 | // ˄ 47 | } 48 | 49 | // ˅ 50 | 51 | // ˄ 52 | } 53 | 54 | // ˅ 55 | 56 | // ˄ 57 | -------------------------------------------------------------------------------- /structural-patterns/decorator/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /structural-patterns/facade/addressbook.txt: -------------------------------------------------------------------------------- 1 | william@example.com=William 2 | emily@example.com=Emily 3 | amelia@example.com=Amelia 4 | -------------------------------------------------------------------------------- /structural-patterns/facade/data-library.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import * as fs from 'fs'; 5 | 6 | // ˄ 7 | 8 | export class DataLibrary { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private static readonly instance: DataLibrary = new DataLibrary(); 14 | 15 | static getInstance(): DataLibrary { 16 | // ˅ 17 | return this.instance; 18 | // ˄ 19 | } 20 | 21 | private constructor() { 22 | // ˅ 23 | 24 | // ˄ 25 | } 26 | 27 | // Read a data library file. 28 | getData(dataLibraryFileName: string): Map { 29 | // ˅ 30 | const data: Map = new Map(); 31 | 32 | for (let line of fs.readFileSync(dataLibraryFileName, `utf8`).toString().split(/\r\n|\r|\n/)) { 33 | const keyAndValue: string[] = line.split(`=`); 34 | if (keyAndValue.length === 2) { 35 | data.set(keyAndValue[0], keyAndValue[1]); 36 | } 37 | } 38 | 39 | return data; 40 | // ˄ 41 | } 42 | 43 | // ˅ 44 | 45 | // ˄ 46 | } 47 | 48 | // ˅ 49 | 50 | // ˄ 51 | -------------------------------------------------------------------------------- /structural-patterns/facade/html-writer.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { WriteStream } from 'fs'; 5 | 6 | // ˄ 7 | 8 | export class HtmlWriter { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private readonly writer: WriteStream; 14 | 15 | constructor(writer: WriteStream) { 16 | // ˅ 17 | this.writer = writer; 18 | // ˄ 19 | } 20 | 21 | // Write a title 22 | heading(title: string): void { 23 | // ˅ 24 | this.writer.write(``); 25 | this.writer.write(`${title}`); 26 | this.writer.write(`\n`); 27 | this.writer.write(`

    ${title}

    \n`); 28 | // ˄ 29 | } 30 | 31 | // Write a paragraph 32 | paragraph(message: string): void { 33 | // ˅ 34 | this.writer.write(`

    ${message}

    \n`); 35 | // ˄ 36 | } 37 | 38 | // Write a mail address 39 | mailto(mailAddress: string, userName: string): void { 40 | // ˅ 41 | this.anchor(`mailto:${mailAddress}`, userName); 42 | // ˄ 43 | } 44 | 45 | close(): void { 46 | // ˅ 47 | this.writer.write(``); 48 | this.writer.write(`\n`); 49 | this.writer.end(); 50 | // ˄ 51 | } 52 | 53 | // Write a link 54 | private anchor(url: string, text: string): void { 55 | // ˅ 56 | this.paragraph(`${text}`); 57 | // ˄ 58 | } 59 | 60 | // ˅ 61 | 62 | // ˄ 63 | } 64 | 65 | // ˅ 66 | 67 | // ˄ 68 | -------------------------------------------------------------------------------- /structural-patterns/facade/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Create a simple homepage through a Facade (PageCreator). The Facade gets info from 3 | the DataLibrary and uses the info to create an HTML file. 4 | */ 5 | 'use strict'; 6 | 7 | import { PageCreator } from './page-creator'; 8 | 9 | PageCreator.getInstance().createSimpleHomepage(`emily@example.com`, `Homepage.html`); 10 | -------------------------------------------------------------------------------- /structural-patterns/facade/page-creator.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import * as fs from 'fs'; 5 | import * as path from 'path'; 6 | import { DataLibrary } from './data-library'; 7 | import { HtmlWriter } from './html-writer'; 8 | 9 | // ˄ 10 | 11 | export class PageCreator { 12 | // ˅ 13 | 14 | // ˄ 15 | 16 | private static readonly instance: PageCreator = new PageCreator(); 17 | 18 | static getInstance(): PageCreator { 19 | // ˅ 20 | return this.instance; 21 | // ˄ 22 | } 23 | 24 | private constructor() { 25 | // ˅ 26 | 27 | // ˄ 28 | } 29 | 30 | createSimpleHomepage(mailAddress: string, htmlFileName: string): void { 31 | // ˅ 32 | const addressBook: Map = DataLibrary.getInstance().getData(`${process.cwd()}/addressbook.txt`); 33 | const userName: string = addressBook.get(mailAddress); 34 | 35 | const writer: HtmlWriter = new HtmlWriter(fs.createWriteStream(htmlFileName, `utf8`)); 36 | writer.heading(`${userName}'s homepage`); 37 | writer.paragraph(`Welcome to ${userName}'s homepage.`); 38 | writer.paragraph(`Please email me at this address.`); 39 | writer.mailto(mailAddress, userName); 40 | writer.close(); 41 | console.log(`${htmlFileName} is created for ${mailAddress} (${userName})`); 42 | console.log(`Output File: ${path.join(process.cwd(), htmlFileName)}`); 43 | // ˄ 44 | } 45 | 46 | // ˅ 47 | 48 | // ˄ 49 | } 50 | 51 | // ˅ 52 | 53 | // ˄ 54 | -------------------------------------------------------------------------------- /structural-patterns/facade/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big0.txt: -------------------------------------------------------------------------------- 1 | 2 | ######## 3 | # ### 4 | # ### 5 | # ### 6 | # ### 7 | # ### 8 | ######## 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big1.txt: -------------------------------------------------------------------------------- 1 | 2 | #### 3 | ### 4 | ### 5 | ### 6 | ### 7 | ### 8 | ####### 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big2.txt: -------------------------------------------------------------------------------- 1 | 2 | ######## 3 | ### 4 | ### 5 | ######## 6 | # 7 | # 8 | ########## 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big3.txt: -------------------------------------------------------------------------------- 1 | 2 | ######## 3 | ### 4 | ### 5 | ######## 6 | ### 7 | # ### 8 | ######## 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big4.txt: -------------------------------------------------------------------------------- 1 | 2 | ##### 3 | # ### 4 | # ### 5 | # ### 6 | ########## 7 | ### 8 | ### 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big5.txt: -------------------------------------------------------------------------------- 1 | 2 | ######### 3 | # 4 | # 5 | ######### 6 | ### 7 | # ### 8 | ######## 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big6.txt: -------------------------------------------------------------------------------- 1 | 2 | ######## 3 | # 4 | # 5 | ######### 6 | # ### 7 | # ### 8 | ######## 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big7.txt: -------------------------------------------------------------------------------- 1 | 2 | ########## 3 | ### 4 | ### 5 | ### 6 | ### 7 | ### 8 | ### 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big8.txt: -------------------------------------------------------------------------------- 1 | 2 | ######## 3 | # ### 4 | # ### 5 | ######## 6 | # ### 7 | # ### 8 | ######## 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/big9.txt: -------------------------------------------------------------------------------- 1 | 2 | ######## 3 | # ### 4 | # ### 5 | ######### 6 | ### 7 | ### 8 | ######## 9 | 10 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/large-size-char-factory.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { LargeSizeChar } from './large-size-char'; 5 | 6 | // ˄ 7 | 8 | export class LargeSizeCharFactory { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private poolChars: Map; 14 | 15 | private static readonly instance: LargeSizeCharFactory = new LargeSizeCharFactory(); 16 | 17 | static getInstance(): LargeSizeCharFactory { 18 | // ˅ 19 | return this.instance; 20 | // ˄ 21 | } 22 | 23 | private constructor() { 24 | // ˅ 25 | this.poolChars = new Map(); 26 | // ˄ 27 | } 28 | 29 | // Create an instance of the large size character. 30 | getLargeSizeChar(charName: string): LargeSizeChar { 31 | // ˅ 32 | let lsc: LargeSizeChar = this.poolChars.get(charName); 33 | if (lsc === undefined) { 34 | lsc = new LargeSizeChar(charName); // Create an instance 35 | this.poolChars.set(charName, lsc); 36 | } 37 | return lsc; 38 | // ˄ 39 | } 40 | 41 | // ˅ 42 | 43 | // ˄ 44 | } 45 | 46 | // ˅ 47 | 48 | // ˄ 49 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/large-size-char.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import * as fs from 'fs'; 5 | 6 | // ˄ 7 | 8 | export class LargeSizeChar { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | // Display data of the large size character 14 | private displayData: string; 15 | 16 | constructor(charName: string) { 17 | // ˅ 18 | try { 19 | const buf = fs.readFileSync(`${process.cwd()}/big${charName}.txt`, `utf8`); 20 | this.displayData = buf.toString(); 21 | } 22 | catch { 23 | this.displayData = `${charName}?`; 24 | } 25 | // ˄ 26 | } 27 | 28 | // Display the large size character 29 | display(): void { 30 | // ˅ 31 | console.log(this.displayData); 32 | // ˄ 33 | } 34 | 35 | // ˅ 36 | 37 | // ˄ 38 | } 39 | 40 | // ˅ 41 | 42 | // ˄ 43 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/large-size-string.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { LargeSizeChar } from './large-size-char'; 5 | import { LargeSizeCharFactory } from './large-size-char-factory'; 6 | 7 | // ˄ 8 | 9 | export class LargeSizeString { 10 | // ˅ 11 | 12 | // ˄ 13 | 14 | private readonly largeSizeChars: Array; 15 | 16 | constructor(stringValue: string) { 17 | // ˅ 18 | this.largeSizeChars = new Array(); 19 | for (let i = 0; i < stringValue.length; i++) { 20 | this.largeSizeChars.push(LargeSizeCharFactory.getInstance().getLargeSizeChar(stringValue[i])); 21 | } 22 | // ˄ 23 | } 24 | 25 | display(): void { 26 | // ˅ 27 | for (let largeSizeChar of this.largeSizeChars) { 28 | largeSizeChar.display(); 29 | } 30 | // ˄ 31 | } 32 | 33 | // ˅ 34 | 35 | // ˄ 36 | } 37 | 38 | // ˅ 39 | 40 | // ˄ 41 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Display a string consisting of large characters (0-9 digits only). 3 | Large character objects are not created until they are needed. 4 | And the created objects are reused. 5 | 6 | Example Output 7 | ----- 8 | Please enter digits (ex. 1212123): 123 9 | 10 | #### 11 | ### 12 | ### 13 | ### 14 | ### 15 | ### 16 | ####### 17 | 18 | 19 | 20 | ######## 21 | ### 22 | ### 23 | ######## 24 | # 25 | # 26 | ########## 27 | 28 | 29 | 30 | ######## 31 | ### 32 | ### 33 | ######## 34 | ### 35 | # ### 36 | ######## 37 | */ 38 | 'use strict'; 39 | 40 | import { LargeSizeString } from './large-size-string'; 41 | import * as rl from 'readline'; 42 | 43 | console.log(`Please enter digits (ex. 1212123):`); 44 | 45 | const readline = rl.createInterface({ 46 | input: process.stdin, 47 | output: process.stdout 48 | }); 49 | 50 | readline.question(``, (data: any) => { 51 | const input: string = data; 52 | readline.close(); 53 | 54 | const lss = new LargeSizeString(input); 55 | lss.display(); 56 | }); 57 | -------------------------------------------------------------------------------- /structural-patterns/flyweight/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /structural-patterns/proxy/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | Print on a named printer. Setting and changing the printer name is done by Proxy (ProxyPrinter). 3 | At the time of printing, create an instance of the RealSubject (RealPrinter) for the first time. 4 | */ 5 | 'use strict'; 6 | 7 | import { ProxyPrinter } from './proxy-printer'; 8 | 9 | const p: ProxyPrinter = new ProxyPrinter(`PRINTER-A`); 10 | console.log(`The printer name is ${p.getName()}.`); 11 | p.changeName(`PRINTER-B`); 12 | console.log(`The printer name is ${p.getName()}.`); 13 | 14 | console.log(`Print start.`); 15 | p.output(`Nice to meet you.`); 16 | console.log(`Print exit.`); 17 | -------------------------------------------------------------------------------- /structural-patterns/proxy/printer.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | // ˄ 5 | 6 | export interface Printer { 7 | 8 | getName(): string; 9 | 10 | changeName(name: string): void; 11 | 12 | output(content: string): void; 13 | 14 | // ˅ 15 | 16 | // ˄ 17 | } 18 | 19 | // ˅ 20 | 21 | // ˄ 22 | -------------------------------------------------------------------------------- /structural-patterns/proxy/proxy-printer.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Printer } from './printer'; 5 | import { RealPrinter } from './real-printer'; 6 | 7 | // ˄ 8 | 9 | // ProxyPrinter forwards requests to RealPrinter when appropriate. 10 | export class ProxyPrinter implements Printer { 11 | // ˅ 12 | 13 | // ˄ 14 | 15 | private currentName: string; 16 | 17 | // A printer that actually prints 18 | private real: RealPrinter; 19 | 20 | constructor(name: string) { 21 | // ˅ 22 | this.currentName = name; 23 | this.real = null; 24 | // ˄ 25 | } 26 | 27 | getName(): string { 28 | // ˅ 29 | if (this.real !== null) { 30 | return this.real.getName(); 31 | } 32 | else { 33 | return this.currentName; 34 | } 35 | // ˄ 36 | } 37 | 38 | changeName(name: string): void { 39 | // ˅ 40 | if (this.real !== null) { 41 | this.real.changeName(name); 42 | } 43 | 44 | this.currentName = name; 45 | // ˄ 46 | } 47 | 48 | output(content: string): void { 49 | // ˅ 50 | // Check to see if the RealPrinter had been created, create it if necessary. 51 | if (this.real === null) { 52 | this.real = new RealPrinter(this.currentName); 53 | } 54 | 55 | this.real.output(content); 56 | // ˄ 57 | } 58 | 59 | // ˅ 60 | 61 | // ˄ 62 | } 63 | 64 | // ˅ 65 | 66 | // ˄ 67 | -------------------------------------------------------------------------------- /structural-patterns/proxy/real-printer.ts: -------------------------------------------------------------------------------- 1 | // ˅ 2 | 'use strict'; 3 | 4 | import { Printer } from './printer'; 5 | 6 | // ˄ 7 | 8 | export class RealPrinter implements Printer { 9 | // ˅ 10 | 11 | // ˄ 12 | 13 | private name: string; 14 | 15 | constructor(name: string) { 16 | // ˅ 17 | this.name = name; 18 | this.heavyTask(`Creating an instance (${this.name}) of the Printer`); 19 | // ˄ 20 | } 21 | 22 | getName(): string { 23 | // ˅ 24 | return this.name; 25 | // ˄ 26 | } 27 | 28 | changeName(name: string): void { 29 | // ˅ 30 | this.name = name; 31 | // ˄ 32 | } 33 | 34 | // Display a content with the name 35 | output(content: string): void { 36 | // ˅ 37 | console.log(`==========`); 38 | console.log(content); 39 | console.log(`Printed by ${this.name}`); 40 | console.log(`==========`); 41 | // ˄ 42 | } 43 | 44 | // Heavy task (Please think so...) 45 | private heavyTask(message: string): void { 46 | // ˅ 47 | process.stdout.write(message); 48 | for (let i = 0; i < 50; i++) { 49 | process.stdout.write(`.`); 50 | } 51 | console.log(`Done.`); 52 | // ˄ 53 | } 54 | 55 | // ˅ 56 | 57 | // ˄ 58 | } 59 | 60 | // ˅ 61 | 62 | // ˄ 63 | -------------------------------------------------------------------------------- /structural-patterns/proxy/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "out", 6 | "sourceMap": true 7 | }, 8 | "include": [ 9 | "./**/*.ts" 10 | ] 11 | } 12 | --------------------------------------------------------------------------------