├── .gitignore ├── LICENSE ├── README.md └── design-pattern ├── abstract-factory-pattern ├── README.md └── index.js ├── adapter-pattern ├── README.md └── index.js ├── bridge-pattern ├── README.md └── index.js ├── builder-pattern ├── README.md └── index.js ├── business-delegate-pattern ├── README.md └── index.js ├── chain-of-responsibility-pattern ├── README.md └── index.js ├── command-pattern ├── README.md └── index.js ├── composite-entity-pattern ├── README.md └── index.js ├── composite-pattern ├── README.md └── index.js ├── data-access-object-pattern ├── README.md └── index.js ├── decorator-pattern ├── README.md └── index.js ├── facade-pattern ├── README.md └── index.js ├── factory-pattern ├── README.md └── index.js ├── filter-pattern ├── README.md └── index.js ├── flyweight-pattern ├── README.md └── index.js ├── front-controller-pattern ├── README.md └── index.js ├── intercepting-filter-pattern ├── README.md └── index.js ├── interpreter-pattern ├── README.md └── index.js ├── iterator-pattern ├── README.md └── index.js ├── mediator-pattern ├── README.md └── index.js ├── memento-pattern ├── README.md └── index.js ├── mvc-pattern ├── README.md └── index.js ├── null-object-pattern ├── README.md └── index.js ├── observer-pattern ├── README.md └── index.js ├── prototype-pattern ├── README.md └── index.js ├── proxy-pattern ├── README.md └── index.js ├── service-locator-pattern ├── README.md └── index.js ├── singleton-pattern ├── README.md └── index.js ├── state-pattern ├── README.md └── index.js ├── strategy-pattern ├── README.md └── index.js ├── template-pattern ├── README.md └── index.js ├── transfer-object-pattern ├── README.md └── index.js └── visitor-pattern ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 ZhangYuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JavaScript中的设计模式 2 | 设计模式往往是软件设计中的最佳实践,是前人对问题解决的经验总结。很多时候你可能会发现在别人的代码会有一些和自己想法不一样的地方,但为什么不知道为什么要这样写,那么本文将会帮助你更好的理解。同时这也是适合新手和老鸟都适合的文章,对于新手而言可以开放自己的思维,对于老鸟而言在遇到问题的时候翻翻,可能也能获得新的思路。我认为由于JavaScript代码的简洁性,能给人更加容易理解和更直观的感受。 3 | 4 | 那么现在就开始对JavaScript设计模式的讲解吧。 5 | 6 | # 创建型模式 7 | * [工厂模式](./design-pattern/factory-pattern/README.md) 8 | * [抽象工厂模式](./design-pattern/abstract-factory-pattern/README.md) 9 | * [单例模式](./design-pattern/singleton-pattern/README.md) 10 | * [建造者模式](./design-pattern/builder-pattern/README.md) 11 | * [原型模式](./design-pattern/prototype-pattern/README.md) 12 | 13 | # 结构型模式 14 | * [适配器模式](./design-pattern/adapter-pattern/README.md) 15 | * [桥接模式](./design-pattern/bridge-pattern/README.md) 16 | * [过滤器模式](./design-pattern/filter-pattern/README.md) 17 | * [组合模式](./design-pattern/composite-pattern/README.md) 18 | * [装饰器模式](./design-pattern/decorator-pattern/README.md) 19 | * [外观模式](./design-pattern/facade-pattern/README.md) 20 | * [享元模式](./design-pattern/flyweight-pattern/README.md) 21 | * [代理模式](./design-pattern/proxy-pattern/README.md) 22 | 23 | # 行为型模式 24 | * [责任链模式](./design-pattern/chain-of-responsibility-pattern/README.md) 25 | * [命令模式](./design-pattern/command-pattern/README.md) 26 | * [解释器模式](./design-pattern/interpreter-pattern/README.md) 27 | * [迭代器模式](./design-pattern/iterator-pattern/README.md) 28 | * [中介者模式](./design-pattern/mediator-pattern/README.md) 29 | * [备忘录模式](./design-pattern/memento-pattern/README.md) 30 | * [观察者模式](./design-pattern/observer-pattern/README.md) 31 | * [状态模式](./design-pattern/state-pattern/README.md) 32 | * [空对象模式](./design-pattern/null-object-pattern/README.md) 33 | * [策略模式](./design-pattern/strategy-pattern/README.md) 34 | * [模板模式](./design-pattern/template-pattern/README.md) 35 | * [访问者模式](./design-pattern/visitor-pattern/README.md) 36 | 37 | # 设计模式的六大原则 38 | 1. 开闭原则(Open Close Principle) 39 | 2. 里氏代换原则(Liskov Substitution Principle) 40 | 3. 依赖倒转原则(Dependence Inversion Principle) 41 | 4. 接口隔离原则(Interface Segregation Principle) 42 | 5. 迪米特法则,又称最少知道原则(Demeter Principle) 43 | 6. 合成复用原则(Composite Reuse Principle) 44 | -------------------------------------------------------------------------------- /design-pattern/abstract-factory-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 抽象工厂模式(abstract Factory Pattern) 2 | 上文讲到了工厂模式,这篇文章将抽象工厂,抽象工厂的名字是真的很抽象,也很容易让人抽象的理解,那么什么是抽象工厂呢? 3 | 4 | 其实抽象工厂,简单来说就是工厂的工厂,因为一般来说一个工厂只负责加载一类组件,那么你有很多小类组件需要生产,那么势必会有很多小类的工厂。那么你最终生产一个大类,那就要很多小类的工厂负责生产。那么如何更方便的管理或者说生产这些工厂呢?那就用生产工厂的工厂来生成吧。 5 | 6 | # 抽象工厂模式的实例 7 | 先把上文说的形状工厂搬过来 8 | ```js 9 | // 这是之前上文说的形状工厂 10 | class Circle { 11 | draw() { 12 | console.log("I'm a circle") 13 | } 14 | } 15 | class Rectangle { 16 | draw() { 17 | console.log("I'm a rectangle") 18 | } 19 | } 20 | class Square { 21 | draw() { 22 | console.log("I'm a square") 23 | } 24 | } 25 | class ShapeFactory { 26 | getShape(shapeType){ 27 | switch(shapeType) { 28 | case 'CIRCLE': 29 | return new Circle(); 30 | case 'RECTANGLE': 31 | return new Rectangle(); 32 | case 'SQUARE': 33 | return new Square(); 34 | default: 35 | return null; 36 | } 37 | } 38 | } 39 | ``` 40 | 这时候你已经有形状了,但你觉得不美观,你还需要颜色,那么你这个时候,你又搞了个颜色工厂,如下: 41 | ```js 42 | // 再新加一个颜色工厂 43 | class Red { 44 | fill() { 45 | console.log("fill red") 46 | } 47 | } 48 | class Blue { 49 | fill() { 50 | console.log("fill blue") 51 | } 52 | } 53 | class Green { 54 | fill() { 55 | console.log("fill green") 56 | } 57 | } 58 | 59 | class ColorFactory { 60 | getColor(color){ 61 | switch(color) { 62 | case 'RED': 63 | return new Red(); 64 | case 'BLUE': 65 | return new Blue(); 66 | case 'GREEN': 67 | return new Green(); 68 | default: 69 | return null; 70 | } 71 | } 72 | } 73 | ``` 74 | 颜色工厂好了,但是你担心,以后工厂多了,不好管理咋办,那还是走之前的套路,把工厂通过抽象工厂生产出来。如下: 75 | ```js 76 | // 最后添加抽象工厂 77 | class FactoryProducer { 78 | static getFactory(choice){ 79 | switch(choice) { 80 | case 'SHAPE': 81 | return new ShapeFactory(); 82 | case 'COLOR': 83 | return new ColorFactory(); 84 | default: 85 | return null; 86 | } 87 | } 88 | } 89 | ``` 90 | 那么这个时候和上文一样只需要new出一个抽象工厂,就能把所有需要的东西拿到手了: 91 | ```js 92 | //通过抽象工厂拿形状工厂 93 | const shapeFactory = FactoryProducer.getFactory('SHAPE'); 94 | // 通过工厂拿各种形状 95 | const shape1 = shapeFactory.getShape('CIRCLE'); 96 | shape1.draw(); 97 | const shape2 = shapeFactory.getShape('RECTANGLE'); 98 | shape2.draw(); 99 | const shape3 = shapeFactory.getShape('SQUARE'); 100 | shape3.draw(); 101 | 102 | //通过抽象工厂拿颜色工厂 103 | const colorFactory = FactoryProducer.getFactory('COLOR'); 104 | // 通过工厂拿各种颜色 105 | const color1 = colorFactory.getColor('RED'); 106 | color1.fill(); 107 | const color2 = colorFactory.getColor('BLUE'); 108 | color2.fill(); 109 | const color3 = colorFactory.getColor('GREEN'); 110 | color3.fill(); 111 | /** 112 | * output: 113 | * I'm a circle 114 | * I'm a rectangle 115 | * I'm a square 116 | * fill red 117 | * fill blue 118 | * fill green 119 | */ 120 | ``` 121 | # 抽象工厂模式的优势 122 | 那么使用抽象工厂模式的好处和工厂模式的好处很相似,给工厂做了一个统一的出入口,也方便了日后对这个工厂的修改。 123 | 124 | [上一页(工厂模式)](../factory-pattern/README.md) 125 | 126 | [下一页(单例模式)](../singleton-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/abstract-factory-pattern/index.js: -------------------------------------------------------------------------------- 1 | // 这是之前上文说的形状工厂 2 | class Circle { 3 | draw() { 4 | console.log("I'm a circle") 5 | } 6 | } 7 | class Rectangle { 8 | draw() { 9 | console.log("I'm a rectangle") 10 | } 11 | } 12 | class Square { 13 | draw() { 14 | console.log("I'm a square") 15 | } 16 | } 17 | class ShapeFactory { 18 | getShape(shapeType){ 19 | switch(shapeType) { 20 | case 'CIRCLE': 21 | return new Circle(); 22 | case 'RECTANGLE': 23 | return new Rectangle(); 24 | case 'SQUARE': 25 | return new Square(); 26 | default: 27 | return null; 28 | } 29 | } 30 | } 31 | // 再新加一个颜色工厂 32 | class Red { 33 | fill() { 34 | console.log("fill red") 35 | } 36 | } 37 | class Blue { 38 | fill() { 39 | console.log("fill blue") 40 | } 41 | } 42 | class Green { 43 | fill() { 44 | console.log("fill green") 45 | } 46 | } 47 | 48 | class ColorFactory { 49 | getColor(color){ 50 | switch(color) { 51 | case 'RED': 52 | return new Red(); 53 | case 'BLUE': 54 | return new Blue(); 55 | case 'GREEN': 56 | return new Green(); 57 | default: 58 | return null; 59 | } 60 | } 61 | } 62 | // 最后添加抽象工厂 63 | class FactoryProducer { 64 | static getFactory(choice){ 65 | switch(choice) { 66 | case 'SHAPE': 67 | return new ShapeFactory(); 68 | case 'COLOR': 69 | return new ColorFactory(); 70 | default: 71 | return null; 72 | } 73 | } 74 | } 75 | 76 | // 运行代码 77 | //通过抽象工厂拿形状工厂 78 | const shapeFactory = FactoryProducer.getFactory('SHAPE'); 79 | // 通过工厂拿各种形状 80 | const shape1 = shapeFactory.getShape('CIRCLE'); 81 | shape1.draw(); 82 | const shape2 = shapeFactory.getShape('RECTANGLE'); 83 | shape2.draw(); 84 | const shape3 = shapeFactory.getShape('SQUARE'); 85 | shape3.draw(); 86 | 87 | //通过抽象工厂拿颜色工厂 88 | const colorFactory = FactoryProducer.getFactory('COLOR'); 89 | // 通过工厂拿各种颜色 90 | const color1 = colorFactory.getColor('RED'); 91 | color1.fill(); 92 | const color2 = colorFactory.getColor('BLUE'); 93 | color2.fill(); 94 | const color3 = colorFactory.getColor('GREEN'); 95 | color3.fill(); 96 | /** 97 | * output: 98 | * I'm a circle 99 | * I'm a rectangle 100 | * I'm a square 101 | * fill red 102 | * fill blue 103 | * fill green 104 | */ -------------------------------------------------------------------------------- /design-pattern/adapter-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 适配器模式(Adapter Pattern) 2 | 适配器模式是作为两个不同接口的一种聚合,把比如说SD卡适配器,无论使用TF或SD卡或者其它卡,对外输出都是USB接口。 3 | # 适配器模式的实例 4 | 首先我们有两个设备一个是Vlc播放器,一个是Mp4播放器,一个需要使用playVlc按钮来播放,一个要使用playMp4来播放。 5 | ```js 6 | class VlcPlayer { 7 | playVlc(fileName) { 8 | console.log("Playing vlc file. Name: "+ fileName); 9 | } 10 | } 11 | class Mp4Player { 12 | playMp4(fileName) { 13 | console.log("Playing mp4 file. Name: "+ fileName); 14 | } 15 | } 16 | ``` 17 | 但是我就想通过一个播放按钮来播放,我不管他是什么播放设备,这个时候,我们就需要一个适配器来做这个事情。 18 | ```js 19 | class MediaAdapter { 20 | constructor(audioType){ 21 | switch(audioType) { 22 | case 'vlc': 23 | MediaAdapter.advancedMusicPlayer = new VlcPlayer(); 24 | break; 25 | case 'mp4': 26 | MediaAdapter.advancedMusicPlayer = new Mp4Player(); 27 | break; 28 | } 29 | } 30 | play(audioType, fileName) { 31 | switch(audioType) { 32 | case 'vlc': 33 | MediaAdapter.advancedMusicPlayer.playVlc(fileName); 34 | break; 35 | case 'mp4': 36 | MediaAdapter.advancedMusicPlayer.playMp4(fileName); 37 | break; 38 | } 39 | } 40 | } 41 | ``` 42 | 通过适配器我们可以把各种设备桥接到一个音频设备上。 43 | ```js 44 | class AudioPlayer{ 45 | play(audioType, fileName) { 46 | switch(audioType) { 47 | case 'mp3': 48 | console.log("Playing mp3 file. Name: "+ fileName); 49 | break; 50 | case 'vlc': 51 | case 'mp4': 52 | AudioPlayer.mediaAdapter = new MediaAdapter(audioType); 53 | AudioPlayer.mediaAdapter.play(audioType, fileName); 54 | break; 55 | default: 56 | console.log("Invalid media. "+ 57 | audioType + " format not supported"); 58 | 59 | break; 60 | } 61 | } 62 | } 63 | ``` 64 | 那么这个时候我们就可以直接通过这个音频设备来播放我们想要播放的音频了 65 | ```js 66 | const audioPlayer = new AudioPlayer(); 67 | 68 | audioPlayer.play("mp3", "beyond the horizon.mp3"); 69 | audioPlayer.play("mp4", "alone.mp4"); 70 | audioPlayer.play("vlc", "far far away.vlc"); 71 | audioPlayer.play("avi", "mind me.avi"); 72 | /** 73 | * output: 74 | * Playing mp3 file. Name: beyond the horizon.mp3 75 | * Playing mp4 file. Name: alone.mp4 76 | * Playing vlc file. Name: far far away.vlc 77 | * Invalid media. avi format not supported 78 | */ 79 | ``` 80 | # 适配器模式的优势 81 | 可以让两个不同接口作为一个适配的接口使用,这样对下层的关心可以减少. 82 | 83 | [上一页(原型模式)](../prototype-pattern/README.md) 84 | 85 | [下一页(桥接模式)](../bridge-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/adapter-pattern/index.js: -------------------------------------------------------------------------------- 1 | class VlcPlayer { 2 | playVlc(fileName) { 3 | console.log("Playing vlc file. Name: "+ fileName); 4 | } 5 | } 6 | 7 | class Mp4Player { 8 | playMp4(fileName) { 9 | console.log("Playing mp4 file. Name: "+ fileName); 10 | } 11 | } 12 | 13 | class MediaAdapter { 14 | constructor(audioType){ 15 | switch(audioType) { 16 | case 'vlc': 17 | MediaAdapter.advancedMusicPlayer = new VlcPlayer(); 18 | break; 19 | case 'mp4': 20 | MediaAdapter.advancedMusicPlayer = new Mp4Player(); 21 | break; 22 | } 23 | } 24 | play(audioType, fileName) { 25 | switch(audioType) { 26 | case 'vlc': 27 | MediaAdapter.advancedMusicPlayer.playVlc(fileName); 28 | break; 29 | case 'mp4': 30 | MediaAdapter.advancedMusicPlayer.playMp4(fileName); 31 | break; 32 | } 33 | } 34 | } 35 | 36 | class AudioPlayer{ 37 | play(audioType, fileName) { 38 | switch(audioType) { 39 | case 'mp3': 40 | console.log("Playing mp3 file. Name: "+ fileName); 41 | break; 42 | case 'vlc': 43 | case 'mp4': 44 | AudioPlayer.mediaAdapter = new MediaAdapter(audioType); 45 | AudioPlayer.mediaAdapter.play(audioType, fileName); 46 | break; 47 | default: 48 | console.log("Invalid media. "+ 49 | audioType + " format not supported"); 50 | 51 | break; 52 | } 53 | } 54 | } 55 | 56 | const audioPlayer = new AudioPlayer(); 57 | 58 | audioPlayer.play("mp3", "beyond the horizon.mp3"); 59 | audioPlayer.play("mp4", "alone.mp4"); 60 | audioPlayer.play("vlc", "far far away.vlc"); 61 | audioPlayer.play("avi", "mind me.avi"); 62 | /** 63 | * output: 64 | * Playing mp3 file. Name: beyond the horizon.mp3 65 | * Playing mp4 file. Name: alone.mp4 66 | * Playing vlc file. Name: far far away.vlc 67 | * Invalid media. avi format not supported 68 | */ -------------------------------------------------------------------------------- /design-pattern/bridge-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 桥接模式(Bridge Pattern) 2 | 桥接模式的设计目的是不让下层的组件的变化,影响上层的调用。 3 | 4 | # 桥接模式的实例 5 | 假设我有两个类,但是它们有很多不确定性,可能在后续会变修改,如下: 6 | ```js 7 | class RedCircle { 8 | drawCircle(radius, x, y) { 9 | console.log("Drawing Circle[ color: red, radius: " 10 | + radius +", x: " +x+", "+ y +"]"); 11 | } 12 | } 13 | class GreenCircle { 14 | drawCircle(radius, x, y) { 15 | console.log("Drawing Circle[ color: green, radius: " 16 | + radius +", x: " +x+", "+ y +"]"); 17 | } 18 | } 19 | ``` 20 | 虽然它们不确定性,但是对外功能还是要相对稳定。所以我们要定义抽象层Shape,和实现层Circle,保持对外暴露的方法始终是draw。 21 | ```js 22 | class Shape { 23 | constructor(drawAPI) { 24 | if(new.target == Shape) { 25 | throw new Error('this class must be extends.') 26 | } 27 | this.drawAPI = drawAPI; 28 | } 29 | draw() {} 30 | } 31 | 32 | class Circle extends Shape { 33 | constructor(x, y, radius, drawAPI) { 34 | super(drawAPI); 35 | this.x = x; 36 | this.y = y; 37 | this.radius = radius; 38 | } 39 | draw() { 40 | this.drawAPI.drawCircle(this.radius, this.x, this.y); 41 | } 42 | } 43 | ``` 44 | 那么在我们使用的时候无论RedCircle和GreenCircle如何变化,但是对外都是使用draw方法来调用 45 | ```js 46 | const redCircle = new Circle(100,100, 10, new RedCircle()); 47 | const greenCircle = new Circle(100,100, 10, new GreenCircle()); 48 | 49 | redCircle.draw(); 50 | greenCircle.draw(); 51 | /** 52 | * output: 53 | * Drawing Circle[ color: red, radius: 10, x: 100, 100] 54 | * Drawing Circle[ color: green, radius: 10, x: 100, 100] 55 | */ 56 | ``` 57 | # 桥接模式的优势 58 | 即使基础组件发生变化,也不影响上层的调用。例子中RedCircle和GreenCircle作为了基础组件,假设方法drawCircle进行了更名或调用方法发生变更,但是在抽象层Shape依旧是draw,只能修改Circle的draw内容来修改,但是对外依然能保持draw方法的调用。 59 | 60 | [上一页(适配器模式)](../adapter-pattern/README.md) 61 | 62 | [下一页(过滤器模式)](../filter-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/bridge-pattern/index.js: -------------------------------------------------------------------------------- 1 | class RedCircle { 2 | drawCircle(radius, x, y) { 3 | console.log("Drawing Circle[ color: red, radius: " 4 | + radius +", x: " +x+", "+ y +"]"); 5 | } 6 | } 7 | class GreenCircle { 8 | drawCircle(radius, x, y) { 9 | console.log("Drawing Circle[ color: green, radius: " 10 | + radius +", x: " +x+", "+ y +"]"); 11 | } 12 | } 13 | 14 | class Shape { 15 | constructor(drawAPI) { 16 | if(new.target == Shape) { 17 | throw new Error('this class must be extends.') 18 | } 19 | this.drawAPI = drawAPI; 20 | } 21 | draw() {} 22 | } 23 | 24 | class Circle extends Shape { 25 | constructor(x, y, radius, drawAPI) { 26 | super(drawAPI); 27 | this.x = x; 28 | this.y = y; 29 | this.radius = radius; 30 | } 31 | draw() { 32 | this.drawAPI.drawCircle(this.radius, this.x, this.y); 33 | } 34 | } 35 | 36 | const redCircle = new Circle(100,100, 10, new RedCircle()); 37 | const greenCircle = new Circle(100,100, 10, new GreenCircle()); 38 | 39 | redCircle.draw(); 40 | greenCircle.draw(); 41 | /** 42 | * output: 43 | * Drawing Circle[ color: red, radius: 10, x: 100, 100] 44 | * Drawing Circle[ color: green, radius: 10, x: 100, 100] 45 | */ -------------------------------------------------------------------------------- /design-pattern/builder-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 建造者模式 2 | 让简单的对象通过组合的方式构造成多种复杂对象。 3 | # 建造者模式的实例 4 | 这里举例西式快餐,里面有非常多的套餐种类,但是各种套餐都是由不同种类的冷饮和汉堡组合而成。同时冷饮需要瓶子装,汉堡需要纸盒包住,那么我们可以先定义冷饮和汉堡类和它们所需要的瓶子和纸盒。 5 | ```js 6 | // 纸盒 7 | class Wrapper { 8 | pack() { 9 | return "Wrapper"; 10 | } 11 | } 12 | // 瓶子 13 | class Bottle { 14 | pack() { 15 | return "Bottle"; 16 | } 17 | } 18 | // 汉堡需要纸盒包住 19 | class Burger { 20 | packing() { 21 | return new Wrapper(); 22 | } 23 | } 24 | // 冷饮需要瓶子装 25 | class ColdDrink { 26 | packing() { 27 | return new Bottle(); 28 | } 29 | } 30 | ``` 31 | 那么我们肯定不止一种冷饮和一种汉堡,比如汉堡有蔬菜汉堡和肌肉汉堡,冷饮有可乐和百事。那么我们需要不同的类型和对应的价格。 32 | ```js 33 | // 蔬菜汉堡 34 | class VegBurger extends Burger { 35 | price() { 36 | return 25.0; 37 | } 38 | name() { 39 | return "Veg Burger"; 40 | } 41 | } 42 | // 肌肉汉堡 43 | class ChickenBurger extends Burger { 44 | price() { 45 | return 50.5; 46 | } 47 | name() { 48 | return "Chicken Burger"; 49 | } 50 | } 51 | // 可乐 52 | class Coke extends ColdDrink { 53 | price() { 54 | return 30.0; 55 | } 56 | name() { 57 | return "Coke"; 58 | } 59 | } 60 | // 百事 61 | class Pepsi extends ColdDrink { 62 | price() { 63 | return 35.0; 64 | } 65 | name() { 66 | return "Pepsi"; 67 | } 68 | } 69 | ``` 70 | 那一个套餐肯定是有多个不同冷饮和汉堡,那么我们需要用一个数组作为储存不同冷饮和汉堡的条目,以下套餐就很容易打造好了。 71 | ```js 72 | class Meal { 73 | constructor () { 74 | const items = []; 75 | /** 76 | * 为什么不用Proxy而使用defineProperty 77 | * 因为Proxy虽然实现和defineProperty类似的功能 78 | * 但是在这个场景下,语意上是定义属性,而不是需要代理 79 | */ 80 | Reflect.defineProperty(this, 'items', { 81 | get:()=>{ 82 | if(this.__proto__ != Meal.prototype) { 83 | throw new Error('items is private!'); 84 | } 85 | return items; 86 | } 87 | }) 88 | } 89 | addItem(item){ 90 | this.items.push(item); 91 | } 92 | 93 | getCost(){ 94 | let cost = 0.0; 95 | for (const item of this.items) { 96 | cost += item.price(); 97 | } 98 | return cost; 99 | } 100 | 101 | showItems(){ 102 | for (const item of this.items) { 103 | const nameStr = "Item : "+item.name(); 104 | const packStr = "Packing : "+item.packing().pack(); 105 | const priceStr = "Price : "+item.price(); 106 | console.log(`${nameStr},${packStr},${priceStr}`); 107 | } 108 | } 109 | } 110 | ``` 111 | 最后我们只要对外提供多个套餐就好了。这个叫它套餐建造者好了。 112 | ```js 113 | class MealBuilder { 114 | prepareVegMeal (){ 115 | const meal = new Meal(); 116 | meal.addItem(new VegBurger()); 117 | meal.addItem(new Coke()); 118 | return meal; 119 | } 120 | prepareNonVegMeal (){ 121 | const meal = new Meal(); 122 | meal.addItem(new ChickenBurger()); 123 | meal.addItem(new Pepsi()); 124 | return meal; 125 | } 126 | } 127 | ``` 128 | 最后我们只要用套餐建造者,给我们做出相应的套餐。 129 | ```js 130 | const mealBuilder = new MealBuilder(); 131 | const vegMeal = mealBuilder.prepareVegMeal(); 132 | console.log("Veg Meal"); 133 | vegMeal.showItems(); 134 | console.log("Total Cost: " +vegMeal.getCost()); 135 | 136 | const nonVegMeal = mealBuilder.prepareNonVegMeal(); 137 | console.log("\nNon-Veg Meal"); 138 | nonVegMeal.showItems(); 139 | console.log("Total Cost: " +nonVegMeal.getCost()); 140 | /** 141 | * output: 142 | * Veg Meal 143 | * Item : Veg Burger,Packing : Wrapper,Price : 25 144 | * Item : Coke,Packing : Bottle,Price : 30 145 | * Total Cost: 55 146 | * 147 | * Non-Veg Meal 148 | * Item : Chicken Burger,Packing : Wrapper,Price : 50.5 149 | * Item : Pepsi,Packing : Bottle,Price : 35 150 | * Total Cost: 85.5 151 | */ 152 | ``` 153 | 154 | # 建造者模式的优势 155 | 这是一种创建复杂对象的最佳实践。尤其是复杂对象多变的情况下,通过基础组件来组合,在基础组件变更时,多种依赖于基础组件的复杂组件也能方便变更,而不需要更改多种不同的复杂组件。 156 | 157 | [上一页(单例模式)](../singleton-pattern/README.md) 158 | 159 | [下一页(原型模式)](../prototype-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/builder-pattern/index.js: -------------------------------------------------------------------------------- 1 | // 纸盒 2 | class Wrapper { 3 | pack() { 4 | return "Wrapper"; 5 | } 6 | } 7 | // 瓶子 8 | class Bottle { 9 | pack() { 10 | return "Bottle"; 11 | } 12 | } 13 | // 汉堡需要纸盒包住 14 | class Burger { 15 | packing() { 16 | return new Wrapper(); 17 | } 18 | } 19 | // 冷饮需要瓶子装 20 | class ColdDrink { 21 | packing() { 22 | return new Bottle(); 23 | } 24 | } 25 | // 蔬菜汉堡 26 | class VegBurger extends Burger { 27 | price() { 28 | return 25.0; 29 | } 30 | name() { 31 | return "Veg Burger"; 32 | } 33 | } 34 | // 肌肉汉堡 35 | class ChickenBurger extends Burger { 36 | price() { 37 | return 50.5; 38 | } 39 | name() { 40 | return "Chicken Burger"; 41 | } 42 | } 43 | // 可乐 44 | class Coke extends ColdDrink { 45 | price() { 46 | return 30.0; 47 | } 48 | name() { 49 | return "Coke"; 50 | } 51 | } 52 | // 百事 53 | class Pepsi extends ColdDrink { 54 | price() { 55 | return 35.0; 56 | } 57 | name() { 58 | return "Pepsi"; 59 | } 60 | } 61 | 62 | class Meal { 63 | constructor () { 64 | const items = []; 65 | /** 66 | * 为什么不用Proxy而使用defineProperty 67 | * 因为Proxy虽然实现和defineProperty类似的功能 68 | * 但是在这个场景下,语意上是定义属性,而不是需要代理 69 | */ 70 | Reflect.defineProperty(this, 'items', { 71 | get:()=>{ 72 | if(this.__proto__ != Meal.prototype) { 73 | throw new Error('items is private!'); 74 | } 75 | return items; 76 | } 77 | }) 78 | 79 | } 80 | addItem(item){ 81 | this.items.push(item); 82 | } 83 | 84 | getCost(){ 85 | let cost = 0.0; 86 | for (const item of this.items) { 87 | cost += item.price(); 88 | } 89 | return cost; 90 | } 91 | 92 | showItems(){ 93 | for (const item of this.items) { 94 | const nameStr = "Item : "+item.name(); 95 | const packStr = "Packing : "+item.packing().pack(); 96 | const priceStr = "Price : "+item.price(); 97 | console.log(`${nameStr},${packStr},${priceStr}`); 98 | } 99 | } 100 | } 101 | class MealBuilder { 102 | prepareVegMeal (){ 103 | const meal = new Meal(); 104 | meal.addItem(new VegBurger()); 105 | meal.addItem(new Coke()); 106 | return meal; 107 | } 108 | prepareNonVegMeal (){ 109 | const meal = new Meal(); 110 | meal.addItem(new ChickenBurger()); 111 | meal.addItem(new Pepsi()); 112 | return meal; 113 | } 114 | } 115 | 116 | const mealBuilder = new MealBuilder(); 117 | const vegMeal = mealBuilder.prepareVegMeal(); 118 | console.log("Veg Meal"); 119 | vegMeal.showItems(); 120 | console.log("Total Cost: " +vegMeal.getCost()); 121 | 122 | const nonVegMeal = mealBuilder.prepareNonVegMeal(); 123 | console.log("\nNon-Veg Meal"); 124 | nonVegMeal.showItems(); 125 | console.log("Total Cost: " +nonVegMeal.getCost()); 126 | /** 127 | * output: 128 | * Veg Meal 129 | * Item : Veg Burger,Packing : Wrapper,Price : 25 130 | * Item : Coke,Packing : Bottle,Price : 30 131 | * Total Cost: 55 132 | * 133 | * Non-Veg Meal 134 | * Item : Chicken Burger,Packing : Wrapper,Price : 50.5 135 | * Item : Pepsi,Packing : Bottle,Price : 35 136 | * Total Cost: 85.5 137 | */ -------------------------------------------------------------------------------- /design-pattern/business-delegate-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 业务代表模式(Business Delegate Pattern) 2 | 业务代码模式其实也是一种分层模型,分为业务服务层,服务查询层,业务代表层,客户端层。 3 | 4 | # 业务代表模式的实例 5 | 假设我们有以下业务服务层。 6 | ```js 7 | class EJBService { 8 | doProcessing() { 9 | console.log("Processing task by invoking EJB Service"); 10 | } 11 | } 12 | class JMSService { 13 | doProcessing() { 14 | console.log("Processing task by invoking JMS Service"); 15 | } 16 | } 17 | ``` 18 | 服务查询层能对服务进行查找。 19 | ```js 20 | class BusinessLookUp { 21 | getBusinessService(serviceType){ 22 | switch(serviceType.toUpperCase()) { 23 | case 'EJB': 24 | return new EJBService(); 25 | default: 26 | return new JMSService(); 27 | } 28 | } 29 | } 30 | ``` 31 | 定义业务代表 32 | ```js 33 | class BusinessDelegate { 34 | constructor() { 35 | this.lookupService = new BusinessLookUp(); 36 | } 37 | setServiceType(serviceType){ 38 | this.serviceType = serviceType; 39 | } 40 | doTask(){ 41 | this.businessService = this.lookupService.getBusinessService(this.serviceType); 42 | this.businessService.doProcessing(); 43 | } 44 | } 45 | ``` 46 | 定义客户端 47 | ```js 48 | class Client { 49 | constructor(businessService){ 50 | this.businessService = businessService; 51 | } 52 | 53 | doTask(){ 54 | this.businessService.doTask(); 55 | } 56 | } 57 | ``` 58 | 使用业务代表和客户端交互 59 | ```js 60 | const businessDelegate = new BusinessDelegate(); 61 | businessDelegate.setServiceType("EJB"); 62 | 63 | const client = new Client(businessDelegate); 64 | client.doTask(); 65 | 66 | businessDelegate.setServiceType("JMS"); 67 | client.doTask(); 68 | ``` 69 | # 业务代表模式的优势 70 | 统一了业务出口,客户端不需要关心底层的模型,只需要关系业务代表暴露的模型。 -------------------------------------------------------------------------------- /design-pattern/business-delegate-pattern/index.js: -------------------------------------------------------------------------------- 1 | class EJBService { 2 | doProcessing() { 3 | console.log("Processing task by invoking EJB Service"); 4 | } 5 | } 6 | class JMSService { 7 | doProcessing() { 8 | console.log("Processing task by invoking JMS Service"); 9 | } 10 | } 11 | 12 | class BusinessLookUp { 13 | getBusinessService(serviceType){ 14 | switch(serviceType.toUpperCase()) { 15 | case 'EJB': 16 | return new EJBService(); 17 | default: 18 | return new JMSService(); 19 | } 20 | } 21 | } 22 | 23 | class BusinessDelegate { 24 | constructor() { 25 | this.lookupService = new BusinessLookUp(); 26 | } 27 | setServiceType(serviceType){ 28 | this.serviceType = serviceType; 29 | } 30 | doTask(){ 31 | this.businessService = this.lookupService.getBusinessService(this.serviceType); 32 | this.businessService.doProcessing(); 33 | } 34 | } 35 | 36 | class Client { 37 | constructor(businessService){ 38 | this.businessService = businessService; 39 | } 40 | 41 | doTask(){ 42 | this.businessService.doTask(); 43 | } 44 | } 45 | 46 | const businessDelegate = new BusinessDelegate(); 47 | businessDelegate.setServiceType("EJB"); 48 | 49 | const client = new Client(businessDelegate); 50 | client.doTask(); 51 | 52 | businessDelegate.setServiceType("JMS"); 53 | client.doTask(); -------------------------------------------------------------------------------- /design-pattern/chain-of-responsibility-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 责任链模式(Chain of Responsibility Pattern) 2 | 这是一种链式结构的一种设计模式,比如如果当前类有义务要做某项事情的时候就必须要去完成 3 | 4 | # 责任链模式的实例 5 | 假设我们的日志系统有三个报错级别,分别是ERROR,DEBUG,INFO。现在我们在做日志系统的时候有一个需求,就是当出现ERROR时要同时打印到ERROR,DEBUG,INFO的控制台上,而出现DEBUG要同时打印到DEBUG,INFO的控制台上,INFO只需要打印到INFO的控制台上。 6 | 7 | 那么我们可以用一种链式设计来完成这种事情,首先我们抽象化日志的类型。 8 | ```js 9 | class AbstractLogger { 10 | constructor() { 11 | if(new.target == AbstractLogger) { 12 | throw new Error('this class must be extends.') 13 | } 14 | } 15 | setNextLogger(nextLogger){ 16 | this.nextLogger = nextLogger; 17 | } 18 | write(message) {} 19 | // 对于形成链的数据,依次执行 20 | logMessage(level, message){ 21 | if(this.level <= level){ 22 | this.write(message); 23 | } 24 | if(this.nextLogger !=null){ 25 | this.nextLogger.logMessage(level, message); 26 | } 27 | } 28 | } 29 | AbstractLogger.INFO = 1; 30 | AbstractLogger.DEBUG = 2; 31 | AbstractLogger.ERROR = 3; 32 | ``` 33 | 然后我们实现不同错误级别日志的实体类 34 | ```js 35 | //定义错误类型 36 | class StandardLogger extends AbstractLogger { 37 | constructor(level){ 38 | super() 39 | this.level = level; 40 | } 41 | write(message) { 42 | console.log("Standard Console::Logger: " + message); 43 | } 44 | } 45 | class ErrorLogger extends AbstractLogger { 46 | constructor(level){ 47 | super() 48 | this.level = level; 49 | } 50 | write(message) { 51 | console.log("Error Console::Logger: " + message); 52 | } 53 | } 54 | class FileLogger extends AbstractLogger { 55 | constructor(level){ 56 | super() 57 | this.level = level; 58 | } 59 | write(message) { 60 | console.log("File Console::Logger: " + message); 61 | } 62 | } 63 | ``` 64 | 然后我们再把它链式化 65 | ```js 66 | function getChainOfLoggers(){ 67 | const errorLogger = new ErrorLogger(AbstractLogger.ERROR); 68 | const fileLogger = new FileLogger(AbstractLogger.DEBUG); 69 | const standardLogger = new StandardLogger(AbstractLogger.INFO); 70 | 71 | errorLogger.setNextLogger(fileLogger); 72 | fileLogger.setNextLogger(standardLogger); 73 | 74 | return errorLogger; 75 | } 76 | ``` 77 | 那么我们在使用的时候就只需要使用链式化的对象即可 78 | ```js 79 | // 实现级别高的报错依次从当前级别输出到低级别 80 | loggerChain.logMessage(AbstractLogger.INFO, "This is an information."); 81 | console.log('') 82 | loggerChain.logMessage(AbstractLogger.DEBUG, 83 | "This is a debug level information."); 84 | console.log('') 85 | loggerChain.logMessage(AbstractLogger.ERROR, 86 | "This is an error information."); 87 | /** 88 | * output: 89 | * Standard Console::Logger: This is an information. 90 | * 91 | * File Console::Logger: This is a debug level information. 92 | * Standard Console::Logger: This is a debug level information. 93 | * 94 | * Error Console::Logger: This is an error information. 95 | * File Console::Logger: This is an error information. 96 | * Standard Console::Logger: This is an error information. 97 | */ 98 | ``` 99 | 100 | # 责任链模式的优势 101 | 当前责任人只需要关心自己应该尽什么责任,而不是要关心,自己完成责任后而发什么的其它的什么责任。避免了发送者和多个接受者耦合在一起。 102 | 103 | 104 | [上一页(代理模式)](../proxy-pattern/README.md) 105 | 106 | [下一页(命令模式)](../command-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/chain-of-responsibility-pattern/index.js: -------------------------------------------------------------------------------- 1 | class AbstractLogger { 2 | constructor() { 3 | if(new.target == AbstractLogger) { 4 | throw new Error('this class must be extends.') 5 | } 6 | } 7 | setNextLogger(nextLogger){ 8 | this.nextLogger = nextLogger; 9 | } 10 | write(message) {} 11 | // 对于形成链的数据,依次执行 12 | logMessage(level, message){ 13 | if(this.level <= level){ 14 | this.write(message); 15 | } 16 | if(this.nextLogger !=null){ 17 | this.nextLogger.logMessage(level, message); 18 | } 19 | } 20 | } 21 | AbstractLogger.INFO = 1; 22 | AbstractLogger.DEBUG = 2; 23 | AbstractLogger.ERROR = 3; 24 | //定义错误类型 25 | class StandardLogger extends AbstractLogger { 26 | constructor(level){ 27 | super() 28 | this.level = level; 29 | } 30 | write(message) { 31 | console.log("Standard Console::Logger: " + message); 32 | } 33 | } 34 | class ErrorLogger extends AbstractLogger { 35 | constructor(level){ 36 | super() 37 | this.level = level; 38 | } 39 | write(message) { 40 | console.log("Error Console::Logger: " + message); 41 | } 42 | } 43 | class FileLogger extends AbstractLogger { 44 | constructor(level){ 45 | super() 46 | this.level = level; 47 | } 48 | write(message) { 49 | console.log("File Console::Logger: " + message); 50 | } 51 | } 52 | 53 | function getChainOfLoggers(){ 54 | const errorLogger = new ErrorLogger(AbstractLogger.ERROR); 55 | const fileLogger = new FileLogger(AbstractLogger.DEBUG); 56 | const standardLogger = new StandardLogger(AbstractLogger.INFO); 57 | 58 | errorLogger.setNextLogger(fileLogger); 59 | fileLogger.setNextLogger(standardLogger); 60 | 61 | return errorLogger; 62 | } 63 | 64 | const loggerChain = getChainOfLoggers(); 65 | 66 | // 实现级别高的报错依次从当前级别输出到低级别 67 | loggerChain.logMessage(AbstractLogger.INFO, "This is an information."); 68 | console.log('') 69 | loggerChain.logMessage(AbstractLogger.DEBUG, 70 | "This is a debug level information."); 71 | console.log('') 72 | loggerChain.logMessage(AbstractLogger.ERROR, 73 | "This is an error information."); 74 | /** 75 | * output: 76 | * Standard Console::Logger: This is an information. 77 | * 78 | * File Console::Logger: This is a debug level information. 79 | * Standard Console::Logger: This is a debug level information. 80 | * 81 | * Error Console::Logger: This is an error information. 82 | * File Console::Logger: This is an error information. 83 | * Standard Console::Logger: This is an error information. 84 | */ -------------------------------------------------------------------------------- /design-pattern/command-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 命令模式(Command Pattern) 2 | 顾名思义,这是一种接受命令再统一执行的模式。 3 | # 命令模式的实例 4 | 比如在股票的买卖中可以买和卖,但是这是一个执行就实施的操作,不利于挂单。 5 | ```js 6 | class Stock { 7 | constructor() { 8 | this.name = "ABC"; 9 | this.quantity = 10; 10 | } 11 | buy(){ 12 | console.log("Stock [ Name: " + this.name + 13 | ",Quantity: " + this.quantity +" ] bought"); 14 | } 15 | sell(){ 16 | console.log("Stock [ Name: " + this.name + 17 | ",Quantity: " + this.quantity +" ] sold"); 18 | } 19 | } 20 | ``` 21 | 那么我们可以先定义单一行为,比如买或卖,同时只有使用了execute才真正执行。 22 | ```js 23 | class BuyStock { 24 | constructor(abcStock){ 25 | this.abcStock = abcStock; 26 | } 27 | execute() { 28 | this.abcStock.buy(); 29 | } 30 | } 31 | class SellStock{ 32 | constructor(abcStock){ 33 | this.abcStock = abcStock; 34 | } 35 | execute() { 36 | this.abcStock.sell(); 37 | } 38 | } 39 | ``` 40 | 那么我们可以使用一个列表orderList来接受挂单了,使用takeOrder提交挂单,同时到达交易时间,我们再使用placeOrders来进行交易处理。 41 | ```js 42 | class Broker { 43 | constructor() { 44 | this.orderList = []; 45 | } 46 | 47 | takeOrder(order){ 48 | this.orderList.push(order); 49 | } 50 | 51 | placeOrders(){ 52 | for (const order of this.orderList) { 53 | order.execute(); 54 | } 55 | this.orderList = []; 56 | } 57 | } 58 | ``` 59 | 那么我们在使用的时候就更加直观和更加参数化了。 60 | ```js 61 | const abcStock = new Stock(); 62 | 63 | const buyStockOrder = new BuyStock(abcStock); 64 | const sellStockOrder = new SellStock(abcStock); 65 | 66 | const broker = new Broker(); 67 | broker.takeOrder(buyStockOrder); 68 | broker.takeOrder(buyStockOrder); 69 | broker.takeOrder(buyStockOrder); 70 | broker.takeOrder(sellStockOrder); 71 | broker.takeOrder(sellStockOrder); 72 | 73 | broker.placeOrders(); 74 | /** 75 | * output: 76 | * Stock [ Name: ABC,Quantity: 10 ] bought 77 | * Stock [ Name: ABC,Quantity: 10 ] bought 78 | * Stock [ Name: ABC,Quantity: 10 ] bought 79 | * Stock [ Name: ABC,Quantity: 10 ] sold 80 | * Stock [ Name: ABC,Quantity: 10 ] sold 81 | */ 82 | ``` 83 | 84 | # 命令模式优势 85 | 由于命令模式是最终执行,所以中途可以发出很多不同的命令,能解决一些比如预约的问题。就比如上面的例子,股票可以提前下单,但是未必能最终生效。同时可以让代码展现更加直观和参数化。 86 | 87 | [上一页(责任链模式)](../chain-of-responsibility-pattern/README.md) 88 | 89 | [下一页(解释器模式)](../interpreter-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/command-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Stock { 2 | constructor() { 3 | this.name = "ABC"; 4 | this.quantity = 10; 5 | } 6 | buy(){ 7 | console.log("Stock [ Name: " + this.name + 8 | ",Quantity: " + this.quantity +" ] bought"); 9 | } 10 | sell(){ 11 | console.log("Stock [ Name: " + this.name + 12 | ",Quantity: " + this.quantity +" ] sold"); 13 | } 14 | } 15 | 16 | class BuyStock { 17 | constructor(abcStock){ 18 | this.abcStock = abcStock; 19 | } 20 | execute() { 21 | this.abcStock.buy(); 22 | } 23 | } 24 | class SellStock{ 25 | constructor(abcStock){ 26 | this.abcStock = abcStock; 27 | } 28 | execute() { 29 | this.abcStock.sell(); 30 | } 31 | } 32 | 33 | class Broker { 34 | constructor() { 35 | this.orderList = []; 36 | } 37 | 38 | takeOrder(order){ 39 | this.orderList.push(order); 40 | } 41 | 42 | placeOrders(){ 43 | for (const order of this.orderList) { 44 | order.execute(); 45 | } 46 | this.orderList = []; 47 | } 48 | } 49 | 50 | const abcStock = new Stock(); 51 | 52 | const buyStockOrder = new BuyStock(abcStock); 53 | const sellStockOrder = new SellStock(abcStock); 54 | 55 | const broker = new Broker(); 56 | broker.takeOrder(buyStockOrder); 57 | broker.takeOrder(buyStockOrder); 58 | broker.takeOrder(buyStockOrder); 59 | broker.takeOrder(sellStockOrder); 60 | broker.takeOrder(sellStockOrder); 61 | 62 | broker.placeOrders(); 63 | /** 64 | * output: 65 | * Stock [ Name: ABC,Quantity: 10 ] bought 66 | * Stock [ Name: ABC,Quantity: 10 ] bought 67 | * Stock [ Name: ABC,Quantity: 10 ] bought 68 | * Stock [ Name: ABC,Quantity: 10 ] sold 69 | * Stock [ Name: ABC,Quantity: 10 ] sold 70 | */ -------------------------------------------------------------------------------- /design-pattern/composite-entity-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 组合实体模式(Composite Entity Pattern) 2 | 即将两个不同的实体组合成一个对象实体使用,每个组合的实体都是有对应的策略。 3 | 4 | # 组合实体模式的实例 5 | 组合之前的实体 6 | ```js 7 | class DependentObject1 { 8 | setData(data){ 9 | this.data = data; 10 | } 11 | getData(){ 12 | return this.data; 13 | } 14 | } 15 | 16 | class DependentObject2 { 17 | setData(data) { 18 | this.data = data; 19 | } 20 | getData() { 21 | return this.data; 22 | } 23 | } 24 | ``` 25 | 粗颗粒度组合 26 | ```js 27 | class CoarseGrainedObject { 28 | constructor() { 29 | this.do1 = new DependentObject1(); 30 | this.do2 = new DependentObject2(); 31 | } 32 | 33 | setData(data1, data2){ 34 | this.do1.setData(data1); 35 | this.do2.setData(data2); 36 | } 37 | getData(){ 38 | return [this.do1.getData(),this.do2.getData()]; 39 | } 40 | } 41 | ``` 42 | 组合实体对象 43 | ```js 44 | class CompositeEntity { 45 | constructor() { 46 | this.cgo = new CoarseGrainedObject(); 47 | } 48 | setData(data1, data2){ 49 | this.cgo.setData(data1, data2); 50 | } 51 | getData(){ 52 | return this.cgo.getData(); 53 | } 54 | } 55 | ``` 56 | 对外交互的客户端 57 | ```js 58 | class Client { 59 | constructor() { 60 | this.compositeEntity = new CompositeEntity(); 61 | } 62 | printData(){ 63 | for (let i = 0; i < this.compositeEntity.getData().length; i++) { 64 | console.log("Data: " + this.compositeEntity.getData()[i]); 65 | } 66 | } 67 | setData(data1, data2){ 68 | this.compositeEntity.setData(data1, data2); 69 | } 70 | } 71 | ``` 72 | 客户端使用组合实体 73 | ```js 74 | const client = new Client(); 75 | client.setData("Test", "Data"); 76 | client.printData(); 77 | client.setData("Second Test", "Data1"); 78 | client.printData(); 79 | /** 80 | * output: 81 | * Data: Test 82 | * Data: Data 83 | * Data: Second Test 84 | * Data: Data1 85 | */ 86 | ``` 87 | # 组合实体模式的优势 88 | 对外只需要关心组合实体后暴露的功能,而不需要关心组合之前的实体。 89 | -------------------------------------------------------------------------------- /design-pattern/composite-entity-pattern/index.js: -------------------------------------------------------------------------------- 1 | class DependentObject1 { 2 | setData(data){ 3 | this.data = data; 4 | } 5 | getData(){ 6 | return this.data; 7 | } 8 | } 9 | 10 | class DependentObject2 { 11 | setData(data) { 12 | this.data = data; 13 | } 14 | getData() { 15 | return this.data; 16 | } 17 | } 18 | 19 | class CoarseGrainedObject { 20 | constructor() { 21 | this.do1 = new DependentObject1(); 22 | this.do2 = new DependentObject2(); 23 | } 24 | 25 | setData(data1, data2){ 26 | this.do1.setData(data1); 27 | this.do2.setData(data2); 28 | } 29 | getData(){ 30 | return [this.do1.getData(),this.do2.getData()]; 31 | } 32 | } 33 | 34 | class CompositeEntity { 35 | constructor() { 36 | this.cgo = new CoarseGrainedObject(); 37 | } 38 | setData(data1, data2){ 39 | this.cgo.setData(data1, data2); 40 | } 41 | getData(){ 42 | return this.cgo.getData(); 43 | } 44 | } 45 | 46 | class Client { 47 | constructor() { 48 | this.compositeEntity = new CompositeEntity(); 49 | } 50 | printData(){ 51 | for (let i = 0; i < this.compositeEntity.getData().length; i++) { 52 | console.log("Data: " + this.compositeEntity.getData()[i]); 53 | } 54 | } 55 | setData(data1, data2){ 56 | this.compositeEntity.setData(data1, data2); 57 | } 58 | } 59 | 60 | const client = new Client(); 61 | client.setData("Test", "Data"); 62 | client.printData(); 63 | client.setData("Second Test", "Data1"); 64 | client.printData(); 65 | /** 66 | * output: 67 | * Data: Test 68 | * Data: Data 69 | * Data: Second Test 70 | * Data: Data1 71 | */ -------------------------------------------------------------------------------- /design-pattern/composite-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 组合模式(Composite Pattern) 2 | 以结构化的方式,是单一对象具有树形结构,让单一对象更具有结构性。 3 | 4 | # 组合模式的实例 5 | 在雇员和雇员之间都是存在上下级关系,如何用代码更直观的表达和关系与关系的操作,这是一个棘手的问题。 6 | 7 | 但通过组合模式,将关系表达为树状结构将更方便更直观的表达,如下。 8 | ```js 9 | class Employee { 10 | constructor(name, dept, sal) { 11 | this.name = name; 12 | this.dept = dept; 13 | this.salary = sal; 14 | this.subordinates = []; 15 | } 16 | add(employee) { 17 | this.subordinates.push(employee); 18 | } 19 | remove(employee) { 20 | this.subordinates.splice(this.subordinates.indexOf(employee),1); 21 | } 22 | getSubordinates(){ 23 | return this.subordinates; 24 | } 25 | toString(){ 26 | return ("Employee :[ Name : "+ this.name 27 | +", dept : "+ this.dept + ", salary :" 28 | + this.salary+" ]"); 29 | } 30 | } 31 | ``` 32 | 所以当我们添加雇员和关系的时候将更加方便的添加,而且对于一个雇员,它只需要关心和下属的关系维护,而不需要关心上级和一些间接的关系。这样会更加清晰和维护更加方便。 33 | ```js 34 | const CEO = new Employee("John","CEO", 30000); 35 | 36 | const headSales = new Employee("Robert","Head Sales", 20000); 37 | 38 | const headMarketing = new Employee("Michel","Head Marketing", 20000); 39 | 40 | const clerk1 = new Employee("Laura","Marketing", 10000); 41 | const clerk2 = new Employee("Bob","Marketing", 10000); 42 | 43 | const salesExecutive1 = new Employee("Richard","Sales", 10000); 44 | const salesExecutive2 = new Employee("Rob","Sales", 10000); 45 | 46 | CEO.add(headSales); 47 | CEO.add(headMarketing); 48 | 49 | headSales.add(salesExecutive1); 50 | headSales.add(salesExecutive2); 51 | 52 | headMarketing.add(clerk1); 53 | headMarketing.add(clerk2); 54 | ``` 55 | 那么我们打印来说也只要打印最高级别的上级就能实现全部打印。 56 | ```js 57 | function printAllEmployee(employee) { 58 | for (const subEmployee of employee.getSubordinates()) { 59 | console.log(subEmployee.toString()); 60 | if(subEmployee.getSubordinates().length>0) { 61 | printAllEmployee(subEmployee) 62 | } 63 | } 64 | } 65 | //打印该组织的所有员工 66 | console.log(CEO.toString()); 67 | printAllEmployee(CEO) 68 | /** 69 | * output: 70 | * Employee :[ Name : John, dept : CEO, salary :30000 ] 71 | * Employee :[ Name : Robert, dept : Head Sales, salary :20000 ] 72 | * Employee :[ Name : Richard, dept : Sales, salary :10000 ] 73 | * Employee :[ Name : Rob, dept : Sales, salary :10000 ] 74 | * Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ] 75 | * Employee :[ Name : Laura, dept : Marketing, salary :10000 ] 76 | * Employee :[ Name : Bob, dept : Marketing, salary :10000 ] 77 | */ 78 | ``` 79 | # 组合模式的优势 80 | 让相互关联的对象产生了结构性,无论是在关系修改或者是关系直观性上,都只需要关心当前下级的关系,那么这样能更好的降低关系和关系之间的复杂度,加强单对象关系结构的可维护性。 81 | 82 | [上一页(过滤器模式)](../filter-pattern/README.md) 83 | 84 | [下一页(装饰器模式)](../decorator-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/composite-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Employee { 2 | constructor(name, dept, sal) { 3 | this.name = name; 4 | this.dept = dept; 5 | this.salary = sal; 6 | this.subordinates = []; 7 | } 8 | add(employee) { 9 | this.subordinates.push(employee); 10 | } 11 | remove(employee) { 12 | this.subordinates.splice(this.subordinates.indexOf(employee),1); 13 | } 14 | getSubordinates(){ 15 | return this.subordinates; 16 | } 17 | toString(){ 18 | return ("Employee :[ Name : "+ this.name 19 | +", dept : "+ this.dept + ", salary :" 20 | + this.salary+" ]"); 21 | } 22 | } 23 | 24 | const CEO = new Employee("John","CEO", 30000); 25 | 26 | const headSales = new Employee("Robert","Head Sales", 20000); 27 | 28 | const headMarketing = new Employee("Michel","Head Marketing", 20000); 29 | 30 | const clerk1 = new Employee("Laura","Marketing", 10000); 31 | const clerk2 = new Employee("Bob","Marketing", 10000); 32 | 33 | const salesExecutive1 = new Employee("Richard","Sales", 10000); 34 | const salesExecutive2 = new Employee("Rob","Sales", 10000); 35 | 36 | CEO.add(headSales); 37 | CEO.add(headMarketing); 38 | 39 | headSales.add(salesExecutive1); 40 | headSales.add(salesExecutive2); 41 | 42 | headMarketing.add(clerk1); 43 | headMarketing.add(clerk2); 44 | 45 | function printAllEmployee(employee) { 46 | for (const subEmployee of employee.getSubordinates()) { 47 | console.log(subEmployee.toString()); 48 | if(subEmployee.getSubordinates().length>0) { 49 | printAllEmployee(subEmployee) 50 | } 51 | } 52 | } 53 | //打印该组织的所有员工 54 | console.log(CEO.toString()); 55 | printAllEmployee(CEO) 56 | /** 57 | * output: 58 | * Employee :[ Name : John, dept : CEO, salary :30000 ] 59 | * Employee :[ Name : Robert, dept : Head Sales, salary :20000 ] 60 | * Employee :[ Name : Richard, dept : Sales, salary :10000 ] 61 | * Employee :[ Name : Rob, dept : Sales, salary :10000 ] 62 | * Employee :[ Name : Michel, dept : Head Marketing, salary :20000 ] 63 | * Employee :[ Name : Laura, dept : Marketing, salary :10000 ] 64 | * Employee :[ Name : Bob, dept : Marketing, salary :10000 ] 65 | */ -------------------------------------------------------------------------------- /design-pattern/data-access-object-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 数据访问对象模式(Data Access Object Pattern) 2 | 即不直接通过数据对象,而是抽象成Dao层来做这个事情。 3 | 4 | # 数据访问对象模式的实例 5 | 定义数据模型 6 | ```js 7 | class Student { 8 | constructor(name, rollNo){ 9 | this.name = name; 10 | this.rollNo = rollNo; 11 | } 12 | 13 | getName() { 14 | return this.name; 15 | } 16 | 17 | setName(name) { 18 | this.name = name; 19 | } 20 | 21 | getRollNo() { 22 | return this.rollNo; 23 | } 24 | 25 | setRollNo(rollNo) { 26 | this.rollNo = rollNo; 27 | } 28 | } 29 | ``` 30 | Dao层的实现 31 | ```js 32 | class StudentDao{ 33 | constructor(){ 34 | this.students = []; 35 | this.students.getIndexByRollNo = (rollNo)=>{ 36 | return this.students.findIndex( 37 | (val)=>val.getRollNo() == rollNo 38 | ); 39 | } 40 | const student1 = new Student("Robert",0); 41 | const student2 = new Student("John",1); 42 | this.students.push(student1); 43 | this.students.push(student2); 44 | } 45 | deleteStudent(student) { 46 | this.students.splice(student.getIndexByRollNo(student.getRollNo() ),1); 47 | console.log("Student: Roll No " + student.getRollNo() 48 | +", deleted from database"); 49 | } 50 | //从数据库中检索学生名单 51 | getAllStudents() { 52 | return this.students; 53 | } 54 | getStudent(rollNo) { 55 | return this.students[this.students.getIndexByRollNo(rollNo)]; 56 | } 57 | 58 | updateStudent(student) { 59 | this.students[this.students.getIndexByRollNo(student.getRollNo())].setName(student.getName()); 60 | console.log("Student: Roll No " + student.getRollNo() 61 | +", updated in the database"); 62 | } 63 | } 64 | ``` 65 | 使用Dao操作数据 66 | ```js 67 | const studentDao = new StudentDao(); 68 | 69 | //输出所有的学生 70 | for (let student of studentDao.getAllStudents()) { 71 | console.log("Student: [RollNo : " 72 | +student.getRollNo()+", Name : "+student.getName()+" ]"); 73 | } 74 | 75 | 76 | //更新学生 77 | const student =studentDao.getAllStudents()[studentDao.getAllStudents().getIndexByRollNo(0)]; 78 | student.setName("Michael"); 79 | studentDao.updateStudent(student); 80 | 81 | //获取学生 82 | studentDao.getStudent(0); 83 | console.log("Student: [RollNo : " 84 | +student.getRollNo()+", Name : "+student.getName()+" ]"); 85 | ``` 86 | 87 | # 数据访问对象模式的优势 88 | 数据层和操作层分离,结构清晰,层级稳定。这看起来像是一种失血模型。 -------------------------------------------------------------------------------- /design-pattern/data-access-object-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Student { 2 | constructor(name, rollNo){ 3 | this.name = name; 4 | this.rollNo = rollNo; 5 | } 6 | 7 | getName() { 8 | return this.name; 9 | } 10 | 11 | setName(name) { 12 | this.name = name; 13 | } 14 | 15 | getRollNo() { 16 | return this.rollNo; 17 | } 18 | 19 | setRollNo(rollNo) { 20 | this.rollNo = rollNo; 21 | } 22 | } 23 | 24 | class StudentDao{ 25 | constructor(){ 26 | this.students = []; 27 | this.students.getIndexByRollNo = (rollNo)=>{ 28 | return this.students.findIndex( 29 | (val)=>val.getRollNo() == rollNo 30 | ); 31 | } 32 | const student1 = new Student("Robert",0); 33 | const student2 = new Student("John",1); 34 | this.students.push(student1); 35 | this.students.push(student2); 36 | } 37 | deleteStudent(student) { 38 | this.students.splice(student.getIndexByRollNo(student.getRollNo() ),1); 39 | console.log("Student: Roll No " + student.getRollNo() 40 | +", deleted from database"); 41 | } 42 | //从数据库中检索学生名单 43 | getAllStudents() { 44 | return this.students; 45 | } 46 | getStudent(rollNo) { 47 | return this.students[this.students.getIndexByRollNo(rollNo)]; 48 | } 49 | 50 | updateStudent(student) { 51 | this.students[this.students.getIndexByRollNo(student.getRollNo())].setName(student.getName()); 52 | console.log("Student: Roll No " + student.getRollNo() 53 | +", updated in the database"); 54 | } 55 | } 56 | 57 | const studentDao = new StudentDao(); 58 | 59 | //输出所有的学生 60 | for (let student of studentDao.getAllStudents()) { 61 | console.log("Student: [RollNo : " 62 | +student.getRollNo()+", Name : "+student.getName()+" ]"); 63 | } 64 | 65 | 66 | //更新学生 67 | const student =studentDao.getAllStudents()[studentDao.getAllStudents().getIndexByRollNo(0)]; 68 | student.setName("Michael"); 69 | studentDao.updateStudent(student); 70 | 71 | //获取学生 72 | studentDao.getStudent(0); 73 | console.log("Student: [RollNo : " 74 | +student.getRollNo()+", Name : "+student.getName()+" ]"); -------------------------------------------------------------------------------- /design-pattern/decorator-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 装饰器模式(Decorator Pattern) 2 | 装饰器模式实现了不改变原有对象,在原有对象上实现功能的添加。这是一种对原有对象的一种包装。 3 | # 装饰器模式的实例 4 | 假设现在有两个形状,一个矩形一个圆形,这时候我们希望能在形状上实现一些特殊的功能,但又不改变原来的类,我们要如何做呢? 5 | ```js 6 | class Rectangle { 7 | draw() { 8 | console.log("Shape: Rectangle"); 9 | } 10 | } 11 | 12 | class Circle { 13 | draw() { 14 | console.log("Shape: Circle"); 15 | } 16 | } 17 | ``` 18 | 这时我们可以用装饰器来实现,假设我们要给形状添加颜色功能 19 | ```js 20 | class RedShapeDecorator { 21 | constructor(decoratedShape) { 22 | this.decoratedShape = decoratedShape; 23 | } 24 | draw() { 25 | this.decoratedShape.draw(); 26 | this.setRedBorder(); 27 | } 28 | setRedBorder(){ 29 | console.log("Border Color: Red"); 30 | } 31 | } 32 | ``` 33 | 那么在使用装饰器的类,在画圆的时候就实现了了画边框的颜色。 34 | ```js 35 | const circle = new Circle(); 36 | const redCircle = new RedShapeDecorator(new Circle()); 37 | const redRectangle = new RedShapeDecorator(new Rectangle()); 38 | 39 | console.log("Circle with normal border"); 40 | circle.draw(); 41 | 42 | console.log("\nCircle of red border"); 43 | redCircle.draw(); 44 | 45 | console.log("\nRectangle of red border"); 46 | redRectangle.draw(); 47 | /** 48 | * output: 49 | * Circle with normal border 50 | * Shape: Circle 51 | * 52 | * Circle of red border 53 | * Shape: Circle 54 | * Border Color: Red 55 | * 56 | * Rectangle of red border 57 | * Shape: Rectangle 58 | * Border Color: Red 59 | */ 60 | ``` 61 | # 装饰器模式的优势 62 | 即使原有对象发生改变,装饰器是种非侵入式功能添加,对原有对象的影响也能降低到最小。同时在JS中更方便的装饰器的实例也在提案中([https://github.com/tc39/proposal-decorators](https://github.com/tc39/proposal-decorators)),本文写于2019年11月。 63 | 64 | 65 | [上一页(组合模式)](../composite-pattern/README.md) 66 | 67 | [下一页(外观模式)](../facade-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/decorator-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Rectangle { 2 | draw() { 3 | console.log("Shape: Rectangle"); 4 | } 5 | } 6 | 7 | class Circle { 8 | draw() { 9 | console.log("Shape: Circle"); 10 | } 11 | } 12 | 13 | class RedShapeDecorator { 14 | constructor(decoratedShape) { 15 | this.decoratedShape = decoratedShape; 16 | } 17 | draw() { 18 | this.decoratedShape.draw(); 19 | this.setRedBorder(); 20 | } 21 | setRedBorder(){ 22 | console.log("Border Color: Red"); 23 | } 24 | } 25 | 26 | 27 | const circle = new Circle(); 28 | const redCircle = new RedShapeDecorator(new Circle()); 29 | const redRectangle = new RedShapeDecorator(new Rectangle()); 30 | 31 | console.log("Circle with normal border"); 32 | circle.draw(); 33 | 34 | console.log("\nCircle of red border"); 35 | redCircle.draw(); 36 | 37 | console.log("\nRectangle of red border"); 38 | redRectangle.draw(); 39 | /** 40 | * output: 41 | * Circle with normal border 42 | * Shape: Circle 43 | * 44 | * Circle of red border 45 | * Shape: Circle 46 | * Border Color: Red 47 | * 48 | * Rectangle of red border 49 | * Shape: Rectangle 50 | * Border Color: Red 51 | */ 52 | /** 53 | * 目前 @ 作为更加方便实现装饰器功能正在提案中 54 | * https://github.com/tc39/proposal-decorators 55 | */ -------------------------------------------------------------------------------- /design-pattern/facade-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 外观模式(Facade Pattern) 2 | 对外提供单一接口,来隐藏系统的复杂性 3 | # 外观模式的实例 4 | 比如目前有几种形状类型,假设这几个形状设计的特别复杂,这个时候你肯定不愿意修改这些接口然后来提供新的接口 5 | ```js 6 | class Rectangle { 7 | draw() { 8 | console.log("Rectangle::draw()"); 9 | } 10 | } 11 | 12 | class Square { 13 | draw() { 14 | console.log("Square::draw()"); 15 | } 16 | } 17 | 18 | class Circle { 19 | draw() { 20 | console.log("Circle::draw()"); 21 | } 22 | } 23 | ``` 24 | 那么可以用外观模式来隐藏这些复杂的接口调用,对外暴露更加单一的接口调用方式 25 | ```js 26 | class ShapeMaker { 27 | constructor() { 28 | this.circle = new Circle(); 29 | this.rectangle = new Rectangle(); 30 | this.square = new Square(); 31 | } 32 | 33 | drawCircle(){ 34 | this.circle.draw(); 35 | } 36 | drawRectangle(){ 37 | this.rectangle.draw(); 38 | } 39 | drawSquare(){ 40 | this.square.draw(); 41 | } 42 | } 43 | ``` 44 | 那么我们使用的时候就更不需要考虑里面复杂的接口,而直接使用外观模式暴露的接口就好了 45 | ```js 46 | const shapeMaker = new ShapeMaker(); 47 | 48 | shapeMaker.drawCircle(); 49 | shapeMaker.drawRectangle(); 50 | shapeMaker.drawSquare(); 51 | /** 52 | * output: 53 | * Circle::draw() 54 | * Rectangle::draw() 55 | * Square::draw() 56 | */ 57 | ``` 58 | # 外观模式的优势 59 | 隐藏内部的复杂性,这样能减少一些对内部修改的可能,同时对外暴露单一功能接口,也有利于降低复杂性。 60 | 61 | [上一页(装饰器模式)](../decorator-pattern/README.md) 62 | 63 | [下一页(享元模式)](../flyweight-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/facade-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Rectangle { 2 | draw() { 3 | console.log("Rectangle::draw()"); 4 | } 5 | } 6 | 7 | class Square { 8 | draw() { 9 | console.log("Square::draw()"); 10 | } 11 | } 12 | 13 | class Circle { 14 | draw() { 15 | console.log("Circle::draw()"); 16 | } 17 | } 18 | 19 | class ShapeMaker { 20 | constructor() { 21 | this.circle = new Circle(); 22 | this.rectangle = new Rectangle(); 23 | this.square = new Square(); 24 | } 25 | 26 | drawCircle(){ 27 | this.circle.draw(); 28 | } 29 | drawRectangle(){ 30 | this.rectangle.draw(); 31 | } 32 | drawSquare(){ 33 | this.square.draw(); 34 | } 35 | } 36 | 37 | const shapeMaker = new ShapeMaker(); 38 | 39 | shapeMaker.drawCircle(); 40 | shapeMaker.drawRectangle(); 41 | shapeMaker.drawSquare(); 42 | /** 43 | * output: 44 | * Circle::draw() 45 | * Rectangle::draw() 46 | * Square::draw() 47 | */ -------------------------------------------------------------------------------- /design-pattern/factory-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 工厂模式(Factory pattern) 2 | 工厂模式是比较常用的设计模式之一,那么什么叫工厂模式呢?简单来说,就是你需要什么东西不直接使用new的方法生成实例,然后统一通过工厂进行生产加工再生成实例。 3 | 4 | # 工厂模式的实例 5 | 比如我们现在有很多形状比如圆形,矩形和正方形。这类都是属于形状,那我们是不是可以通过专门生产形状的工厂来生成它们的实例么? 6 | ```js 7 | class Circle { 8 | draw() { 9 | console.log("I'm a circle") 10 | } 11 | } 12 | class Rectangle { 13 | draw() { 14 | console.log("I'm a rectangle") 15 | } 16 | } 17 | class Square { 18 | draw() { 19 | console.log("I'm a square") 20 | } 21 | } 22 | ``` 23 | 那么接下来,我们可以建立一个专门生产形状的工厂来生产它们了。即根据字符串来产生对应需要的类。你在这里可以看到类的出口都已经在一个方法中了。 24 | ```js 25 | class ShapeFactory { 26 | getShape(shapeType){ 27 | switch(shapeType) { 28 | case 'CIRCLE': 29 | return new Circle(); 30 | case 'RECTANGLE': 31 | return new Rectangle(); 32 | case 'SQUARE': 33 | return new Square(); 34 | default: 35 | return null; 36 | } 37 | } 38 | } 39 | ``` 40 | 那么我们需要使用的时候,就可以直接只需要new出一个工厂,在根据字符串就能拿到对应的需要生产的类了。而不需要分别对类进行new。 41 | ```js 42 | const shapeFactory = new ShapeFactory(); 43 | // 通过工厂拿各种形状 44 | const shape1 = shapeFactory.getShape('CIRCLE'); 45 | shape1.draw(); 46 | const shape2 = shapeFactory.getShape('RECTANGLE'); 47 | shape2.draw(); 48 | const shape3 = shapeFactory.getShape('SQUARE'); 49 | shape3.draw(); 50 | /** 51 | * output: 52 | * I'm a circle 53 | * I'm a rectangle 54 | * I'm a square 55 | */ 56 | ``` 57 | # 工厂模式的优势 58 | 那么使用工厂模式的好处也是显而易见的,比如实例的生产比较复杂,或者说生成实例后还需要额外加工,这个时候工厂给了我们一个统一的出入口,也方便了日后对这个实例的修改。比如你要修改工厂产出是一个单例的时候,就不需要在所有的类中修改,而只要在工厂出口修改即可达到目标。 59 | 60 | [下一页(抽象工厂模式)](../abstract-factory-pattern/README.md) 61 | -------------------------------------------------------------------------------- /design-pattern/factory-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Circle { 2 | draw() { 3 | console.log("I'm a circle") 4 | } 5 | } 6 | class Rectangle { 7 | draw() { 8 | console.log("I'm a rectangle") 9 | } 10 | } 11 | class Square { 12 | draw() { 13 | console.log("I'm a square") 14 | } 15 | } 16 | class ShapeFactory { 17 | getShape(shapeType){ 18 | switch(shapeType) { 19 | case 'CIRCLE': 20 | return new Circle(); 21 | case 'RECTANGLE': 22 | return new Rectangle(); 23 | case 'SQUARE': 24 | return new Square(); 25 | default: 26 | return null; 27 | } 28 | } 29 | } 30 | const shapeFactory = new ShapeFactory(); 31 | // 通过工厂拿各种形状 32 | const shape1 = shapeFactory.getShape('CIRCLE'); 33 | shape1.draw(); 34 | const shape2 = shapeFactory.getShape('RECTANGLE'); 35 | shape2.draw(); 36 | const shape3 = shapeFactory.getShape('SQUARE'); 37 | shape3.draw(); 38 | /** 39 | * output: 40 | * I'm a circle 41 | * I'm a rectangle 42 | * I'm a square 43 | */ -------------------------------------------------------------------------------- /design-pattern/filter-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 过滤器模式(Filter Pattern) 2 | 通过多个单一的功能筛选构建出一个复杂的筛选功能。 3 | 4 | # 过滤器模式的实例 5 | 首先定义一个对象,我们后续可以通过名字(name),性别(gender),婚姻状况(maritalStatus) 6 | ```js 7 | // 定义对象 8 | class Person { 9 | constructor(name, gender, maritalStatus){ 10 | this.name = name; 11 | this.gender = gender; 12 | this.maritalStatus = maritalStatus; 13 | } 14 | getName() { 15 | return this.name; 16 | } 17 | getGender() { 18 | return this.gender; 19 | } 20 | getMaritalStatus() { 21 | return this.maritalStatus; 22 | } 23 | } 24 | ``` 25 | 定义一些单一功能的筛选条件,比如啊判断是男,是女,是不是单身。 26 | ```js 27 | // 添加筛选条件 28 | class CriteriaMale { 29 | meetCriteria(persons) { 30 | const malePersons = []; 31 | for (const person of persons) { 32 | if(person.getGender().toUpperCase() == "MALE"){ 33 | malePersons.push(person); 34 | } 35 | } 36 | return malePersons; 37 | } 38 | } 39 | 40 | class CriteriaFemale { 41 | meetCriteria(persons) { 42 | const femalePersons = []; 43 | for (const person of persons) { 44 | if(person.getGender().toUpperCase() == "FEMALE"){ 45 | femalePersons.push(person); 46 | } 47 | } 48 | return femalePersons; 49 | } 50 | } 51 | 52 | class CriteriaSingle { 53 | meetCriteria(persons) { 54 | const singlePersons = []; 55 | for (const person of persons) { 56 | if(person.getMaritalStatus().toUpperCase() == "SINGLE"){ 57 | singlePersons.push(person); 58 | } 59 | } 60 | return singlePersons; 61 | } 62 | } 63 | ``` 64 | 将单一功能增加对应的操作符,使单一功能筛选条件能通过组合来实现复杂的筛选。 65 | ```js 66 | // 添加筛选操作符 67 | class AndCriteria { 68 | constructor(criteria, otherCriteria) { 69 | this.criteria = criteria; 70 | this.otherCriteria = otherCriteria; 71 | } 72 | meetCriteria(persons) { 73 | const firstCriteriaPersons = this.criteria.meetCriteria(persons); 74 | return this.otherCriteria.meetCriteria(firstCriteriaPersons); 75 | } 76 | } 77 | 78 | class OrCriteria{ 79 | constructor(criteria, otherCriteria) { 80 | this.criteria = criteria; 81 | this.otherCriteria = otherCriteria; 82 | } 83 | 84 | meetCriteria(persons) { 85 | const firstCriteriaItems = this.criteria.meetCriteria(persons); 86 | const otherCriteriaItems = this.otherCriteria.meetCriteria(persons); 87 | for (const person of otherCriteriaItems) { 88 | if(firstCriteriaItems.indexOf(person)==-1){ 89 | firstCriteriaItems.push(person); 90 | } 91 | } 92 | return firstCriteriaItems; 93 | } 94 | } 95 | ``` 96 | 使用单一筛选条件或是组合单一筛选条件来筛选,达到复杂筛选目的 97 | ```js 98 | function printPersons(persons){ 99 | for (const person of persons) { 100 | console.log(person); 101 | } 102 | } 103 | 104 | const persons = []; 105 | persons.push(new Person("Robert","Male", "Single")); 106 | persons.push(new Person("John","Male", "Married")); 107 | persons.push(new Person("Laura","Female", "Married")); 108 | persons.push(new Person("Diana","Female", "Single")); 109 | persons.push(new Person("Mike","Male", "Single")); 110 | persons.push(new Person("Bobby","Male", "Single")); 111 | 112 | const male = new CriteriaMale(); 113 | const female = new CriteriaFemale(); 114 | const single = new CriteriaSingle(); 115 | const singleMale = new AndCriteria(single, male); 116 | const singleOrFemale = new OrCriteria(single, female); 117 | 118 | console.log("Males: "); 119 | printPersons(male.meetCriteria(persons)); 120 | 121 | console.log("\nFemales: "); 122 | printPersons(female.meetCriteria(persons)); 123 | 124 | console.log("\nSingle Males: "); 125 | printPersons(singleMale.meetCriteria(persons)); 126 | 127 | console.log("\nSingle Or Females: "); 128 | printPersons(singleOrFemale.meetCriteria(persons)); 129 | /** 130 | * output: 131 | * Males: 132 | * Person { name: 'Robert', gender: 'Male', maritalStatus: 'Single' } 133 | * Person { name: 'John', gender: 'Male', maritalStatus: 'Married' } 134 | * Person { name: 'Mike', gender: 'Male', maritalStatus: 'Single' } 135 | * Person { name: 'Bobby', gender: 'Male', maritalStatus: 'Single' } 136 | * 137 | * Females: 138 | * Person { name: 'Laura', gender: 'Female', maritalStatus: 'Married' } 139 | * Person { name: 'Diana', gender: 'Female', maritalStatus: 'Single' } 140 | * 141 | * Single Males: 142 | * Person { name: 'Robert', gender: 'Male', maritalStatus: 'Single' } 143 | * Person { name: 'Mike', gender: 'Male', maritalStatus: 'Single' } 144 | * Person { name: 'Bobby', gender: 'Male', maritalStatus: 'Single' } 145 | * 146 | * Single Or Females: 147 | * Person { name: 'Robert', gender: 'Male', maritalStatus: 'Single' } 148 | * Person { name: 'Diana', gender: 'Female', maritalStatus: 'Single' } 149 | * Person { name: 'Mike', gender: 'Male', maritalStatus: 'Single' } 150 | * Person { name: 'Bobby', gender: 'Male', maritalStatus: 'Single' } 151 | * Person { name: 'Laura', gender: 'Female', maritalStatus: 'Married' } 152 | */ 153 | ``` 154 | # 过滤器模式优势 155 | 在需要做类的筛选的时候,通过每次单一功能的筛选,再做聚合能极大的降低筛选功能的复杂性。 156 | 157 | [上一页(桥接模式)](../bridge-pattern/README.md) 158 | 159 | [下一页(组合模式)](../composite-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/filter-pattern/index.js: -------------------------------------------------------------------------------- 1 | // 定义对象 2 | class Person { 3 | constructor(name, gender, maritalStatus){ 4 | this.name = name; 5 | this.gender = gender; 6 | this.maritalStatus = maritalStatus; 7 | } 8 | getName() { 9 | return this.name; 10 | } 11 | getGender() { 12 | return this.gender; 13 | } 14 | getMaritalStatus() { 15 | return this.maritalStatus; 16 | } 17 | } 18 | 19 | // 添加筛选条件 20 | class CriteriaMale { 21 | meetCriteria(persons) { 22 | const malePersons = []; 23 | for (const person of persons) { 24 | if(person.getGender().toUpperCase() == "MALE"){ 25 | malePersons.push(person); 26 | } 27 | } 28 | return malePersons; 29 | } 30 | } 31 | 32 | class CriteriaFemale { 33 | meetCriteria(persons) { 34 | const femalePersons = []; 35 | for (const person of persons) { 36 | if(person.getGender().toUpperCase() == "FEMALE"){ 37 | femalePersons.push(person); 38 | } 39 | } 40 | return femalePersons; 41 | } 42 | } 43 | 44 | class CriteriaSingle { 45 | meetCriteria(persons) { 46 | const singlePersons = []; 47 | for (const person of persons) { 48 | if(person.getMaritalStatus().toUpperCase() == "SINGLE"){ 49 | singlePersons.push(person); 50 | } 51 | } 52 | return singlePersons; 53 | } 54 | } 55 | 56 | // 添加筛选操作符 57 | class AndCriteria { 58 | constructor(criteria, otherCriteria) { 59 | this.criteria = criteria; 60 | this.otherCriteria = otherCriteria; 61 | } 62 | meetCriteria(persons) { 63 | const firstCriteriaPersons = this.criteria.meetCriteria(persons); 64 | return this.otherCriteria.meetCriteria(firstCriteriaPersons); 65 | } 66 | } 67 | 68 | class OrCriteria{ 69 | constructor(criteria, otherCriteria) { 70 | this.criteria = criteria; 71 | this.otherCriteria = otherCriteria; 72 | } 73 | 74 | meetCriteria(persons) { 75 | const firstCriteriaItems = this.criteria.meetCriteria(persons); 76 | const otherCriteriaItems = this.otherCriteria.meetCriteria(persons); 77 | for (const person of otherCriteriaItems) { 78 | if(firstCriteriaItems.indexOf(person)==-1){ 79 | firstCriteriaItems.push(person); 80 | } 81 | } 82 | return firstCriteriaItems; 83 | } 84 | } 85 | 86 | function printPersons(persons){ 87 | for (const person of persons) { 88 | console.log(person); 89 | } 90 | } 91 | 92 | const persons = []; 93 | persons.push(new Person("Robert","Male", "Single")); 94 | persons.push(new Person("John","Male", "Married")); 95 | persons.push(new Person("Laura","Female", "Married")); 96 | persons.push(new Person("Diana","Female", "Single")); 97 | persons.push(new Person("Mike","Male", "Single")); 98 | persons.push(new Person("Bobby","Male", "Single")); 99 | 100 | const male = new CriteriaMale(); 101 | const female = new CriteriaFemale(); 102 | const single = new CriteriaSingle(); 103 | const singleMale = new AndCriteria(single, male); 104 | const singleOrFemale = new OrCriteria(single, female); 105 | 106 | console.log("Males: "); 107 | printPersons(male.meetCriteria(persons)); 108 | 109 | console.log("\nFemales: "); 110 | printPersons(female.meetCriteria(persons)); 111 | 112 | console.log("\nSingle Males: "); 113 | printPersons(singleMale.meetCriteria(persons)); 114 | 115 | console.log("\nSingle Or Females: "); 116 | printPersons(singleOrFemale.meetCriteria(persons)); 117 | /** 118 | * output: 119 | * Males: 120 | * Person { name: 'Robert', gender: 'Male', maritalStatus: 'Single' } 121 | * Person { name: 'John', gender: 'Male', maritalStatus: 'Married' } 122 | * Person { name: 'Mike', gender: 'Male', maritalStatus: 'Single' } 123 | * Person { name: 'Bobby', gender: 'Male', maritalStatus: 'Single' } 124 | * 125 | * Females: 126 | * Person { name: 'Laura', gender: 'Female', maritalStatus: 'Married' } 127 | * Person { name: 'Diana', gender: 'Female', maritalStatus: 'Single' } 128 | * 129 | * Single Males: 130 | * Person { name: 'Robert', gender: 'Male', maritalStatus: 'Single' } 131 | * Person { name: 'Mike', gender: 'Male', maritalStatus: 'Single' } 132 | * Person { name: 'Bobby', gender: 'Male', maritalStatus: 'Single' } 133 | * 134 | * Single Or Females: 135 | * Person { name: 'Robert', gender: 'Male', maritalStatus: 'Single' } 136 | * Person { name: 'Diana', gender: 'Female', maritalStatus: 'Single' } 137 | * Person { name: 'Mike', gender: 'Male', maritalStatus: 'Single' } 138 | * Person { name: 'Bobby', gender: 'Male', maritalStatus: 'Single' } 139 | * Person { name: 'Laura', gender: 'Female', maritalStatus: 'Married' } 140 | */ -------------------------------------------------------------------------------- /design-pattern/flyweight-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 享元模式(Flyweight Pattern) 2 | 利用Map或其它方式减少重复创建相同类型的实例对象。 3 | 4 | # 享元模式的实例 5 | 现在有一个圆,但是我们需要不同颜色的圆来使用。 6 | ```js 7 | class Circle { 8 | constructor(color){ 9 | this.color = color; 10 | } 11 | setX(x) { 12 | this.x = x; 13 | } 14 | setY(y) { 15 | this.y = y; 16 | } 17 | setRadius(radius) { 18 | this.radius = radius; 19 | } 20 | draw() { 21 | console.log("Circle: Draw() [Color : " + this.color 22 | +", x : " + this.x +", y :" + this.y +", radius :" + this.radius); 23 | } 24 | } 25 | ``` 26 | 那么我们可以使用一个形状工厂,在生产的时候使用Map将相同的颜色缓存起来,需要再用的时候还可以再使用。 27 | ```js 28 | class ShapeFactory { 29 | static getCircle(color) { 30 | let circle = ShapeFactory.circleMap.get(color); 31 | if(circle == null) { 32 | circle = new Circle(color); 33 | ShapeFactory.circleMap.set(color, circle); 34 | console.log("Creating circle of color : " + color); 35 | } 36 | return circle; 37 | } 38 | } 39 | ShapeFactory.circleMap = new Map(); 40 | ``` 41 | 那么即使用创建很多不同种类的圆,但是真正创建的实例只有圆种类的数量。 42 | ```js 43 | const colors =["Red", "Green", "Blue", "White", "Black" ]; 44 | for(let i=0; i < 20; ++i) { 45 | const circle = ShapeFactory.getCircle( 46 | colors[Math.floor(Math.random()*colors.length)] 47 | ); 48 | circle.setX(Math.floor(Math.random()*100)); 49 | circle.setY(Math.floor(Math.random()*100)); 50 | circle.setRadius(100); 51 | circle.draw(); 52 | } 53 | /** 54 | * output: 55 | * Creating circle of color : Red 56 | * Circle: Draw() [Color : Red, x : 44, y :20, radius :100 57 | * Creating circle of color : Green 58 | * Circle: Draw() [Color : Green, x : 84, y :4, radius :100 59 | * Circle: Draw() [Color : Green, x : 98, y :64, radius :100 60 | * Creating circle of color : Blue 61 | * Circle: Draw() [Color : Blue, x : 97, y :31, radius :100 62 | * Circle: Draw() [Color : Red, x : 37, y :5, radius :100 63 | * Creating circle of color : Black 64 | * Circle: Draw() [Color : Black, x : 5, y :51, radius :100 65 | * Circle: Draw() [Color : Black, x : 49, y :36, radius :100 66 | * Circle: Draw() [Color : Blue, x : 27, y :69, radius :100 67 | * Circle: Draw() [Color : Red, x : 82, y :99, radius :100 68 | * Circle: Draw() [Color : Blue, x : 79, y :1, radius :100 69 | * Creating circle of color : White 70 | * Circle: Draw() [Color : White, x : 19, y :23, radius :100 71 | * Circle: Draw() [Color : White, x : 27, y :36, radius :100 72 | * Circle: Draw() [Color : Blue, x : 71, y :90, radius :100 73 | * Circle: Draw() [Color : Green, x : 80, y :66, radius :100 74 | * Circle: Draw() [Color : Black, x : 94, y :49, radius :100 75 | * Circle: Draw() [Color : Red, x : 49, y :90, radius :100 76 | * Circle: Draw() [Color : Black, x : 33, y :86, radius :100 77 | * Circle: Draw() [Color : Blue, x : 52, y :97, radius :100 78 | * Circle: Draw() [Color : White, x : 0, y :42, radius :100 79 | * Circle: Draw() [Color : Blue, x : 29, y :42, radius :100 80 | */ 81 | ``` 82 | # 享元模式的优势 83 | 在需要大量重复相同实例的时候,可以使用这种方式来降低极大的内存开销。 84 | 85 | 86 | [上一页(外观模式)](../facade-pattern/README.md) 87 | 88 | [下一页(代理模式)](../proxy-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/flyweight-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Circle { 2 | constructor(color){ 3 | this.color = color; 4 | } 5 | setX(x) { 6 | this.x = x; 7 | } 8 | setY(y) { 9 | this.y = y; 10 | } 11 | setRadius(radius) { 12 | this.radius = radius; 13 | } 14 | draw() { 15 | console.log("Circle: Draw() [Color : " + this.color 16 | +", x : " + this.x +", y :" + this.y +", radius :" + this.radius); 17 | } 18 | } 19 | 20 | class ShapeFactory { 21 | static getCircle(color) { 22 | let circle = ShapeFactory.circleMap.get(color); 23 | if(circle == null) { 24 | circle = new Circle(color); 25 | ShapeFactory.circleMap.set(color, circle); 26 | console.log("Creating circle of color : " + color); 27 | } 28 | return circle; 29 | } 30 | } 31 | ShapeFactory.circleMap = new Map(); 32 | 33 | 34 | const colors =["Red", "Green", "Blue", "White", "Black" ]; 35 | for(let i=0; i < 20; ++i) { 36 | const circle = ShapeFactory.getCircle( 37 | colors[Math.floor(Math.random()*colors.length)] 38 | ); 39 | circle.setX(Math.floor(Math.random()*100)); 40 | circle.setY(Math.floor(Math.random()*100)); 41 | circle.setRadius(100); 42 | circle.draw(); 43 | } 44 | /** 45 | * output: 46 | * Creating circle of color : Red 47 | * Circle: Draw() [Color : Red, x : 44, y :20, radius :100 48 | * Creating circle of color : Green 49 | * Circle: Draw() [Color : Green, x : 84, y :4, radius :100 50 | * Circle: Draw() [Color : Green, x : 98, y :64, radius :100 51 | * Creating circle of color : Blue 52 | * Circle: Draw() [Color : Blue, x : 97, y :31, radius :100 53 | * Circle: Draw() [Color : Red, x : 37, y :5, radius :100 54 | * Creating circle of color : Black 55 | * Circle: Draw() [Color : Black, x : 5, y :51, radius :100 56 | * Circle: Draw() [Color : Black, x : 49, y :36, radius :100 57 | * Circle: Draw() [Color : Blue, x : 27, y :69, radius :100 58 | * Circle: Draw() [Color : Red, x : 82, y :99, radius :100 59 | * Circle: Draw() [Color : Blue, x : 79, y :1, radius :100 60 | * Creating circle of color : White 61 | * Circle: Draw() [Color : White, x : 19, y :23, radius :100 62 | * Circle: Draw() [Color : White, x : 27, y :36, radius :100 63 | * Circle: Draw() [Color : Blue, x : 71, y :90, radius :100 64 | * Circle: Draw() [Color : Green, x : 80, y :66, radius :100 65 | * Circle: Draw() [Color : Black, x : 94, y :49, radius :100 66 | * Circle: Draw() [Color : Red, x : 49, y :90, radius :100 67 | * Circle: Draw() [Color : Black, x : 33, y :86, radius :100 68 | * Circle: Draw() [Color : Blue, x : 52, y :97, radius :100 69 | * Circle: Draw() [Color : White, x : 0, y :42, radius :100 70 | * Circle: Draw() [Color : Blue, x : 29, y :42, radius :100 71 | */ -------------------------------------------------------------------------------- /design-pattern/front-controller-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 前端控制器模式(Front Controller Pattern) 2 | 即为所有的请求提供一个统一处理的方法,像现在网关层就是一个前端控制器模式组件实例,但今天主要讲代码实例。 3 | 4 | # 前端控制器模式的实例 5 | 定义两个需要展示的页面 6 | ```js 7 | class HomeView { 8 | show(){ 9 | console.log("Displaying Home Page"); 10 | } 11 | } 12 | class StudentView { 13 | show(){ 14 | console.log("Displaying Student Page"); 15 | } 16 | } 17 | ``` 18 | 定义展示器 19 | ```js 20 | class Dispatcher { 21 | constructor() { 22 | this.studentView = new StudentView(); 23 | this.homeView = new HomeView(); 24 | } 25 | dispatch(request) { 26 | if(request.toUpperCase()=="STUDENT"){ 27 | this.studentView.show(); 28 | }else{ 29 | this.homeView.show(); 30 | } 31 | } 32 | } 33 | ``` 34 | 定义前端控制器 35 | ```js 36 | class FrontController { 37 | 38 | constructor(){ 39 | this.dispatcher = new Dispatcher(); 40 | } 41 | 42 | isAuthenticUser(){ 43 | console.log("User is authenticated successfully."); 44 | return true; 45 | } 46 | 47 | trackRequest(request){ 48 | console.log("Page requested: " + request); 49 | } 50 | 51 | dispatchRequest(request){ 52 | //记录每一个请求 53 | this.trackRequest(request); 54 | //对用户进行身份验证 55 | if(this.isAuthenticUser()){ 56 | this.dispatcher.dispatch(request); 57 | } 58 | } 59 | } 60 | ``` 61 | 通过前端请求,就能对所有访问的接口,进行记录和鉴权。 62 | ```js 63 | 64 | const frontController = new FrontController(); 65 | frontController.dispatchRequest("HOME"); 66 | frontController.dispatchRequest("STUDENT"); 67 | ``` 68 | # 前端控制器模式 69 | 能够提供统一处理接口请求的常用的方案。 -------------------------------------------------------------------------------- /design-pattern/front-controller-pattern/index.js: -------------------------------------------------------------------------------- 1 | class HomeView { 2 | show(){ 3 | console.log("Displaying Home Page"); 4 | } 5 | } 6 | class StudentView { 7 | show(){ 8 | console.log("Displaying Student Page"); 9 | } 10 | } 11 | 12 | class Dispatcher { 13 | constructor() { 14 | this.studentView = new StudentView(); 15 | this.homeView = new HomeView(); 16 | } 17 | dispatch(request) { 18 | if(request.toUpperCase()=="STUDENT"){ 19 | this.studentView.show(); 20 | }else{ 21 | this.homeView.show(); 22 | } 23 | } 24 | } 25 | 26 | class FrontController { 27 | 28 | constructor(){ 29 | this.dispatcher = new Dispatcher(); 30 | } 31 | 32 | isAuthenticUser(){ 33 | console.log("User is authenticated successfully."); 34 | return true; 35 | } 36 | 37 | trackRequest(request){ 38 | console.log("Page requested: " + request); 39 | } 40 | 41 | dispatchRequest(request){ 42 | //记录每一个请求 43 | this.trackRequest(request); 44 | //对用户进行身份验证 45 | if(this.isAuthenticUser()){ 46 | this.dispatcher.dispatch(request); 47 | } 48 | } 49 | } 50 | 51 | const frontController = new FrontController(); 52 | frontController.dispatchRequest("HOME"); 53 | frontController.dispatchRequest("STUDENT"); -------------------------------------------------------------------------------- /design-pattern/intercepting-filter-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 拦截过滤器模式(Intercepting Filter Pattern) 2 | 顾名思义在执行目标操作之前的一些检查和拦截。 3 | 4 | # 拦截过滤器模式的实例 5 | 首先实现两个拦截器,一个用于鉴权,一个用于记录日志。 6 | ```js 7 | class AuthenticationFilter { 8 | execute(request){ 9 | console.log("Authenticating request: " + request); 10 | } 11 | } 12 | 13 | class DebugFilter { 14 | execute(request){ 15 | console.log("request log: " + request); 16 | } 17 | } 18 | ``` 19 | 定义我们目标执行方法。 20 | ```js 21 | class Target { 22 | execute(request){ 23 | console.log("Executing request: " + request); 24 | } 25 | } 26 | ``` 27 | 定义拦截器的链 28 | ```js 29 | class FilterChain { 30 | constructor() { 31 | this.filters = []; 32 | } 33 | 34 | addFilter(filter){ 35 | this.filters.push(filter); 36 | } 37 | 38 | execute(request){ 39 | for (let filter of this.filters) { 40 | filter.execute(request); 41 | } 42 | this.target.execute(request); 43 | } 44 | 45 | setTarget(target){ 46 | this.target = target; 47 | } 48 | } 49 | ``` 50 | 添加拦截器的管理器 51 | ```js 52 | class FilterManager { 53 | constructor(target){ 54 | this.filterChain = new FilterChain(); 55 | this.filterChain.setTarget(target); 56 | } 57 | setFilter(filter){ 58 | this.filterChain.addFilter(filter); 59 | } 60 | 61 | filterRequest(request){ 62 | this.filterChain.execute(request); 63 | } 64 | } 65 | ``` 66 | 定义请求的客户端 67 | ```js 68 | class Client { 69 | setFilterManager(filterManager){ 70 | this.filterManager = filterManager; 71 | } 72 | 73 | sendRequest(request){ 74 | this.filterManager.filterRequest(request); 75 | } 76 | } 77 | ``` 78 | 通过给拦截器管理增加目标函数和过滤器后使用客户端请求 79 | ```js 80 | const filterManager = new FilterManager(new Target()); 81 | filterManager.setFilter(new AuthenticationFilter()); 82 | filterManager.setFilter(new DebugFilter()); 83 | 84 | const client = new Client(); 85 | client.setFilterManager(filterManager); 86 | client.sendRequest("HOME"); 87 | ``` 88 | 89 | # 拦截过滤器模式的优势 90 | 首先要完成一个拦截器只需要关心需要实现的接口,只需要关心和其它拦截器的先后关系,这也算是对拦截器和拦截器之间的解耦。其次在执行目标函数的拦截器也不需要关心目标操作的结果和关系。 -------------------------------------------------------------------------------- /design-pattern/intercepting-filter-pattern/index.js: -------------------------------------------------------------------------------- 1 | class AuthenticationFilter { 2 | execute(request){ 3 | console.log("Authenticating request: " + request); 4 | } 5 | } 6 | 7 | class DebugFilter { 8 | execute(request){ 9 | console.log("request log: " + request); 10 | } 11 | } 12 | 13 | class Target { 14 | execute(request){ 15 | console.log("Executing request: " + request); 16 | } 17 | } 18 | 19 | class FilterChain { 20 | constructor() { 21 | this.filters = []; 22 | } 23 | 24 | addFilter(filter){ 25 | this.filters.push(filter); 26 | } 27 | 28 | execute(request){ 29 | for (let filter of this.filters) { 30 | filter.execute(request); 31 | } 32 | this.target.execute(request); 33 | } 34 | 35 | setTarget(target){ 36 | this.target = target; 37 | } 38 | } 39 | 40 | class FilterManager { 41 | constructor(target){ 42 | this.filterChain = new FilterChain(); 43 | this.filterChain.setTarget(target); 44 | } 45 | setFilter(filter){ 46 | this.filterChain.addFilter(filter); 47 | } 48 | 49 | filterRequest(request){ 50 | this.filterChain.execute(request); 51 | } 52 | } 53 | 54 | class Client { 55 | setFilterManager(filterManager){ 56 | this.filterManager = filterManager; 57 | } 58 | 59 | sendRequest(request){ 60 | this.filterManager.filterRequest(request); 61 | } 62 | } 63 | 64 | const filterManager = new FilterManager(new Target()); 65 | filterManager.setFilter(new AuthenticationFilter()); 66 | filterManager.setFilter(new DebugFilter()); 67 | 68 | const client = new Client(); 69 | client.setFilterManager(filterManager); 70 | client.sendRequest("HOME"); -------------------------------------------------------------------------------- /design-pattern/interpreter-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 解释器模式(Interpreter Pattern) 2 | 解释器模式提供了对语言或语法的的一些判断方式。 3 | # 解释器模式的实例 4 | 定义对于语法的断言 5 | ```js 6 | class TerminalExpression { 7 | constructor(data){ 8 | this.data = data; 9 | } 10 | interpret(context) { 11 | if(context.indexOf(this.data)>-1){ 12 | return true; 13 | } 14 | return false; 15 | } 16 | } 17 | ``` 18 | 添加断言的操作符 19 | ```js 20 | // 添加表达式判断符 21 | class OrExpression { 22 | constructor(expr1, expr2) { 23 | this.expr1 = expr1; 24 | this.expr2 = expr2; 25 | } 26 | 27 | interpret(context) { 28 | return this.expr1.interpret(context) || this.expr2.interpret(context); 29 | } 30 | } 31 | class AndExpression { 32 | constructor(expr1, expr2) { 33 | this.expr1 = expr1; 34 | this.expr2 = expr2; 35 | } 36 | 37 | interpret(context) { 38 | return this.expr1.interpret(context) && this.expr2.interpret(context); 39 | } 40 | } 41 | ``` 42 | 组合断言操作符 43 | ```js 44 | // 获取对应表达式 45 | function getMaleExpression(){ 46 | const robert = new TerminalExpression("Robert"); 47 | const john = new TerminalExpression("John"); 48 | return new OrExpression(robert, john); 49 | } 50 | 51 | function getMarriedWomanExpression(){ 52 | const julie = new TerminalExpression("Julie"); 53 | const married = new TerminalExpression("Married"); 54 | return new AndExpression(julie, married); 55 | } 56 | ``` 57 | 实现断言的判断 58 | ```js 59 | // 判断语句断言 60 | const isMale = getMaleExpression(); 61 | const isMarriedWoman = getMarriedWomanExpression(); 62 | 63 | console.log("John is male? " + isMale.interpret("John")); 64 | console.log("Julie is a married women? " 65 | + isMarriedWoman.interpret("Married Julie")); 66 | /** 67 | * output: 68 | * John is male? true 69 | * Julie is a married women? true 70 | */ 71 | ``` 72 | 73 | # 解释器模式的优势 74 | 通过单一断言判断来组合出一些断言的提升。比如在解析二进制表达式断言的判断,只需要区分是否存在左表达式或右表达式,而不需要关心左表达式中是否还有左右表达式或右表达式中是否还有左右表达式,最后通过组合提升的方式,来解析整个二进制表达式。 75 | 76 | [上一页(命令模式)](../command-pattern/README.md) 77 | 78 | [下一页(迭代器模式)](../iterator-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/interpreter-pattern/index.js: -------------------------------------------------------------------------------- 1 | class TerminalExpression { 2 | constructor(data){ 3 | this.data = data; 4 | } 5 | interpret(context) { 6 | if(context.indexOf(this.data)>-1){ 7 | return true; 8 | } 9 | return false; 10 | } 11 | } 12 | // 添加表达式判断符 13 | class OrExpression { 14 | constructor(expr1, expr2) { 15 | this.expr1 = expr1; 16 | this.expr2 = expr2; 17 | } 18 | 19 | interpret(context) { 20 | return this.expr1.interpret(context) || this.expr2.interpret(context); 21 | } 22 | } 23 | class AndExpression { 24 | constructor(expr1, expr2) { 25 | this.expr1 = expr1; 26 | this.expr2 = expr2; 27 | } 28 | 29 | interpret(context) { 30 | return this.expr1.interpret(context) && this.expr2.interpret(context); 31 | } 32 | } 33 | 34 | // 获取对应表达式 35 | function getMaleExpression(){ 36 | const robert = new TerminalExpression("Robert"); 37 | const john = new TerminalExpression("John"); 38 | return new OrExpression(robert, john); 39 | } 40 | 41 | function getMarriedWomanExpression(){ 42 | const julie = new TerminalExpression("Julie"); 43 | const married = new TerminalExpression("Married"); 44 | return new AndExpression(julie, married); 45 | } 46 | 47 | // 判断语句断言 48 | const isMale = getMaleExpression(); 49 | const isMarriedWoman = getMarriedWomanExpression(); 50 | 51 | console.log("John is male? " + isMale.interpret("John")); 52 | console.log("Julie is a married women? " 53 | + isMarriedWoman.interpret("Married Julie")); 54 | 55 | /** 56 | * output: 57 | * John is male? true 58 | * Julie is a married women? true 59 | */ -------------------------------------------------------------------------------- /design-pattern/iterator-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 迭代器模式(Iterator Pattern) 2 | 主要是用于遍历数据,比如迭代链表。在数据过大时亦可像流一样,一点一点来接数据。 3 | 4 | # 迭代器模式的实例 5 | 定义生成器,在生成器中定义迭代器,并通过迭代器返回 6 | ```js 7 | class NameRepository { 8 | constructor() { 9 | this.names = ["Robert" , "John" ,"Julie" , "Lora"]; 10 | } 11 | getIterator() { 12 | const names = this.names; 13 | class NameIterator{ 14 | constructor() { 15 | this.index = 0; 16 | } 17 | hasNext() { 18 | if(this.index < names.length){ 19 | return true; 20 | } 21 | return false; 22 | } 23 | next() { 24 | if(this.hasNext()){ 25 | return names[this.index++]; 26 | } 27 | return null; 28 | } 29 | } 30 | return new NameIterator(); 31 | } 32 | } 33 | ``` 34 | 通过生成器和迭代器的方式,即可在for循环中直接遍历数据,如下 35 | ```js 36 | console.log("ES5 Iterator:"); 37 | const namesRepository = new NameRepository(); 38 | for(const iter = namesRepository.getIterator(); iter.hasNext();){ 39 | const name = iter.next(); 40 | console.log("Name : " + name); 41 | } 42 | /** 43 | * output: 44 | * ES5 Iterator: 45 | * Name : Robert 46 | * Name : John 47 | * Name : Julie 48 | * Name : Lora 49 | */ 50 | ``` 51 | 当然还有ES6的写法,其实很类似,就不详细说了。 52 | ```js 53 | // 而在es6中可以这样使用 54 | class NameRepositoryEs6 { 55 | constructor() { 56 | this.names = ["Robert" , "John" ,"Julie" , "Lora"]; 57 | this.index = 0; 58 | } 59 | [Symbol.iterator]() { 60 | return { 61 | next: () => { 62 | let done = true; 63 | if(this.index < this.names.length){ 64 | done = false; 65 | } 66 | return {value: this.names[this.index++],done}; 67 | } 68 | }; 69 | } 70 | } 71 | console.log("\nES6 Iterator:"); 72 | const namesRepositoryEs6 = new NameRepositoryEs6() 73 | for(const name of namesRepositoryEs6) { 74 | console.log("Name : " + name); 75 | } 76 | /** 77 | * output: 78 | * ES6 Iterator: 79 | * Name : Robert 80 | * Name : John 81 | * Name : Julie 82 | * Name : Lora 83 | */ 84 | ``` 85 | # 迭代器模式的优势 86 | 通过迭代器的方式可以更方便的遍历有规律的数据,或者是通过迭代器来完成一些流式操作。 87 | 88 | [上一页(解释器模式)](../interpreter-pattern/README.md) 89 | 90 | [下一页(中介者模式)](../mediator-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/iterator-pattern/index.js: -------------------------------------------------------------------------------- 1 | class NameRepository { 2 | constructor() { 3 | this.names = ["Robert" , "John" ,"Julie" , "Lora"]; 4 | } 5 | getIterator() { 6 | const names = this.names; 7 | class NameIterator{ 8 | constructor() { 9 | this.index = 0; 10 | } 11 | hasNext() { 12 | if(this.index < names.length){ 13 | return true; 14 | } 15 | return false; 16 | } 17 | 18 | next() { 19 | if(this.hasNext()){ 20 | return names[this.index++]; 21 | } 22 | return null; 23 | } 24 | } 25 | return new NameIterator(); 26 | } 27 | } 28 | 29 | console.log("ES5 Iterator:"); 30 | const namesRepository = new NameRepository(); 31 | for(const iter = namesRepository.getIterator(); iter.hasNext();){ 32 | const name = iter.next(); 33 | console.log("Name : " + name); 34 | } 35 | /** 36 | * output: 37 | * ES5 Iterator: 38 | * Name : Robert 39 | * Name : John 40 | * Name : Julie 41 | * Name : Lora 42 | */ 43 | 44 | // 而在es6中可以这样使用 45 | class NameRepositoryEs6 { 46 | constructor() { 47 | this.names = ["Robert" , "John" ,"Julie" , "Lora"]; 48 | this.index = 0; 49 | } 50 | [Symbol.iterator]() { 51 | return { 52 | next: () => { 53 | let done = true; 54 | if(this.index < this.names.length){ 55 | done = false; 56 | } 57 | return {value: this.names[this.index++],done}; 58 | } 59 | }; 60 | } 61 | } 62 | console.log("\nES6 Iterator:"); 63 | const namesRepositoryEs6 = new NameRepositoryEs6() 64 | for(const name of namesRepositoryEs6) { 65 | console.log("Name : " + name); 66 | } 67 | /** 68 | * output: 69 | * ES6 Iterator: 70 | * Name : Robert 71 | * Name : John 72 | * Name : Julie 73 | * Name : Lora 74 | */ -------------------------------------------------------------------------------- /design-pattern/mediator-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 中介者模式(Mediator Pattern) 2 | 中介者模式顾名思义,就是通过一个中间人来完成不同类的交互. 3 | 4 | # 中介者模式的实例 5 | 在租客和房东之间,中介者只需要负责进行租客或房东的交易,或者在交易中收取回扣。那么我们可以完全不用管租客或房东类的方法如何变化。 6 | ```js 7 | class Middleman { 8 | constructor(tenant, landlord) { 9 | this.tenant = tenant; 10 | this.landlord = landlord; 11 | } 12 | sendLandlord(money){ 13 | this.tenant.money-=money; 14 | this.landlord.money+=money; 15 | console.log(`Now: ${this.tenant.name} have $${this.tenant.money}` 16 | +`,${this.landlord.name} have $${this.landlord.money}`); 17 | } 18 | sendTenant(money){ 19 | this.landlord.money-=money; 20 | this.tenant.money+=money; 21 | console.log(`Now: ${this.tenant.name} have $${this.tenant.money}` 22 | +`,${this.landlord.name} have $${this.landlord.money}`); 23 | } 24 | } 25 | ``` 26 | 比如原来有租客和房东,假设如果要在租客类中的方法来给给房东的钱,那么必然要直接涉及和房东类的方法互相调用,那么这两个类就是耦合在一起。 27 | 28 | 那么如果中介者模式,可以通过中介者来进行缴纳,而不需要在租客的方法中操作房东的类,保证了两个类不会进行耦合。 29 | ```js 30 | // 租客 31 | class Tenant { 32 | constructor(name, money) { 33 | this.name = name; 34 | this.money = money; 35 | console.log(`${name} have $${money}`); 36 | } 37 | payDeposit(money,middleman) { 38 | console.log(`${this.name} pay $${money} deposit`); 39 | middleman.sendLandlord(money); 40 | } 41 | payRent(money,middleman) { 42 | console.log(`${this.name} pay $${money} for rent`); 43 | middleman.sendLandlord(money); 44 | } 45 | } 46 | 47 | // 房东 48 | class Landlord{ 49 | constructor(name, money) { 50 | this.name = name; 51 | this.money = money; 52 | console.log(`${name} have $${money}`); 53 | } 54 | refundDeposit(money,middleman) { 55 | console.log(`${this.name} refund $${money} deposit`); 56 | middleman.sendTenant(money) 57 | } 58 | } 59 | ``` 60 | 在我们调用的时候也更加直观。 61 | ```js 62 | const tenant = new Tenant('Jack',1000); 63 | const landlord = new Landlord('Zero',0); 64 | const middleman = new Middleman(tenant, landlord); 65 | tenant.payDeposit(300, middleman); 66 | tenant.payRent(100, middleman); 67 | tenant.payRent(100, middleman); 68 | tenant.payRent(100, middleman); 69 | landlord.refundDeposit(200, middleman); 70 | /** 71 | * output: 72 | * Jack have $1000 73 | * Zero have $0 74 | * Jack pay $300 deposit 75 | * Now: Jack have $700,Zero have $300 76 | * Jack pay $100 for rent 77 | * Now: Jack have $600,Zero have $400 78 | * Jack pay $100 for rent 79 | * Now: Jack have $500,Zero have $500 80 | * Jack pay $100 for rent 81 | * Now: Jack have $400,Zero have $600 82 | * Zero refund $200 deposit 83 | * Now: Jack have $600,Zero have $400 84 | */ 85 | ``` 86 | 87 | # 中介者优势 88 | 通过中介者模式,可以不需要管需要交互的类如何变化,只需通过中介者特定的接口访问,在两种不同模型的类来交互时,能起到解耦作用。 89 | 90 | 91 | [上一页(迭代器模式)](../iterator-pattern/README.md) 92 | 93 | [下一页(备忘录模式)](../memento-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/mediator-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Middleman { 2 | constructor(tenant, landlord) { 3 | this.tenant = tenant; 4 | this.landlord = landlord; 5 | } 6 | sendLandlord(money){ 7 | this.tenant.money-=money; 8 | this.landlord.money+=money; 9 | console.log(`Now: ${this.tenant.name} have $${this.tenant.money}` 10 | +`,${this.landlord.name} have $${this.landlord.money}`); 11 | } 12 | sendTenant(money){ 13 | this.landlord.money-=money; 14 | this.tenant.money+=money; 15 | console.log(`Now: ${this.tenant.name} have $${this.tenant.money}` 16 | +`,${this.landlord.name} have $${this.landlord.money}`); 17 | } 18 | } 19 | 20 | // 租客 21 | class Tenant { 22 | constructor(name, money) { 23 | this.name = name; 24 | this.money = money; 25 | console.log(`${name} have $${money}`); 26 | } 27 | payDeposit(money,middleman) { 28 | console.log(`${this.name} pay $${money} deposit`); 29 | middleman.sendLandlord(money); 30 | } 31 | payRent(money,middleman) { 32 | console.log(`${this.name} pay $${money} for rent`); 33 | middleman.sendLandlord(money); 34 | } 35 | } 36 | 37 | // 房东 38 | class Landlord{ 39 | constructor(name, money) { 40 | this.name = name; 41 | this.money = money; 42 | console.log(`${name} have $${money}`); 43 | } 44 | refundDeposit(money,middleman) { 45 | console.log(`${this.name} refund $${money} deposit`); 46 | middleman.sendTenant(money) 47 | } 48 | } 49 | 50 | const tenant = new Tenant('Jack',1000); 51 | const landlord = new Landlord('Zero',0); 52 | const middleman = new Middleman(tenant, landlord); 53 | tenant.payDeposit(300, middleman); 54 | tenant.payRent(100, middleman); 55 | tenant.payRent(100, middleman); 56 | tenant.payRent(100, middleman); 57 | landlord.refundDeposit(200, middleman); 58 | /** 59 | * output: 60 | * Jack have $1000 61 | * Zero have $0 62 | * Jack pay $300 deposit 63 | * Now: Jack have $700,Zero have $300 64 | * Jack pay $100 for rent 65 | * Now: Jack have $600,Zero have $400 66 | * Jack pay $100 for rent 67 | * Now: Jack have $500,Zero have $500 68 | * Jack pay $100 for rent 69 | * Now: Jack have $400,Zero have $600 70 | * Zero refund $200 deposit 71 | * Now: Jack have $600,Zero have $400 72 | */ -------------------------------------------------------------------------------- /design-pattern/memento-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 备忘录模式(Memento Pattern) 2 | 备忘录模式在游戏中很好理解,你在某个时间存了一份自己觉得完美的存档,但你要试试看看再让它更完美一点自己会不会挂掉,而不幸的是你挂了。那么你翻存档的时候发现还有一个存档,通过存档就像时间倒流一样回到了那个游戏节点。备忘录模式就是做这个用途的。 3 | 4 | # 备忘录模式的实例 5 | 首先我们需要定义备忘录和它的状态。 6 | ```js 7 | class Memento { 8 | constructor(state){ 9 | this.state = state; 10 | } 11 | getState(){ 12 | return this.state; 13 | } 14 | } 15 | ``` 16 | 通过发起人来获取需要保存的备忘录或者是还原出需要还原的状态 17 | ```js 18 | class Originator { 19 | setState(state){ 20 | this.state = state; 21 | } 22 | getState(){ 23 | return this.state; 24 | } 25 | saveStateToMemento(){ 26 | return new Memento(this.state); 27 | } 28 | getStateFromMemento(memento){ 29 | this.state = memento.getState(); 30 | } 31 | } 32 | ``` 33 | 通过守护者来记录存档,或者是获取存档 34 | ```js 35 | class CareTaker { 36 | constructor() { 37 | this.mementoList = []; 38 | } 39 | add(state){ 40 | this.mementoList.push(state); 41 | } 42 | get(index){ 43 | return this.mementoList[index]; 44 | } 45 | } 46 | ``` 47 | 通过发起人和守护者来实现,备忘录的还原或保存。 48 | ```js 49 | const originator = new Originator(); 50 | const careTaker = new CareTaker(); 51 | originator.setState("State #1"); 52 | originator.setState("State #2"); 53 | careTaker.add(originator.saveStateToMemento()); 54 | originator.setState("State #3"); 55 | careTaker.add(originator.saveStateToMemento()); 56 | originator.setState("State #4"); 57 | 58 | console.log("Current State: " + originator.getState()); 59 | originator.getStateFromMemento(careTaker.get(0)); 60 | console.log("First saved State: " + originator.getState()); 61 | originator.getStateFromMemento(careTaker.get(1)); 62 | console.log("Second saved State: " + originator.getState()); 63 | /** 64 | * output: 65 | * Current State: State #4 66 | * First saved State: State #2 67 | * Second saved State: State #3 68 | */ 69 | ``` 70 | # 备忘录模式的优势 71 | 可以说在编程中需要时光倒流的某些事件的一种最佳实践,通过备忘录模式来做类似的事情。 72 | 73 | [上一页(中介者模式)](../mediator-pattern/README.md) 74 | 75 | [下一页(观察者模式)](../observer-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/memento-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Memento { 2 | constructor(state){ 3 | this.state = state; 4 | } 5 | getState(){ 6 | return this.state; 7 | } 8 | } 9 | 10 | class Originator { 11 | setState(state){ 12 | this.state = state; 13 | } 14 | getState(){ 15 | return this.state; 16 | } 17 | saveStateToMemento(){ 18 | return new Memento(this.state); 19 | } 20 | getStateFromMemento(memento){ 21 | this.state = memento.getState(); 22 | } 23 | } 24 | 25 | class CareTaker { 26 | constructor() { 27 | this.mementoList = []; 28 | } 29 | add(state){ 30 | this.mementoList.push(state); 31 | } 32 | get(index){ 33 | return this.mementoList[index]; 34 | } 35 | } 36 | 37 | const originator = new Originator(); 38 | const careTaker = new CareTaker(); 39 | originator.setState("State #1"); 40 | originator.setState("State #2"); 41 | careTaker.add(originator.saveStateToMemento()); 42 | originator.setState("State #3"); 43 | careTaker.add(originator.saveStateToMemento()); 44 | originator.setState("State #4"); 45 | 46 | console.log("Current State: " + originator.getState()); 47 | originator.getStateFromMemento(careTaker.get(0)); 48 | console.log("First saved State: " + originator.getState()); 49 | originator.getStateFromMemento(careTaker.get(1)); 50 | console.log("Second saved State: " + originator.getState()); 51 | /** 52 | * output: 53 | * Current State: State #4 54 | * First saved State: State #2 55 | * Second saved State: State #3 56 | */ -------------------------------------------------------------------------------- /design-pattern/mvc-pattern/README.md: -------------------------------------------------------------------------------- 1 | # MVC模式(Model-View-Controller Pattern) 2 | 常用于单应用框架或多应用的单体框架中,不过目前前后端采取分离形态,所以V很多已挪到了前端。MVC模式指的是模型,视图,控制器相互分离的一种思想。 3 | 4 | # MVC模式的实例 5 | 首先构建模型,一般是指数据的最小单元,其实模型的设计又有很多细节。 6 | ```js 7 | /** 8 | * 模型,在业务中模式设计其实特别讲究 9 | * 在领域模型其实就细分了很多种类,比如: 10 | * 失血模型 11 | * 贫血模型 12 | * 充血模型 13 | * 胀血模型 14 | */ 15 | class Student { 16 | getRollNo() { 17 | return this.rollNo; 18 | } 19 | setRollNo(rollNo) { 20 | this.rollNo = rollNo; 21 | } 22 | getName() { 23 | return this.name; 24 | } 25 | setName(name) { 26 | this.name = name; 27 | } 28 | } 29 | ``` 30 | 其次构建视图,一般用于输出或显示。 31 | ```js 32 | class StudentView { 33 | printStudentDetails(studentName, studentRollNo){ 34 | console.log("Student: "); 35 | console.log("Name: " + studentName); 36 | console.log("Roll No: " + studentRollNo); 37 | } 38 | } 39 | ``` 40 | 最后根据控制器来控制模型渲染视图。 41 | ```js 42 | class StudentController { 43 | constructor(model, view){ 44 | this.model = model; 45 | this.view = view; 46 | } 47 | setStudentName(name){ 48 | this.model.setName(name); 49 | } 50 | getStudentName(){ 51 | return this.model.getName(); 52 | } 53 | setStudentRollNo(rollNo){ 54 | this.model.setRollNo(rollNo); 55 | } 56 | getStudentRollNo(){ 57 | return this.model.getRollNo(); 58 | } 59 | updateView(){ 60 | view.printStudentDetails(this.model.getName(), this.model.getRollNo()); 61 | } 62 | } 63 | ``` 64 | 在使用来说就是根据数据变化,更新显示了。 65 | ```js 66 | function retrieveStudentFromDatabase() { 67 | const student = new Student(); 68 | student.setName("Robert"); 69 | student.setRollNo("10"); 70 | return student; 71 | } 72 | 73 | const model = retrieveStudentFromDatabase(); 74 | //创建一个视图:把学生详细信息输出到控制台 75 | const view = new StudentView(); 76 | const controller = new StudentController(model, view); 77 | controller.updateView(); 78 | //更新模型数据 79 | controller.setStudentName("John"); 80 | controller.updateView(); 81 | ``` 82 | # MVC模式的优势 83 | 在MVC模式下,每层各司其职,层次清楚,层级干涉度小,有利于解除应用内层级之间的耦合。 -------------------------------------------------------------------------------- /design-pattern/mvc-pattern/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 模型,在业务中模式设计其实特别讲究 3 | * 在领域模型其实就细分了很多种类,比如: 4 | * 失血模型 5 | * 贫血模型 6 | * 充血模型 7 | * 胀血模型 8 | */ 9 | class Student { 10 | getRollNo() { 11 | return this.rollNo; 12 | } 13 | setRollNo(rollNo) { 14 | this.rollNo = rollNo; 15 | } 16 | getName() { 17 | return this.name; 18 | } 19 | setName(name) { 20 | this.name = name; 21 | } 22 | } 23 | 24 | class StudentView { 25 | printStudentDetails(studentName, studentRollNo){ 26 | console.log("Student: "); 27 | console.log("Name: " + studentName); 28 | console.log("Roll No: " + studentRollNo); 29 | } 30 | } 31 | 32 | class StudentController { 33 | constructor(model, view){ 34 | this.model = model; 35 | this.view = view; 36 | } 37 | setStudentName(name){ 38 | this.model.setName(name); 39 | } 40 | getStudentName(){ 41 | return this.model.getName(); 42 | } 43 | setStudentRollNo(rollNo){ 44 | this.model.setRollNo(rollNo); 45 | } 46 | getStudentRollNo(){ 47 | return this.model.getRollNo(); 48 | } 49 | updateView(){ 50 | view.printStudentDetails(this.model.getName(), this.model.getRollNo()); 51 | } 52 | } 53 | 54 | function retrieveStudentFromDatabase() { 55 | const student = new Student(); 56 | student.setName("Robert"); 57 | student.setRollNo("10"); 58 | return student; 59 | } 60 | 61 | const model = retrieveStudentFromDatabase(); 62 | //创建一个视图:把学生详细信息输出到控制台 63 | const view = new StudentView(); 64 | const controller = new StudentController(model, view); 65 | controller.updateView(); 66 | //更新模型数据 67 | controller.setStudentName("John"); 68 | controller.updateView(); -------------------------------------------------------------------------------- /design-pattern/null-object-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 空对象模式(Null Object Pattern) 2 | 当存在两个同类的对象时,可以用相同的空对象来代替Null或undefined来做一些检测。 3 | # 空对象模式的实例 4 | 定义基类 5 | ```js 6 | class Customer { 7 | constructor(name) { 8 | this.name = name; 9 | } 10 | getName() {} 11 | isNil() {} 12 | } 13 | ``` 14 | 定义一个真正使用的类和一个空对象类 15 | ```js 16 | class RealCustomer extends Customer { 17 | getName() { 18 | return this.name; 19 | } 20 | isNil() { 21 | return false; 22 | } 23 | } 24 | class NullCustomer extends Customer { 25 | getName() { 26 | return "Not Available in Customer Database"; 27 | } 28 | isNil() { 29 | return true; 30 | } 31 | } 32 | ``` 33 | 定义对象工厂方便生产 34 | ```js 35 | class CustomerFactory { 36 | static getCustomer(name){ 37 | for (let i = 0; i < CustomerFactory.names.length; i++) { 38 | if (CustomerFactory.names[i].toUpperCase()==name.toUpperCase()){ 39 | return new RealCustomer(name); 40 | } 41 | } 42 | return new NullCustomer(); 43 | } 44 | } 45 | ``` 46 | 在使用的时候空对象可以正常使用,而null却不能调用getName方法。 47 | ```js 48 | CustomerFactory.names = ["Rob", "Joe", "Julie"]; 49 | 50 | const customer1 = CustomerFactory.getCustomer("Rob"); 51 | const customer2 = CustomerFactory.getCustomer("Bob"); 52 | const customer3 = CustomerFactory.getCustomer("Julie"); 53 | const customer4 = CustomerFactory.getCustomer("Laura"); 54 | 55 | console.log("Customers"); 56 | console.log(customer1.getName()); 57 | console.log(customer2.getName()); 58 | console.log(customer3.getName()); 59 | console.log(customer4.getName()); 60 | /** 61 | * output: 62 | * Customers 63 | * Rob 64 | * Not Available in Customer Database 65 | * Julie 66 | * Not Available in Customer Database 67 | */ 68 | ``` 69 | # 空对象模式的优势 70 | 比如在使用某个类时,需要对这个类来做空判断,在不确定后续这个类的方法是否会被调用时,用一个相同类的空对象来返回,这样可以更加无缝对接空值判断。 71 | 72 | 73 | [上一页(状态模式)](../state-pattern/README.md) 74 | 75 | [下一页(策略模式)](../strategy-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/null-object-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Customer { 2 | constructor(name) { 3 | this.name = name; 4 | } 5 | getName() {} 6 | isNil() {} 7 | } 8 | 9 | class RealCustomer extends Customer { 10 | getName() { 11 | return this.name; 12 | } 13 | isNil() { 14 | return false; 15 | } 16 | } 17 | class NullCustomer extends Customer { 18 | getName() { 19 | return "Not Available in Customer Database"; 20 | } 21 | isNil() { 22 | return true; 23 | } 24 | } 25 | 26 | class CustomerFactory { 27 | static getCustomer(name){ 28 | for (let i = 0; i < CustomerFactory.names.length; i++) { 29 | if (CustomerFactory.names[i].toUpperCase()==name.toUpperCase()){ 30 | return new RealCustomer(name); 31 | } 32 | } 33 | return new NullCustomer(); 34 | } 35 | } 36 | CustomerFactory.names = ["Rob", "Joe", "Julie"]; 37 | 38 | const customer1 = CustomerFactory.getCustomer("Rob"); 39 | const customer2 = CustomerFactory.getCustomer("Bob"); 40 | const customer3 = CustomerFactory.getCustomer("Julie"); 41 | const customer4 = CustomerFactory.getCustomer("Laura"); 42 | 43 | console.log("Customers"); 44 | console.log(customer1.getName()); 45 | console.log(customer2.getName()); 46 | console.log(customer3.getName()); 47 | console.log(customer4.getName()); 48 | /** 49 | * output: 50 | * Customers 51 | * Rob 52 | * Not Available in Customer Database 53 | * Julie 54 | * Not Available in Customer Database 55 | */ -------------------------------------------------------------------------------- /design-pattern/observer-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 观察者模式(Observer Pattern) 2 | 当一个对象和另外几个对象产生关联的时候,当这个对象发生改变的时候实现通知对应对象变更。 3 | 4 | # 观察者模式的实例 5 | 首先建立好观察者模式的模型,即构建队列再进行通知。 6 | ```js 7 | class Subject { 8 | constructor() { 9 | this.observers = []; 10 | } 11 | getState() { 12 | return this.state; 13 | } 14 | setState(state) { 15 | this.state = state; 16 | this.notifyAllObservers(); 17 | } 18 | attach(observer){ 19 | this.observers.push(observer); 20 | } 21 | notifyAllObservers(){ 22 | for (const observer of this.observers) { 23 | observer.update(); 24 | } 25 | } 26 | } 27 | ``` 28 | 假设我们需要实现一个功能输入一个十进制要显示出对应的其它进制,比如二进制和八进制和十六进制。当对象初始化后加入到观察者模型中。 29 | ```js 30 | class BinaryObserver { 31 | constructor(subject){ 32 | this.subject = subject; 33 | this.subject.attach(this); 34 | } 35 | update() { 36 | console.log( "Binary String: " 37 | + this.subject.getState().toString(2) ); 38 | } 39 | } 40 | class OctalObserver { 41 | constructor(subject){ 42 | this.subject = subject; 43 | this.subject.attach(this); 44 | } 45 | update() { 46 | console.log( "Octal String: " 47 | + this.subject.getState().toString(8) ); 48 | } 49 | } 50 | 51 | class HexObserver { 52 | constructor(subject){ 53 | this.subject = subject; 54 | this.subject.attach(this); 55 | } 56 | update() { 57 | console.log( "Hex String: " 58 | + this.subject.getState().toString(16) ); 59 | } 60 | } 61 | ``` 62 | 在使用的时候,只需要在观察者和对应的进制初始化完成即可使用。 63 | ```js 64 | const subject = new Subject(); 65 | 66 | new HexObserver(subject); 67 | new OctalObserver(subject); 68 | new BinaryObserver(subject); 69 | 70 | console.log("First state change: 15"); 71 | subject.setState(15); 72 | console.log("Second state change: 10"); 73 | subject.setState(10); 74 | /** 75 | * output: 76 | * First state change: 15 77 | * Hex String: f 78 | * Octal String: 17 79 | * Binary String: 1111 80 | * Second state change: 10 81 | * Hex String: a 82 | * Octal String: 12 83 | * Binary String: 1010 84 | */ 85 | ``` 86 | 87 | # 观察者模式的优势 88 | 对于对象和对象的关系中,可以通过这种方式来降低耦合,同时在代码的使用上也更加直观。 89 | 90 | [上一页(备忘录模式)](../memento-pattern/README.md) 91 | 92 | [下一页(状态模式)](../state-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/observer-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Subject { 2 | constructor() { 3 | this.observers = []; 4 | } 5 | getState() { 6 | return this.state; 7 | } 8 | setState(state) { 9 | this.state = state; 10 | this.notifyAllObservers(); 11 | } 12 | attach(observer){ 13 | this.observers.push(observer); 14 | } 15 | notifyAllObservers(){ 16 | for (const observer of this.observers) { 17 | observer.update(); 18 | } 19 | } 20 | } 21 | 22 | class BinaryObserver { 23 | constructor(subject){ 24 | this.subject = subject; 25 | this.subject.attach(this); 26 | } 27 | update() { 28 | console.log( "Binary String: " 29 | + this.subject.getState().toString(2) ); 30 | } 31 | } 32 | class OctalObserver { 33 | constructor(subject){ 34 | this.subject = subject; 35 | this.subject.attach(this); 36 | } 37 | update() { 38 | console.log( "Octal String: " 39 | + this.subject.getState().toString(8) ); 40 | } 41 | } 42 | 43 | class HexObserver { 44 | constructor(subject){ 45 | this.subject = subject; 46 | this.subject.attach(this); 47 | } 48 | update() { 49 | console.log( "Hex String: " 50 | + this.subject.getState().toString(16) ); 51 | } 52 | } 53 | 54 | const subject = new Subject(); 55 | 56 | new HexObserver(subject); 57 | new OctalObserver(subject); 58 | new BinaryObserver(subject); 59 | 60 | console.log("First state change: 15"); 61 | subject.setState(15); 62 | console.log("Second state change: 10"); 63 | subject.setState(10); 64 | /** 65 | * output: 66 | * First state change: 15 67 | * Hex String: f 68 | * Octal String: 17 69 | * Binary String: 1111 70 | * Second state change: 10 71 | * Hex String: a 72 | * Octal String: 12 73 | * Binary String: 1010 74 | */ -------------------------------------------------------------------------------- /design-pattern/prototype-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 原型模式(Prototype Pattern) 2 | 大家熟知的原型链,也是按照原型模式进行。原型模式是一种创建对象的方式。今天我们就以JS的原型链作为主要讲解,通过原型链的实例继承和对象的创建。本文可能需要对JS原型链一定的了解,否则阅读本文可能会相对困难。 3 | 4 | # 原型模式的实例 5 | 首先我们需要实现一个类,这个类可以通过clone的方法实现原型的创建对象,那么这个clone实际是就是实现了JS中的new的功能,因为JS对象的创建本来就是通过原型的方式实现而不是完全重现开辟空间。所以我们讲原型模式可以直接模拟JS类创建的方式即可。 6 | ```js 7 | class Shape { 8 | constructor() { 9 | this.id = null; 10 | this.type = null; 11 | } 12 | getType(){ 13 | return this.type; 14 | } 15 | getId() { 16 | return this.id; 17 | } 18 | setId(id) { 19 | this.id = id; 20 | } 21 | clone() { 22 | /** 23 | * 如果子类要改成class形式,这个方法要改写成下面形式 24 | * 因为主要是通过JS原型链帮助理解原型模式,所以子类不使用class形式 25 | * class和function构造函数的区别是class的构造函数增加了只能作为构造函数使用的校验,比如new 26 | * return Reflect.construct( 27 | * this.__proto__.constructor, 28 | * [], 29 | * this.__proto__.constructor 30 | * ) 31 | */ 32 | let clone = {}; 33 | // 注意如果此类被继承,this会变成子类的方法 34 | // 同时这里使用的是原型的指针,所以比直接创建对象性能损耗更低 35 | clone.__proto__ = this.__proto__; 36 | this.__proto__.constructor.call(clone); 37 | return clone; 38 | } 39 | } 40 | ``` 41 | 对于子类,原型链要实现继承是需要通过不断的追溯__proto__之上的对象作为继承的类,而prototype实例化之后会赋引用值到__proto__,所以要实现继承则是绑定prototype.__proto__作为追溯所得的结果之一。 42 | ```js 43 | function Rectangle() { 44 | this.type = "Rectangle"; 45 | } 46 | Rectangle.prototype.__proto__ = new Shape(); 47 | Rectangle.prototype.draw = function() { 48 | console.log("I'm a rectangle") 49 | } 50 | 51 | function Square() { 52 | this.type = "Square"; 53 | } 54 | Square.prototype.__proto__ = new Shape(); 55 | Square.prototype.draw = function() { 56 | console.log("I'm a square") 57 | } 58 | 59 | function Circle() { 60 | this.type = "Circle"; 61 | } 62 | Circle.prototype.__proto__ = new Shape(); 63 | Circle.prototype.draw = function() { 64 | console.log("I'm a circle") 65 | } 66 | ``` 67 | 在当前例子中,我们通过载入形状的cache的方式,再从cache中调用clone方法来实现原型创建的例子。 68 | ```js 69 | class ShapeCache { 70 | static getShape(shapeId) { 71 | const cachedShape = ShapeCache.shapeMap.get(shapeId); 72 | return cachedShape.clone(); 73 | } 74 | static loadCache() { 75 | const circle = new Circle(); 76 | circle.setId("1"); 77 | ShapeCache.shapeMap.set(circle.getId(),circle); 78 | 79 | const square = new Square(); 80 | square.setId("2"); 81 | ShapeCache.shapeMap.set(square.getId(),square); 82 | 83 | const rectangle = new Rectangle(); 84 | rectangle.setId("3"); 85 | ShapeCache.shapeMap.set(rectangle.getId(),rectangle); 86 | } 87 | } 88 | ShapeCache.shapeMap = new Map(); 89 | 90 | 91 | ShapeCache.loadCache(); 92 | 93 | const clonedShape = ShapeCache.getShape("1"); 94 | console.log("Shape : " + clonedShape.getType()); 95 | 96 | const clonedShape2 = ShapeCache.getShape("2"); 97 | console.log("Shape : " + clonedShape2.getType()); 98 | 99 | const clonedShape3 = ShapeCache.getShape("3"); 100 | console.log("Shape : " + clonedShape3.getType()); 101 | /** 102 | * output: 103 | * Shape : Circle 104 | * Shape : Square 105 | * Shape : Rectangle 106 | */ 107 | ``` 108 | # 原型模式的优势 109 | 在其它编程中使用原型模式的优势是使用更小的代价来创建对象,通过原型引用的方式而不是开辟新的空间。但JS是个例外,直接new就好了,因为JS创建对象的方式就是原型引用,所以对比其它语言创建大对象的性能,能高出不少。 110 | 111 | [上一页(建造者模式)](../builder-pattern/README.md) 112 | 113 | [下一页(适配器模式)](../adapter-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/prototype-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Shape { 2 | constructor() { 3 | this.id = null; 4 | this.type = null; 5 | } 6 | getType(){ 7 | return this.type; 8 | } 9 | getId() { 10 | return this.id; 11 | } 12 | setId(id) { 13 | this.id = id; 14 | } 15 | clone() { 16 | /** 17 | * 如果子类要改成class形式,这个方法要改写成下面形式 18 | * 因为主要是通过JS原型链帮助理解原型模式,所以子类不使用class形式 19 | * class和function构造函数的区别是class的构造函数增加了只能作为构造函数使用的校验,比如new 20 | * return Reflect.construct( 21 | * this.__proto__.constructor, 22 | * [], 23 | * this.__proto__.constructor 24 | * ) 25 | */ 26 | let clone = {}; 27 | // 注意如果此类被继承,this会变成子类的方法 28 | // 同时这里使用的是原型的指针,所以比直接创建对象性能损耗更低 29 | clone.__proto__ = this.__proto__; 30 | this.__proto__.constructor.call(clone); 31 | return clone; 32 | } 33 | } 34 | 35 | function Rectangle() { 36 | this.type = "Rectangle"; 37 | } 38 | Rectangle.prototype.__proto__ = new Shape(); 39 | Rectangle.prototype.draw = function() { 40 | console.log("I'm a rectangle") 41 | } 42 | 43 | function Square() { 44 | this.type = "Square"; 45 | } 46 | Square.prototype.__proto__ = new Shape(); 47 | Square.prototype.draw = function() { 48 | console.log("I'm a square") 49 | } 50 | 51 | function Circle() { 52 | this.type = "Circle"; 53 | } 54 | Circle.prototype.__proto__ = new Shape(); 55 | Circle.prototype.draw = function() { 56 | console.log("I'm a circle") 57 | } 58 | 59 | class ShapeCache { 60 | static getShape(shapeId) { 61 | const cachedShape = ShapeCache.shapeMap.get(shapeId); 62 | return cachedShape.clone(); 63 | } 64 | static loadCache() { 65 | const circle = new Circle(); 66 | circle.setId("1"); 67 | ShapeCache.shapeMap.set(circle.getId(),circle); 68 | 69 | const square = new Square(); 70 | square.setId("2"); 71 | ShapeCache.shapeMap.set(square.getId(),square); 72 | 73 | const rectangle = new Rectangle(); 74 | rectangle.setId("3"); 75 | ShapeCache.shapeMap.set(rectangle.getId(),rectangle); 76 | } 77 | } 78 | ShapeCache.shapeMap = new Map(); 79 | 80 | 81 | ShapeCache.loadCache(); 82 | 83 | const clonedShape = ShapeCache.getShape("1"); 84 | console.log("Shape : " + clonedShape.getType()); 85 | 86 | const clonedShape2 = ShapeCache.getShape("2"); 87 | console.log("Shape : " + clonedShape2.getType()); 88 | 89 | const clonedShape3 = ShapeCache.getShape("3"); 90 | console.log("Shape : " + clonedShape3.getType()); 91 | /** 92 | * output: 93 | * Shape : Circle 94 | * Shape : Square 95 | * Shape : Rectangle 96 | */ -------------------------------------------------------------------------------- /design-pattern/proxy-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 代理模式(Proxy Pattern) 2 | 用一个新的类来代理另一个类,这种模式一般用于方法的前置操作或拦截。 3 | # 代理模式的实例 4 | 现有一个类在初始化的时候会装载磁盘,再调用display按钮展示文件。 5 | ```js 6 | class RealImage { 7 | constructor(fileName){ 8 | this.fileName = fileName; 9 | this.loadFromDisk(fileName); 10 | } 11 | display() { 12 | console.log("Displaying " + this.fileName); 13 | } 14 | loadFromDisk(fileName){ 15 | console.log("Loading " + fileName); 16 | } 17 | } 18 | ``` 19 | 但我不想这样,我希望初始化不载入文件,而是在display的时候,如果发现没有载入就载入,如果载入了那就直接展示,那么我们要怎么做呢? 20 | 21 | 这个时候我们可以用一个类来代理这个类的行为。 22 | ```js 23 | class ProxyImage { 24 | constructor(fileName){ 25 | this.fileName = fileName; 26 | } 27 | display() { 28 | if(this.realImage == null){ 29 | this.realImage = new RealImage(this.fileName); 30 | } 31 | this.realImage.display(); 32 | } 33 | } 34 | ``` 35 | 那么我们在使用的时候就可以不改变原来的类,而实现一些不一样的功能 36 | ```js 37 | const image = new ProxyImage("test_10mb.jpg"); 38 | 39 | console.log('not use es6 Proxy:') 40 | // 图像将从磁盘加载 41 | image.display(); 42 | console.log('') 43 | // 图像不需要从磁盘加载 44 | image.display(); 45 | /** 46 | * output: 47 | * not use es6 Proxy: 48 | * Loading test_10mb.jpg 49 | * Displaying test_10mb.jpg 50 | * 51 | * Displaying test_10mb.jpg 52 | */ 53 | ``` 54 | 当然用最新的ES6也提供了代理的功能,就不细说了,有兴趣可以看以下 55 | ```js 56 | const ProxyImageEs6 = new Proxy(RealImage,{ 57 | construct: function(target, args) { 58 | let imageObj = {}; 59 | imageObj.__proto__ = target.prototype; 60 | imageObj.fileName = args[0]; 61 | return new Proxy(imageObj,{ 62 | get: function (target, key, receiver) { 63 | if(key=='display') { 64 | return ()=>{ 65 | if(this.realImage == null){ 66 | this.realImage = new RealImage(target.fileName); 67 | } 68 | this.realImage.display(); 69 | }; 70 | } 71 | return Reflect.get(target, key, receiver); 72 | }, 73 | }); 74 | } 75 | }) 76 | console.log('\n\nuse es6 Proxy:') 77 | const imageEs6 = new ProxyImageEs6("test_10mb.jpg"); 78 | // 图像将从磁盘加载 79 | imageEs6.display(); 80 | console.log('') 81 | // 图像不需要从磁盘加载 82 | imageEs6.display(); 83 | /** 84 | * output: 85 | * use es6 Proxy: 86 | * Loading test_10mb.jpg 87 | * Displaying test_10mb.jpg 88 | * 89 | * Displaying test_10mb.jpg 90 | */ 91 | ``` 92 | 93 | # 代理模式的优势 94 | 能在不改变原有类的情况下,实现一些功能或者实现拦截和一些前置操作。 95 | 96 | [上一页(享元模式)](../flyweight-pattern/README.md) 97 | 98 | [下一页(责任链模式)](../chain-of-responsibility-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/proxy-pattern/index.js: -------------------------------------------------------------------------------- 1 | class RealImage { 2 | constructor(fileName){ 3 | this.fileName = fileName; 4 | this.loadFromDisk(fileName); 5 | } 6 | display() { 7 | console.log("Displaying " + this.fileName); 8 | } 9 | loadFromDisk(fileName){ 10 | console.log("Loading " + fileName); 11 | } 12 | } 13 | 14 | class ProxyImage { 15 | constructor(fileName){ 16 | this.fileName = fileName; 17 | } 18 | display() { 19 | if(this.realImage == null){ 20 | this.realImage = new RealImage(this.fileName); 21 | } 22 | this.realImage.display(); 23 | } 24 | } 25 | 26 | const image = new ProxyImage("test_10mb.jpg"); 27 | 28 | console.log('not use es6 Proxy:') 29 | // 图像将从磁盘加载 30 | image.display(); 31 | console.log('') 32 | // 图像不需要从磁盘加载 33 | image.display(); 34 | /** 35 | * output: 36 | * not use es6 Proxy: 37 | * Loading test_10mb.jpg 38 | * Displaying test_10mb.jpg 39 | * 40 | * Displaying test_10mb.jpg 41 | */ 42 | 43 | // 其实上面的代码可以直接用es6代替 44 | const ProxyImageEs6 = new Proxy(RealImage,{ 45 | construct: function(target, args) { 46 | let imageObj = {}; 47 | imageObj.__proto__ = target.prototype; 48 | imageObj.fileName = args[0]; 49 | return new Proxy(imageObj,{ 50 | get: function (target, key, receiver) { 51 | if(key=='display') { 52 | return ()=>{ 53 | if(this.realImage == null){ 54 | this.realImage = new RealImage(target.fileName); 55 | } 56 | this.realImage.display(); 57 | }; 58 | } 59 | return Reflect.get(target, key, receiver); 60 | }, 61 | }); 62 | } 63 | }) 64 | console.log('\n\nuse es6 Proxy:') 65 | const imageEs6 = new ProxyImageEs6("test_10mb.jpg"); 66 | // 图像将从磁盘加载 67 | imageEs6.display(); 68 | console.log('') 69 | // 图像不需要从磁盘加载 70 | imageEs6.display(); 71 | /** 72 | * output: 73 | * use es6 Proxy: 74 | * Loading test_10mb.jpg 75 | * Displaying test_10mb.jpg 76 | * 77 | * Displaying test_10mb.jpg 78 | */ -------------------------------------------------------------------------------- /design-pattern/service-locator-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 服务定位器模式(Service Locator Pattern) 2 | 即原来要通过JNDI查询定位服务成本很高,所以通过一系列手段将服务缓存起来,让服务更快返回,这也是缓存服务的一种最佳实践。 3 | 4 | # 服务定位器模式的实例 5 | 定义多种服务 6 | ```js 7 | class Service1 { 8 | execute(){ 9 | console.log("Executing Service1"); 10 | } 11 | 12 | getName() { 13 | return "Service1"; 14 | } 15 | } 16 | class Service2 { 17 | execute(){ 18 | console.log("Executing Service2"); 19 | } 20 | 21 | getName() { 22 | return "Service2"; 23 | } 24 | } 25 | ``` 26 | 定义查询服务功能 27 | ```js 28 | class InitialContext { 29 | lookup(jndiName){ 30 | switch(jndiName.toUpperCase()) { 31 | case "SERVICE1": 32 | console.log("Looking up and creating a new Service1 object"); 33 | return new Service1(); 34 | case "SERVICE2": 35 | console.log("Looking up and creating a new Service2 object"); 36 | return new Service2(); 37 | default: 38 | return null; 39 | } 40 | } 41 | } 42 | ``` 43 | 定义服务缓存 44 | ```js 45 | class Cache { 46 | constructor(){ 47 | this.services = []; 48 | } 49 | getService(serviceName){ 50 | for (const service of this.services) { 51 | if(service.getName().toUpperCase()==serviceName.toUpperCase()){ 52 | console.log("Returning cached "+serviceName+" object"); 53 | return service; 54 | } 55 | } 56 | return null; 57 | } 58 | addService(newService){ 59 | let exists = false; 60 | for (const service of this.services) { 61 | if(service.getName().toUpperCase()==newService.getName().toUpperCase()){ 62 | exists = true; 63 | } 64 | } 65 | if(!exists){ 66 | this.services.push(newService); 67 | } 68 | } 69 | } 70 | ``` 71 | 定义服务定位器,其功能是在获取服务时,优先从缓存中获取,若不存在再查询并加入缓存 72 | ```js 73 | class ServiceLocator { 74 | static getService(jndiName){ 75 | const service = ServiceLocator.cache.getService(jndiName); 76 | if(service != null){ 77 | return service; 78 | } 79 | const context = new InitialContext(); 80 | const service1 = context.lookup(jndiName); 81 | ServiceLocator.cache.addService(service1); 82 | return service1; 83 | } 84 | } 85 | ServiceLocator.cache = new Cache(); 86 | ``` 87 | 通过服务定位器获取服务后执行 88 | ```js 89 | let service = ServiceLocator.getService("Service1"); 90 | service.execute(); 91 | service = ServiceLocator.getService("Service2"); 92 | service.execute(); 93 | service = ServiceLocator.getService("Service1"); 94 | service.execute(); 95 | service = ServiceLocator.getService("Service2"); 96 | service.execute(); 97 | ``` 98 | 99 | # 服务定位器模式的优势 100 | 缓存服务和数据的最佳实践 -------------------------------------------------------------------------------- /design-pattern/service-locator-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Service1 { 2 | execute(){ 3 | console.log("Executing Service1"); 4 | } 5 | 6 | getName() { 7 | return "Service1"; 8 | } 9 | } 10 | class Service2 { 11 | execute(){ 12 | console.log("Executing Service2"); 13 | } 14 | 15 | getName() { 16 | return "Service2"; 17 | } 18 | } 19 | 20 | class InitialContext { 21 | lookup(jndiName){ 22 | switch(jndiName.toUpperCase()) { 23 | case "SERVICE1": 24 | console.log("Looking up and creating a new Service1 object"); 25 | return new Service1(); 26 | case "SERVICE2": 27 | console.log("Looking up and creating a new Service2 object"); 28 | return new Service2(); 29 | default: 30 | return null; 31 | } 32 | } 33 | } 34 | 35 | class Cache { 36 | constructor(){ 37 | this.services = []; 38 | } 39 | getService(serviceName){ 40 | for (const service of this.services) { 41 | if(service.getName().toUpperCase()==serviceName.toUpperCase()){ 42 | console.log("Returning cached "+serviceName+" object"); 43 | return service; 44 | } 45 | } 46 | return null; 47 | } 48 | addService(newService){ 49 | let exists = false; 50 | for (const service of this.services) { 51 | if(service.getName().toUpperCase()==newService.getName().toUpperCase()){ 52 | exists = true; 53 | } 54 | } 55 | if(!exists){ 56 | this.services.push(newService); 57 | } 58 | } 59 | } 60 | 61 | class ServiceLocator { 62 | static getService(jndiName){ 63 | const service = ServiceLocator.cache.getService(jndiName); 64 | if(service != null){ 65 | return service; 66 | } 67 | const context = new InitialContext(); 68 | const service1 = context.lookup(jndiName); 69 | ServiceLocator.cache.addService(service1); 70 | return service1; 71 | } 72 | } 73 | ServiceLocator.cache = new Cache(); 74 | 75 | let service = ServiceLocator.getService("Service1"); 76 | service.execute(); 77 | service = ServiceLocator.getService("Service2"); 78 | service.execute(); 79 | service = ServiceLocator.getService("Service1"); 80 | service.execute(); 81 | service = ServiceLocator.getService("Service2"); 82 | service.execute(); -------------------------------------------------------------------------------- /design-pattern/singleton-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 单例模式(Singleton Pattern) 2 | 什么叫单例模式,简单来说就是一个实例只生产一次。 3 | 4 | # 单例模式的实例 5 | 这个很简单,我觉得可以直接看代码。 6 | 7 | 这是一种“懒汉式”写法,还有一种叫饿汉式写法,区别是懒汉使用时才初始化,饿汉则先初始化,用的时候直接给。 8 | 9 | 由于js不需要考虑线程安全,所以推荐使用懒汉式写法,饿汉在JS中反而容易产生没必要的垃圾。 10 | ```js 11 | class SingleObject { 12 | constructor() { 13 | // 防止调用new初始化 14 | if(new.target != undefined) { 15 | const errorMsg = "This is single object,Can't use keyword new!"; 16 | const tipMsg = "You should use method getInstance to get instance。"; 17 | throw new Error(`\n${errorMsg}\n${tipMsg}`) 18 | } 19 | } 20 | static getInstance(){ 21 | // 生产单例 22 | if(SingleObject.instance) { 23 | return SingleObject.instance; 24 | } 25 | SingleObject.instance = {}; 26 | SingleObject.instance.__proto__ = SingleObject.prototype; 27 | return SingleObject.instance; 28 | } 29 | 30 | showMessage(){ 31 | console.log("Hello World!"); 32 | } 33 | } 34 | const instance = SingleObject.getInstance(); 35 | instance.showMessage(); 36 | /** 37 | * output: 38 | * Hello World! 39 | */ 40 | ``` 41 | # 单例模式的优势 42 | 对于频繁使用且可重复使用的对象,可以极大来减少内存消耗和没必要的垃圾回收。 43 | 44 | [上一页(抽象工厂模式)](../abstract-factory-pattern/README.md) 45 | 46 | [下一页(建造者模式)](../builder-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/singleton-pattern/index.js: -------------------------------------------------------------------------------- 1 | // 这是一种“懒汉式”写法,还有一种叫饿汉式写法,区别是懒汉使用时才初始化,饿汉则先初始化,用的时候直接给。 2 | // 由于js不需要考虑线程安全,所以推荐使用懒汉式写法,饿汉在JS中反而容易产生没必要的垃圾。 3 | class SingleObject { 4 | constructor() { 5 | // 防止调用new初始化 6 | if(new.target != undefined) { 7 | const errorMsg = "This is single object,Can't use keyword new!"; 8 | const tipMsg = "You should use method getInstance to get instance。"; 9 | throw new Error(`\n${errorMsg}\n${tipMsg}`) 10 | } 11 | } 12 | static getInstance(){ 13 | // 生产单例 14 | if(SingleObject.instance) { 15 | return SingleObject.instance; 16 | } 17 | SingleObject.instance = {}; 18 | SingleObject.instance.__proto__ = SingleObject.prototype; 19 | return SingleObject.instance; 20 | } 21 | 22 | showMessage(){ 23 | console.log("Hello World!"); 24 | } 25 | } 26 | const instance = SingleObject.getInstance(); 27 | instance.showMessage(); 28 | 29 | /** 30 | * output: 31 | * Hello World! 32 | */ -------------------------------------------------------------------------------- /design-pattern/state-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 状态模式(State Pattern) 2 | 即类的行为根据状态的改变而改变。 3 | # 状态模式的实例 4 | 举例定义了两种状态,开始状态和结束状态 5 | ```js 6 | class StartState { 7 | doAction(context) { 8 | console.log("Player is in start state"); 9 | context.setState(this); 10 | } 11 | toString(){ 12 | return "Start State"; 13 | } 14 | } 15 | class StopState { 16 | doAction(context) { 17 | console.log("Player is in stop state"); 18 | context.setState(this); 19 | } 20 | toString(){ 21 | return "Stop State"; 22 | } 23 | } 24 | ``` 25 | 定义根据状态,而发生改变的上下文类型。 26 | ```js 27 | class Context { 28 | constructor(){ 29 | this.state = null; 30 | } 31 | setState(state){ 32 | this.state = state; 33 | } 34 | getState(){ 35 | return this.state.toString(); 36 | } 37 | } 38 | ``` 39 | 那么在使用的时候,当Context的状态发生改变,那么Context获取状态的行为也发生了改变 40 | ```js 41 | const context = new Context(); 42 | 43 | const startState = new StartState(); 44 | startState.doAction(context); 45 | 46 | console.log(context.getState()); 47 | 48 | const stopState = new StopState(); 49 | stopState.doAction(context); 50 | 51 | console.log(context.getState()); 52 | /** 53 | * output: 54 | * Player is in start state 55 | * Start State 56 | * Player is in stop state 57 | * Stop State 58 | */ 59 | ``` 60 | # 状态模式的优势 61 | 适合一些需要根据状态而改变的类,或通过此来消除一些if...else语句。 62 | 63 | [上一页(观察者模式)](../observer-pattern/README.md) 64 | 65 | [下一页(空对象模式)](../null-object-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/state-pattern/index.js: -------------------------------------------------------------------------------- 1 | class StartState { 2 | doAction(context) { 3 | console.log("Player is in start state"); 4 | context.setState(this); 5 | } 6 | toString(){ 7 | return "Start State"; 8 | } 9 | } 10 | class StopState { 11 | doAction(context) { 12 | console.log("Player is in stop state"); 13 | context.setState(this); 14 | } 15 | toString(){ 16 | return "Stop State"; 17 | } 18 | } 19 | 20 | class Context { 21 | constructor(){ 22 | this.state = null; 23 | } 24 | setState(state){ 25 | this.state = state; 26 | } 27 | getState(){ 28 | return this.state.toString(); 29 | } 30 | } 31 | 32 | const context = new Context(); 33 | 34 | const startState = new StartState(); 35 | startState.doAction(context); 36 | 37 | console.log(context.getState()); 38 | 39 | const stopState = new StopState(); 40 | stopState.doAction(context); 41 | 42 | console.log(context.getState()); 43 | /** 44 | * output: 45 | * Player is in start state 46 | * Start State 47 | * Player is in stop state 48 | * Stop State 49 | */ -------------------------------------------------------------------------------- /design-pattern/strategy-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 策略模式(Strategy Pattern) 2 | 策略模式,我觉得是状态模式的一种升级或衍生版本,策略模式更倾向于运算或策略,而状态模式更倾向于状态。 3 | # 策略模式的实例 4 | 首先定义几种不同的策略 5 | ```js 6 | class OperationAdd { 7 | doOperation(num1, num2) { 8 | return num1 + num2; 9 | } 10 | } 11 | class OperationSubstract { 12 | doOperation(num1, num2) { 13 | return num1 - num2; 14 | } 15 | } 16 | class OperationMultiply { 17 | doOperation(num1, num2) { 18 | return num1 * num2; 19 | } 20 | } 21 | ``` 22 | 最后定义根据策略来变更行为的上下文 23 | ```js 24 | class Context { 25 | constructor(strategy){ 26 | this.strategy = strategy; 27 | } 28 | executeStrategy(num1, num2){ 29 | return this.strategy.doOperation(num1, num2); 30 | } 31 | } 32 | ``` 33 | 那么根据策略的不同改变了上下文执行的行为 34 | ```js 35 | let context = new Context(new OperationAdd()); 36 | console.log("10 + 5 = " + context.executeStrategy(10, 5)); 37 | 38 | context = new Context(new OperationSubstract()); 39 | console.log("10 - 5 = " + context.executeStrategy(10, 5)); 40 | 41 | context = new Context(new OperationMultiply()); 42 | console.log("10 * 5 = " + context.executeStrategy(10, 5)); 43 | /** 44 | * output: 45 | * 10 + 5 = 15 46 | * 10 - 5 = 5 47 | * 10 * 5 = 50 48 | */ 49 | ``` 50 | # 策略模式的优势 51 | 策略和策略之间实现解耦,和状态模式类似可以通过此来消除一些if...else语句。 52 | 53 | [上一页(空对象模式)](../null-object-pattern/README.md) 54 | 55 | [下一页(模板模式)](../template-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/strategy-pattern/index.js: -------------------------------------------------------------------------------- 1 | class OperationAdd { 2 | doOperation(num1, num2) { 3 | return num1 + num2; 4 | } 5 | } 6 | class OperationSubstract { 7 | doOperation(num1, num2) { 8 | return num1 - num2; 9 | } 10 | } 11 | class OperationMultiply { 12 | doOperation(num1, num2) { 13 | return num1 * num2; 14 | } 15 | } 16 | 17 | class Context { 18 | constructor(strategy){ 19 | this.strategy = strategy; 20 | } 21 | executeStrategy(num1, num2){ 22 | return this.strategy.doOperation(num1, num2); 23 | } 24 | } 25 | 26 | let context = new Context(new OperationAdd()); 27 | console.log("10 + 5 = " + context.executeStrategy(10, 5)); 28 | 29 | context = new Context(new OperationSubstract()); 30 | console.log("10 - 5 = " + context.executeStrategy(10, 5)); 31 | 32 | context = new Context(new OperationMultiply()); 33 | console.log("10 * 5 = " + context.executeStrategy(10, 5)); 34 | /** 35 | * output: 36 | * 10 + 5 = 15 37 | * 10 - 5 = 5 38 | * 10 * 5 = 50 39 | */ -------------------------------------------------------------------------------- /design-pattern/template-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 模板模式(Template Pattern) 2 | 当一个类型公开了它的执行方式,其它类型只需按需实现的的时候可以使用这个模式。 3 | 4 | # 模板模式的实例 5 | 实现游戏的基类,同时它的执行方式play不允许被子类修改。 6 | ```js 7 | class Game { 8 | constructor() { 9 | if(this.play!= Game.prototype.play) { 10 | throw new Error("play mothed is final,can't be modify!"); 11 | } 12 | } 13 | initialize(){}; 14 | startPlay(){}; 15 | endPlay(){}; 16 | 17 | play(){ 18 | //初始化游戏 19 | this.initialize(); 20 | //开始游戏 21 | this.startPlay(); 22 | //结束游戏 23 | this.endPlay(); 24 | } 25 | } 26 | ``` 27 | 子类只需要通过基类流程中的方法即可 28 | ```js 29 | class Cricket extends Game { 30 | endPlay() { 31 | console.log("Cricket Game Finished!"); 32 | } 33 | initialize() { 34 | console.log("Cricket Game Initialized! Start playing."); 35 | } 36 | startPlay() { 37 | console.log("Cricket Game Started. Enjoy the game!"); 38 | } 39 | } 40 | 41 | class Football extends Game { 42 | endPlay() { 43 | console.log("Football Game Finished!"); 44 | } 45 | initialize() { 46 | console.log("Football Game Initialized! Start playing."); 47 | } 48 | startPlay() { 49 | console.log("Football Game Started. Enjoy the game!"); 50 | } 51 | } 52 | ``` 53 | 最终都是通过play进行运行 54 | ```js 55 | let game = new Cricket(); 56 | game.play(); 57 | console.log(''); 58 | game = new Football(); 59 | game.play(); 60 | /** 61 | * output: 62 | * Cricket Game Initialized! Start playing. 63 | * Cricket Game Started. Enjoy the game! 64 | * Cricket Game Finished! 65 | * 66 | * Football Game Initialized! Start playing. 67 | * Football Game Started. Enjoy the game! 68 | * Football Game Finished! 69 | */ 70 | ``` 71 | # 模板模式的优势 72 | 只需要关注自己功能的实现,而不需要着眼整个流程。 73 | 74 | [上一页(策略模式)](../strategy-pattern/README.md) 75 | 76 | [下一页(访问者模式)](../visitor-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/template-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Game { 2 | constructor() { 3 | if(this.play!= Game.prototype.play) { 4 | throw new Error("play mothed is final,can't be modify!"); 5 | } 6 | } 7 | initialize(){}; 8 | startPlay(){}; 9 | endPlay(){}; 10 | 11 | play(){ 12 | //初始化游戏 13 | this.initialize(); 14 | //开始游戏 15 | this.startPlay(); 16 | //结束游戏 17 | this.endPlay(); 18 | } 19 | } 20 | 21 | class Cricket extends Game { 22 | endPlay() { 23 | console.log("Cricket Game Finished!"); 24 | } 25 | initialize() { 26 | console.log("Cricket Game Initialized! Start playing."); 27 | } 28 | startPlay() { 29 | console.log("Cricket Game Started. Enjoy the game!"); 30 | } 31 | } 32 | 33 | class Football extends Game { 34 | endPlay() { 35 | console.log("Football Game Finished!"); 36 | } 37 | initialize() { 38 | console.log("Football Game Initialized! Start playing."); 39 | } 40 | startPlay() { 41 | console.log("Football Game Started. Enjoy the game!"); 42 | } 43 | } 44 | 45 | let game = new Cricket(); 46 | game.play(); 47 | console.log(''); 48 | game = new Football(); 49 | game.play(); 50 | /** 51 | * output: 52 | * Cricket Game Initialized! Start playing. 53 | * Cricket Game Started. Enjoy the game! 54 | * Cricket Game Finished! 55 | * 56 | * Football Game Initialized! Start playing. 57 | * Football Game Started. Enjoy the game! 58 | * Football Game Finished! 59 | */ -------------------------------------------------------------------------------- /design-pattern/transfer-object-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 传输对象模式(Transfer Object Pattern) 2 | 是一种业务对象传输的实践表达。 3 | 4 | # 传输对象模式的实例 5 | 定义值对象类 6 | ```js 7 | class StudentVO { 8 | constructor(name, rollNo){ 9 | this.name = name; 10 | this.rollNo = rollNo; 11 | } 12 | getName() { 13 | return this.name; 14 | } 15 | setName(name) { 16 | this.name = name; 17 | } 18 | getRollNo() { 19 | return this.rollNo; 20 | } 21 | setRollNo(rollNo) { 22 | this.rollNo = rollNo; 23 | } 24 | } 25 | ``` 26 | 定义值对象对应的业务对象 27 | ```js 28 | class StudentBO{ 29 | constructor(){ 30 | //列表是当作一个数据库 31 | this.students = []; 32 | this.students.getIndexByRollNo = (rollNo)=>{ 33 | return this.students.findIndex( 34 | (val)=>val.getRollNo() == rollNo 35 | ); 36 | } 37 | const student1 = new StudentVO("Robert",0); 38 | const student2 = new StudentVO("John",1); 39 | this.students.push(student1); 40 | this.students.push(student2); 41 | } 42 | deleteStudent(student) { 43 | this.students.splice(student.getIndexByRollNo(student.getRollNo() ),1); 44 | console.log("Student: Roll No " + student.getRollNo() 45 | +", deleted from database"); 46 | } 47 | //从数据库中检索学生名单 48 | getAllStudents() { 49 | return this.students; 50 | } 51 | getStudent(rollNo) { 52 | return this.students[this.students.getIndexByRollNo(rollNo)]; 53 | } 54 | 55 | updateStudent(student) { 56 | this.students[this.students.getIndexByRollNo(student.getRollNo())].setName(student.getName()); 57 | console.log("Student: Roll No " + student.getRollNo() 58 | +", updated in the database"); 59 | } 60 | } 61 | ``` 62 | 获取全部业务对象,并更新传输对象 63 | ```js 64 | const studentBusinessObject = new StudentBO(); 65 | 66 | //输出所有的学生 67 | for (const student of studentBusinessObject.getAllStudents()) { 68 | console.log("Student: [RollNo : " 69 | +student.getRollNo()+", Name : "+student.getName()+" ]"); 70 | } 71 | 72 | //更新学生 73 | const student =studentBusinessObject.getAllStudents()[ 74 | studentBusinessObject.getAllStudents().getIndexByRollNo(0) 75 | ]; 76 | student.setName("Michael"); 77 | studentBusinessObject.updateStudent(student); 78 | 79 | //获取学生 80 | studentBusinessObject.getStudent(0); 81 | console.log("Student: [RollNo : " 82 | +student.getRollNo()+", Name : "+student.getName()+" ]"); 83 | ``` 84 | # 传输对象模式的优势 85 | 代码看起来和数据访问对象模式很像,但是表达的意思是完全不一样的。传输对象模式表达的是一种传输模型,数据访问对象模式表达的是模型层和操作层的分离。 -------------------------------------------------------------------------------- /design-pattern/transfer-object-pattern/index.js: -------------------------------------------------------------------------------- 1 | class StudentVO { 2 | constructor(name, rollNo){ 3 | this.name = name; 4 | this.rollNo = rollNo; 5 | } 6 | getName() { 7 | return this.name; 8 | } 9 | setName(name) { 10 | this.name = name; 11 | } 12 | getRollNo() { 13 | return this.rollNo; 14 | } 15 | setRollNo(rollNo) { 16 | this.rollNo = rollNo; 17 | } 18 | } 19 | 20 | class StudentBO{ 21 | constructor(){ 22 | //列表是当作一个数据库 23 | this.students = []; 24 | this.students.getIndexByRollNo = (rollNo)=>{ 25 | return this.students.findIndex( 26 | (val)=>val.getRollNo() == rollNo 27 | ); 28 | } 29 | const student1 = new StudentVO("Robert",0); 30 | const student2 = new StudentVO("John",1); 31 | this.students.push(student1); 32 | this.students.push(student2); 33 | } 34 | deleteStudent(student) { 35 | this.students.splice(student.getIndexByRollNo(student.getRollNo() ),1); 36 | console.log("Student: Roll No " + student.getRollNo() 37 | +", deleted from database"); 38 | } 39 | //从数据库中检索学生名单 40 | getAllStudents() { 41 | return this.students; 42 | } 43 | getStudent(rollNo) { 44 | return this.students[this.students.getIndexByRollNo(rollNo)]; 45 | } 46 | 47 | updateStudent(student) { 48 | this.students[this.students.getIndexByRollNo(student.getRollNo())].setName(student.getName()); 49 | console.log("Student: Roll No " + student.getRollNo() 50 | +", updated in the database"); 51 | } 52 | } 53 | 54 | const studentBusinessObject = new StudentBO(); 55 | 56 | //输出所有的学生 57 | for (const student of studentBusinessObject.getAllStudents()) { 58 | console.log("Student: [RollNo : " 59 | +student.getRollNo()+", Name : "+student.getName()+" ]"); 60 | } 61 | 62 | //更新学生 63 | const student =studentBusinessObject.getAllStudents()[ 64 | studentBusinessObject.getAllStudents().getIndexByRollNo(0) 65 | ]; 66 | student.setName("Michael"); 67 | studentBusinessObject.updateStudent(student); 68 | 69 | //获取学生 70 | studentBusinessObject.getStudent(0); 71 | console.log("Student: [RollNo : " 72 | +student.getRollNo()+", Name : "+student.getName()+" ]"); -------------------------------------------------------------------------------- /design-pattern/visitor-pattern/README.md: -------------------------------------------------------------------------------- 1 | # 访问者模式(Visitor Pattern) 2 | 即根据访问者不同,所展示的行为也不同。 3 | 4 | # 访问者模式的实例 5 | 首先我们定义一组设备 6 | ```js 7 | class Keyboard { 8 | accept(computerPartVisitor) { 9 | computerPartVisitor.visit(this); 10 | } 11 | } 12 | class Monitor { 13 | accept(computerPartVisitor) { 14 | computerPartVisitor.visit(this); 15 | } 16 | } 17 | class Mouse { 18 | accept(computerPartVisitor) { 19 | computerPartVisitor.visit(this); 20 | } 21 | } 22 | ``` 23 | 定义电脑为一种设备,同时集成了其它设备 24 | ```js 25 | class Computer { 26 | constructor(){ 27 | this.parts = [new Mouse(), new Keyboard(), new Monitor()]; 28 | } 29 | accept(computerPartVisitor) { 30 | for (let i = 0; i < this.parts.length; i++) { 31 | this.parts[i].accept(computerPartVisitor); 32 | } 33 | computerPartVisitor.visit(this); 34 | } 35 | } 36 | ``` 37 | 定义访问者接口 38 | ```js 39 | 40 | class ComputerPartDisplayVisitor{ 41 | visit(device) { 42 | console.log(`Displaying ${device.constructor.name}.`); 43 | } 44 | } 45 | ``` 46 | 在使用的时候都只需要用设备接受新的访问者即可实现对应访问者的功能 47 | ```js 48 | const computer = new Computer(); 49 | computer.accept(new ComputerPartDisplayVisitor()); 50 | /** 51 | * output: 52 | * Displaying Mouse. 53 | * Displaying Keyboard. 54 | * Displaying Monitor. 55 | * Displaying Computer. 56 | */ 57 | ``` 58 | # 访问者模式的优势 59 | 如上类似设备这个东西是一个相对稳定的结构,而访问者要实现的功能又是非常不确定的,那么针对不同访问者,都可以对相同的设备进行不同的输出。其次只需要暴露特定接口,而相对稳定的设备不需要考虑接口中实现的内容。 60 | 61 | [上一页(模板模式)](../template-pattern/README.md) -------------------------------------------------------------------------------- /design-pattern/visitor-pattern/index.js: -------------------------------------------------------------------------------- 1 | class Keyboard { 2 | accept(computerPartVisitor) { 3 | computerPartVisitor.visit(this); 4 | } 5 | } 6 | class Monitor { 7 | accept(computerPartVisitor) { 8 | computerPartVisitor.visit(this); 9 | } 10 | } 11 | class Mouse { 12 | accept(computerPartVisitor) { 13 | computerPartVisitor.visit(this); 14 | } 15 | } 16 | 17 | class Computer { 18 | constructor(){ 19 | this.parts = [new Mouse(), new Keyboard(), new Monitor()]; 20 | } 21 | accept(computerPartVisitor) { 22 | for (let i = 0; i < this.parts.length; i++) { 23 | this.parts[i].accept(computerPartVisitor); 24 | } 25 | computerPartVisitor.visit(this); 26 | } 27 | } 28 | 29 | class ComputerPartDisplayVisitor{ 30 | visit(device) { 31 | console.log(`Displaying ${device.constructor.name}.`); 32 | } 33 | } 34 | 35 | const computer = new Computer(); 36 | computer.accept(new ComputerPartDisplayVisitor()); 37 | /** 38 | * output: 39 | * Displaying Mouse. 40 | * Displaying Keyboard. 41 | * Displaying Monitor. 42 | * Displaying Computer. 43 | */ --------------------------------------------------------------------------------