设计模式目的
设计模式是为了更好的代码重用性,可读性,可靠性,可维护性。
设计六大原则
单一原则 (SRP): 实现类要职责单一,一个类只做一件事或者一类事,不要将功能无法划分为一类的揉到一起,答应我好吗
里氏替换原则(LSP): 不要破坏继承体系,子类可以完全替换掉他们所继承的父类,可以理解为调用父类方法的地方换成子类也可以正常执行调用,爸爸打下的江山儿子继位得无压力好吗
依赖倒置原则(DIP):我说下我的理解,如果某套功能或者业务逻辑可能之后会出现并行的另外一种模式或者较大的调整,那不如把这部分逻辑抽象出来,创建一个包含相关方法的抽象类,而实现类继承这个抽象类来重写抽象类中的方法,完成具体的实现,调用这些功能方法的类不需要关心自己调用的这些个方法的具体实现,只管调用这些抽象类中定义好的形式上的方法即可,不与实际实现这些方法的类发生直接依赖关系,方便之后的实现逻辑的替换更改;
接口隔离原则(ISP) : 在设计抽象类的时候要精简单一,白话说就是,A需要依赖B提供的一些方法,A我只用B的3个方法,B就尽量不要给A用不到的方法啦;
迪米特法则(LoD)降低耦合,尽量减少对象之间的直接的交互,如果其中一个类需要调用另一个类的某一个方法的话,可通过一个关系类发起这个调用,这样一个模块修改时,就可以最大程度的减少波及。
开放-封闭原则(OCP)告诉我们要对扩展开放,对修改关闭,你可以继承扩展我所有的能力,到你手里你想咋改咋改,但是,别 动我 本人 好吗?好的
设计模式分类 for example
创建型模式:工厂方法模式 单例模式、原型模式。
结构型模式:适配器模式、装饰器模式、代理模式、
行为型模式:观察者模式。
其实还有两类:并发型模式和线程池模式。
JS中的设计模式
1、工厂模式
常见的实例化对象模式,工厂模式就相当于创建实例对象的new,提供一个创建对象的接口
应用场景:JQuery中的$、Vue.component异步组件、React.createElement等
2、单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点,一般登录、购物车等都是一个单例。
应用场景:JQuery中的$、Vuex中的Store、Redux中的Store等
3、适配器模式
用来解决两个接口不兼容问题,由一个对象来包装不兼容的对象,比如参数转换,允许直接访问
应用场景:Vue的computed、旧的JSON格式转换成新的格式等
4、装饰器模式
在不改变对象自身的基础上,动态的给某个对象添加新的功能,同时又不改变其接口
利用AOP给函数动态添加功能,即Function的after或者before
Function.prototype.before = function (beforeFn) {
const _self = this;
return function () {
beforeFn.apply(this, arguments);
return _self.apply(this, arguments);
}
}
Function.prototype.after = function (afterFn) {
const _self = this;
return function () {
const ret = _self.apply(this, arguments);
afterFn.apply(this, arguments);
return ret;
}
}
let func = function () {
console.log('2');
}
func = func.before(function() {
console.log('1');
}).after(function() {
console.log('3');
})
func();
console.log(func()); // 依次打印 1 2 3
应用场景:ES7装饰器、Vuex中1.0版本混入Vue时,重写init方法、Vue中数组变异方法实现等
5、代理模式
为其他对象提供一种代理,便以控制对这个对象的访问,不能直接访问目标对象
class Flower {}
// 源对象
class Jack {
constructor (target) {
this.target = target;
}
sendFlower (target) {
const flower = new Flower();
this.target.receiveFlower(flower)
}
}
// 目标对象
class Rose {
receiveFlower (flower) {
console.log('收到花: ' + flower)
}
}
// 代理对象
class ProxyObj {
constructor () {
this.target = new Rose();
}
receiveFlower (flower) {
this.sendFlower(flower)
}
sendFlower (flower) {
this.target.receiveFlower(flower)
}
}
const proxyObj = new ProxyObj();
const jack = new Jack(proxyObj);
jack.sendFlower(proxyObj); // 收到花:[object Object]
应用场景:ES6 Proxy、Vuex中对于getters访问、图片预加载等
6、外观模式
为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更容易,不符合单一职责原则和开放封闭原则
应用场景:JS事件不同浏览器兼容处理、同一方法可以传入不同参数兼容处理
7、观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知
观察者模式 是松耦合关系 ;发布/订阅模式是解耦的 (有中间系统中心)
应用场景:JS事件、JS Promise、JQuery.$CallBack、Vue watch、NodeJS自定义事件,文件流等
观察者模式有两个重要的角色,即目标和观察者。在目标和观察者之间是没有事件通道的。
观察者模式有两个重要的角色,即目标和观察者。在目标和观察者之间是没有事件通道的。一方面,观察者要想订阅目标事件,由于没有事件通道,因此必须将自己添加到目标(Subject) 中进行管理;另一方面,目标在触发事件的时候,也无法将通知操作(notify) 委托给事件通道,因此只能亲自去通知所有的观察者。
订阅-发布模式
classPubSub {
constructor() {
this.subscribers = [];
}
subscribe(topic, callback) {
letcallbacks =this.subscribers[topic];
if(!callbacks) {
this.subscribers[topic] = [callback];
}
else{
callbacks.push(callback);
}
}
publish(topic, ...args) {
letcallbacks =this.subscribers[topic] || [];
callbacks.forEach(callback => callback(...args));
}} // 创建事件调度中心,为订阅者和发布者提供调度服务letpubSub =newPubSub();// A订阅了SMS事件(A只关注SMS本身,而不关心谁发布这个事件)pubSub.subscribe('SMS', console.log);// B订阅了SMS事件pubSub.subscribe('SMS', console.log);// C发布了SMS事件(C只关注SMS本身,不关心谁订阅了这个事件)pubSub.publish('SMS','I published `SMS` event');
观察者
classSubject {
constructor() { this.observers = []; }
add(observer) { this.observers.push(observer); }
notify(...args) {
this.observers.forEach(observer => observer.update(...args));
}}
classObserver {
update(...args) {
console.log(...args);
}}
// 创建观察者
ob1letob1 =newObserver();
// 创建观察者
ob2letob2 =newObserver();
// 创建目标subletsub =newSubject();
// 目标sub添加观察者ob1 (目标和观察者建立了依赖关系)
sub.add(ob1);
// 目标sub添加观察者
ob2sub.add(ob2);
// 目标sub触发SMS事件(目标主动通知观察者)
sub.notify('I fired `SMS` event');
8、迭代器模式
提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示
可分为:内部迭代器和外部迭代器
内部迭代器: 内部已经定义好迭代规则,外部只需要调用一次即可。
const each = (args, fn) => {
for (let i = 0, len = args.length; i < len; i++) {
const value = fn(args[i], i, args);
if (value === false) break;
}
}
应用场景: JQuery.each方法
外部迭代器:必须显示的请求迭代下一个元素。
// 迭代器
class Iterator {
constructor (list) {
this.list = list;
this.index = 0;
}
next () {
if (this.hasNext()) {
return this.list[this.index++]
}
return null;
}
hasNext () {
if (this.index === this.list.length) {
return false;
}
return true;
}
}
const arr = [1, 2, 3, 4, 5, 6];
const ite = new Iterator();
while(ite.hasNext()) {
console.log(ite.next()); // 依次打印 1 2 3 4 5 6
}
应用场景:JS Iterator、JS Generator
9、状态模式
关键是区分事物内部的状态,事物内部状态往往会带来事物的行为改变,即允许对象在内部状态发生改变时改变它的行为
// 红灯
class RedLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to red light');
this.state.setState(this.state.greenLight)
}
}
// 绿灯
class greenLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to green light');
this.state.setState(this.state.yellowLight)
}
}
// 黄灯
class yellowLight {
constructor (state) {
this.state = state;
}
light () {
console.log('turn to yellow light');
this.state.setState(this.state.redLight)
}
}
class State {
constructor () {
this.redLight = new RedLight(this)
this.greenLight = new greenLight(this)
this.yellowLight = new yellowLight(this)
this.setState(this.redLight) // 初始化为红灯
}
setState (state) {
this.currState = state;
}
}
const state = new State();
state.currState.light() // turn to red light
setInterval(() => {
state.currState.light() // 每隔3秒依次打印红灯、绿灯、黄灯
}, 3000)