Design Patterns

概念

  • DRY(Dont Repeat Yourself)
  • GoF(四人帮Gang of Four)提出23种设计模式
  • 设计模式是用来设计面向对象的模式
  • 面向对象三大特性:封装、继承、多态
  • 接口:若干抽象方法的集合;对高层隐藏底层实现
  • 面向对象设计SOLID原则
    • 单一职责原则 Single Responsibility
      • 一个类只负责一项职责
    • 开放封闭原则 Open Closed
      • 一个软件实体(类、模块、函数)应该开放扩展,关闭修改
      • 软件实体尽量在不修改原有代码的基础上进行扩展
    • 里式替换原则 Liskov Substitution
      • 所有引用父类的地方必须能透明地使用其子类的对象
      • 子类是特殊的父类,子类可以替换父类
      • Square 算不算 Rectangle
    • 接口隔离原则 Interface Segregation
      • 使用多个专门的接口,而不是使用单一的接口
      • 子类只实现他需要的方法
    • 依赖倒置原则 Dependency Inversion
      • 高层模块不应该依赖低层模块
      • 二者都应该依赖其抽象
      • 抽象不应该依赖细节,细节应该依赖抽象
      • 针对接口编程,而不是针对实现编程

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

  • 轻量级
  • 优化重复、缓慢和低效数据共享
  • 内部状态和外部状态
  • 对象池