【JavaScript设计模式】W3CSchool教程阅读笔记
Design Patterns
概念
- DRY(Dont Repeat Yourself)
- GoF(四人帮Gang of Four)提出23种设计模式
- 设计模式是用来设计面向对象的模式
- 面向对象三大特性:封装、继承、多态
- 接口:若干抽象方法的集合;对高层隐藏底层实现
- 面向对象设计SOLID原则
- 单一职责原则 Single Responsibility
- 一个类只负责一项职责
- 开放封闭原则 Open Closed
- 一个软件实体(类、模块、函数)应该开放扩展,关闭修改
- 软件实体尽量在不修改原有代码的基础上进行扩展
- 里式替换原则 Liskov Substitution
- 所有引用父类的地方必须能透明地使用其子类的对象
- 子类是特殊的父类,子类可以替换父类
- Square 算不算 Rectangle
- 接口隔离原则 Interface Segregation
- 使用多个专门的接口,而不是使用单一的接口
- 子类只实现他需要的方法
- 依赖倒置原则 Dependency Inversion
- 高层模块不应该依赖低层模块
- 二者都应该依赖其抽象
- 抽象不应该依赖细节,细节应该依赖抽象
- 针对接口编程,而不是针对实现编程
- 单一职责原则 Single Responsibility
Creational:5
name | description | Class/Object |
---|---|---|
Factory Method | 工厂方法 | Class |
Abstract Factory | 抽象工厂 | Object |
Builder | 建造者 | |
Prototype | 原型 | |
Singleton | 单例 |
Structural:7
name | description | Class/Object |
---|---|---|
Adapter | 适配器 | Class |
Bridge | 桥接 | |
Composite | 组合 | |
Decorator | 装饰器 | |
Facada | 外观 | |
Flyweight | 享元模式 | |
Proxy | 代理 |
Behavioral:11
name | description | Class/Object |
---|---|---|
Interpreter | 解释器 | Class |
Template Method | 模板方法 | |
Chain of Responsibility | 响应链 | Object |
Command | 命令 | |
Iterator | 迭代器 | |
Mediator | 中介者 | |
Observer | 观察者 | |
State | 状态 | |
Strategy | 策略 | |
Visitor | 访问者 |
JavaScript设计模式
1. 构造器模式Consructor
function Car(type, price){
this.type = type;
this.price = price;
this.toString = function(){
return this.type + ":" + this.price;
}
}
2. 模块化模式
- 命名空间
- 私有变量
// 购物车
var basketModule = (function(){
var basket = [];
return {
getItemCount: function(){
return basket.length;
},
addItem: function(item){
basket.push(item);
},
clear: function(){
basket.length = 0;
},
getTotal: function(){
var len = this.getItemCount();
var total = 0;
while(len--){
total += basket[len].price;
}
return total;
}
}
}())
- 私有成员扩展性不行。一次性定义完成,后续无法更改。
- 无法对私有变量打补丁
3. 暴露模块模式
- 启发式模块模式
- 为私有属性/成员提供公有方法/函数 set/get
- 无法为公有方法打补丁(在外部访问不到私有属性)
4. 单例模式
- 单例模式限制一个类只能有一个实例化对象
var mySingleton = (function(){
var Instance;
function init(){
var number = 10;
return {
publicMethod: function(){
return number;
}
}
}
return {
getInstance: function(){
if(!Instance){
Instance = init();
}
return Instance;
}
}
}())
5. 观察者模式
- 松耦合
// 被观察者
function Subject(){
this.state = 0;
this.observers = [];
}
Subject.prototype.addObserver = function(observer){
this.observers.push(observer);
}
Subject.prototype.addObservers = function(observers){
this.observers = this.observers.concat(observers);
}
Subject.prototype.removeObserver = function (observer){
let index = this.observers.indexOf(observer);
if(index > -1){
this.observers.splice(index, 1);
}
}
Subject.prototype.changeState = function(){
this.state++;
this.broadcast();
}
Subject.prototype.broadcast = function(){
this.observers.forEach(o => o.update(this.state))
}
// 观察者
function Observer(){
this.update = function(value){
console.log("观察到了:" + value);
}
}
Observer.prototype.changeUpdate = function(fun){
this.update = fun;
}
var observer = new Observer();
var subject = new Subject();
var ob = new Observer();
ob.changeUpdate(v => console.log("加倍:" + v * 2));
subject.addObservers([observer, ob]);
subject.changeState();
- 发布订阅,增加了中间件(Publisher -> Event Channel -> Subscriber)
- 不是一个对象直接调用另一个对象的方法,而是通过订阅另一个对象的一个特定的任务或活动,在这个任务或活动出现时得到通知
- 订阅者的功能崩溃,发布者无法感知
- 订阅者彼此之间没有感知
- 发布者和订阅者不需要互相了解,他们只需要在中间层消息代理(消息队列)的帮助下通信
- 在发布者/订阅者模式中,组件与观察者完全分离。在观察者模式中,主体和观察者松散耦合
- 观察者模式主要是已同步方式实现的,既当发生某些事件时,主题调用观察者的相关方法。调用权在被观察者,调用实现在观察者内部。
- 发布订阅模式主要以异步方式实现。最终处理权在订阅者。
// 调度中心
function EventChannel(){
this.events = {};
}
// 接受订阅者的注册信息, 记录需要推送的订阅者
EventChannel.prototype.addEventListener = function(type, handler){
if(!(this.events[type] instanceof Array)){
this.events[type] = [];
}
this.events[type].push(handler);
}
// 接受发布者的发布消息, 发布来自发布者的信息
EventChannel.prototype.publish = function(type, ...args){
if(this.events[type] instanceof Array){
this.events[type].forEach(h => h(args));
}
}
// 实例
var eventChannel = new EventChannel();
// 订阅者(一个函数,一个行为)
function subscriber(e){
console.log(e);
}
eventChannel.addEventListener('oneEvent', subscriber);
// 订阅者2
var sub = {
'oneEvent': function(e){
console.log('I received ' + e);
},
'anotherEvent': function(e){
console.log("It's time to do " + e);
}
}
eventChannel.addEventListener('oneEvent', sub.oneEvent);
eventChannel.addEventListener('anotherEvent', sub.anotherEvent);
// 发布者
var publisher = {
publish: function(type){
eventChannel.publish(type, Math.random());
}
}
publisher.publish('oneEvent');
publisher.publish('anotherEvent');
6. 中介者模式
- 中央集权?(消息转发者,中间层的权力下降,谁发送谁接受的决定权被分散到每个协同者中去,自行决定)
- 中介模式是观察者模式中的共享被观察者
- 降低通信复杂度,网状->星型
- 协同者向中介发送消息,最终处理权在于中介
- MVC
// 中介
function Mediator(){
this.colleagues = {};
}
Mediator.prototype.register = function(clg){
clg.mediator = this;
this.colleagues[clg.name] = clg;
return clg;
}
// 转发,将协同者发送来的消息转发给协同者指定的同事
Mediator.prototype.forward = function(msg, from, to){
if(to){
// 单发
to.receive(msg, from);
}else{
// 群发
for(let c in this.colleagues){
this.colleagues[c].receive(msg, from);
}
}
}
// 协同者
function Colleague(name){
this.name = name;
this.mediator = null;
}
Colleague.prototype.send = function(msg, to){
this.mediator.forward(msg, this, to);
}
Colleague.prototype.receive = function(msg, from){
console.log(from.name + ' to ' + this.name + ': ' + msg);
}
var mediator = new Mediator();
var A = mediator.register(new Colleague('A'));
var B = mediator.register(new Colleague('B'));
var C = mediator.register(new Colleague('C'));
var D = mediator.register(new Colleague('D'));
A.send('Just for B', B);
B.send('For all');
7. 原型模式
- 使用对象创建对象
- 对象直接继承自其他对象
var obj = Object.create(proto);
- 问题:hasOwnProperty()
8. 命令模式
- 消除事务性的不可分指令
- 把调用对象和实现操作的对象隔离开,将发出命令和执行命令的职责分开
Command
/ConcreteCommand
/Invoker
/Receiver
// 计算器
function add(a,b){return a + b;} // plus
function sub(a,b){return a - b;} // subtraction
function mul(a,b){return a * b;} // multiply
function div(a,b){return a / b;} // division
// 命令抽象类,接口,只提供方法名,不管具体实现
var Command = function(execute, undo, value){
this.execute = execute;
this.undo = undo;
this.value = value;
}
Command.prototype.log = function(flag){
console.log(this[flag].name + this.value);
}
// 命令具体类
var addCommand = function(value){
return new Command(add, sub, value);
}
var subCommand = function(value){
return new Command(sub, add, value);
}
var mulCommand = function(value){
return new Command(mul, div, value);
}
var divCommand = function(value){
return new Command(div, mul, value);
}
// 计算器类,模块模式
var Calculator = function(){
var current = 0;
var commands = []; // 命令队列
return {
execute: function(cmd){
current = cmd.execute(current, cmd.value);
commands.push(cmd);
cmd.log('execute');
},
undo: function(){
let cmd = commands.pop();
current = cmd.undo(current, cmd.value);
cmd.log('undo');
},
getCurrentValue: function(){
return current;
}
}
}
var calc = new Calculator();
calc.execute(addCommand(100));
9. 外观模式facade
- 门面模式
- 选择性的暴露方法
- Facade — Subsystem
- 为内部子系统简化沟通管道,隐藏内部细节
- 便利函数
- 经典应用: 兼容不同的浏览器API形成的方法
var addEvent = function(element, event, handler){
if(element.addEventListener){
element.addEveneListener(event, handler);
}else if (element.attachEvent){
element.attachEvent("on" + event, handler);
}else{
element["on" + event] = handler;
}
}
10. 工厂模式
// 内部实现,不暴露
function Warrior(){}
Warrior.prototype.attack = function(){
console.log('Close Attack');
}
function Mage(){}
Mage.prototype.attack = function(){
console.log('Magic Damage');
}
function Archer(){}
Archer.prototype.attack = function(){
conosle.log('Remote Attack');
}
// 外部,客户端,杂货铺
var CharacterFactory = {
createCharacter: function(type){
switch(type){
case 'Warrior': return new Warrior();
case 'Mage': return new Mage();
case 'Archer': return new Archer();
default: return new Warrior();
}
}
}
// 访问
var warrior = CharacterFactory.createCharacter('Warrior');
- 工厂方法
- Creator — Product
- 父类希望延迟创建到派生类
- 依赖倒置
// 各自类实现各自的工厂方法
var MageFactory = function(){}
MageFactory.prototype.createCharacter = function(){
return new Mage();
}
// 外部,客户端,专卖店
var mf = new MageFactory();
// 访问
var mage = mf.createCharacter();
- 抽象工厂
- 创建一系列相关产品
// 武器弓
function Bow(){}
// 工厂方法
var ArcherFactory = function(){}
ArcherFactory.prototype.createCharacter = function(){
return new Archer();
}
ArcherFactory.prototype.createWeapon = function(){
return new Bow();
}
// 不光卖鞋,顺带的衣服帽子全套
11. Mixin模式
- 织入模式
- 复用
// 可以被共享的方法
var myMixin = {
up: function(){
console.log('up');
},
down: function(){
console.log('down');
},
stop: function(){
console.log('stop');
}
}
// 待扩展的类/对象
function Car() {
this.left = function(){console.log('left');}
this.right = function(){console.log('right');}
}
// 1. 覆盖原型
Car.prototype = myMixin;
var car = new Car();
// 2. 增强原型
for(let key in myMixin){
Car.prototype[key] = myMixin[key];
}
12. 装饰模式
- 子类划分
- 向一个系统中现有的类动态添加行为的能力
- 装饰本身并不关心类的基础功能,只是将自身拷贝到超类之中
- 在一个简单基础对象上逐步添加能够 提供附加功能的 装饰对象
- 向一个基础对象添加(装饰)属性或方法
// 基础对象
function Hero(){
this.attackDamage = function(){
return 60;
}
this.abilityPower = function(){
return 20;
}
this.physicalResistance = function(){
return 30;
}
this.spellResistance = function(){
return 10;
}
}
// Decorator
function BFSword(hero){
var bfad = 35; // 暴风大剑提供35攻击
var ad = hero.attackDamage();
hero.attackDamage = function(){
return ad + bfad;
}
}
function UselessStick(hero){
var usap = 60; // 无用大棒提供60法强
var ap = hero.abilityPower();
hero.abilityPower = function(){
return ap + usap;
}
}
// 装饰
var Demacia_Power = new Hero();
Demacia_Power.attackDamage(); // 60
BFSword(Demacia_Power);
Demacia_Power.attackDamage(); // 95
BFSword(Demacia_Power);
Demacia_Power.attackDamage(); // 130
BFSword(Demacia_Power);
Demacia_Power.attackDamage(); // 165
BFSword(Demacia_Power);
Demacia_Power.attackDamage(); // 200
UselessStick(Demacia_Power);
Demacia_Power.abilityPower(); // 80
- 接口
- 抽象装饰者
- 装饰基础类,而不是生成许多类
13. 享元模式Flyweight
- 轻量级
- 优化重复、缓慢和低效数据共享
- 内部状态和外部状态
- 对象池