参考文章
老话题:6个方法,检验你有没有正确使用设计模式
图说设计模式
结合开发中的实例,全面解读 JavaScript 设计模式
js中的设计模式
为啥学习设计模式
在系统的学习设计模式之后,我们需要达到3个层次:
- 能在白纸上画出所有的模式结构和时序图;
- 能用代码实现;如果模式的代码都没有实现过,是用不出来的;即所谓,看得懂,不会用;
- 灵活应用到工作中的项目中;
设计模式核心是面向对象的能力,而面向对象的很大特色就是:封装、继承、多态。绕不开的话题就是 提高代码复用性。
合理使用继承,在合适场景下多使用组合代替继承是良好的设计原则
学习面向对象最重要的是:深入理解面向对象的概念——封装、继承、多态的实现手段、目的。
在设计每个基类时候要仔细考虑它自身的特点,以及相关类的关系,才能合理地确定各个类之间的关系,是继承,还是组合,还是通过接口实现多态等等。
所有的模式说到底只是手段,实现类之间的交互才是目的。
继承是为了复用代码,但是——复用哪部分的代码?
- 复用基类代码,但不是主要目的
- 复用调用类方法的外部代码
设计原则
扩展阅读:对开发人员有用的定律、理论、原则和模式 https://github.com/nusr/hacker-laws-zh
单一职责原则
单一职责原则原则就是每个程序只负责做好一件事情,如果功能过于复杂就拆分开,每个部分保持独立
开放封闭原则
对扩展开放,对修改封闭
李氏置换原则
子类能够覆盖父类,父类能出现的地方,子类就可以出现
接口独立原则
保持接口的单一独立
依赖倒置原则
依赖倒置原则的含义是面向接口编程,依赖于抽象而不依赖于具体,使用方只关注接口而不关注具体类的实现
设计模式分类:
创建型
- 工厂模式(工厂方法模式、抽象工厂模式、建造者模式)、单例模式
组合型
- 适配器模式、装饰器模式、代理模式、外观模式
行为型
- 观察者模式、状态模式
设计模式
工厂模式
jq的$ ,无需要new直接就可以得到jq的实例
class jQuery {
constructor(selector) {
let slice = Array.prototype.slice
let dom = slice.call(document.querySelectorAll(selector))
let len = dom ? dom.length : 0
for(let i = 0;i < len; i++) {
this[i] = dom[i]
}
this.length = len
this.selector = selector || ''
}
append(node) {
}
html(data) {
}
//等等API
}
window.$ = function (selector) {
return new jQuery(selector)
}
适配器
适配器模式的含义是旧接口格式和使用者不兼容,中间加一个适配器接口。生活当中随处可见符合适配器模式的例子,如:插头转换器,电脑接口转换器。
vue的computed
<div id="example">
<p>Original message: "{{ message }}"</p>
<p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
el: '#example',
data: {
message: 'Hello'
},
computed: {
// 计算属性的 getter
reversedMessage: function () {
// `this` 指向 vm 实例
return this.message.split('').reverse().join('')
}
}
})
装饰器
装饰器模式,装饰我们可以理解为就是给一个东西装饰另外一些东西使其更好看,对应到前端中就是为对象添加新功能,并且不改变其原有的结构和功能,这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能
class Hero {
constructor(define = 10,attack = 20, blood = 20) {
this.init(define,attack,blood)
}
init(define,attack,blood) {
this.define = define;
this.attack = attack;
this.blood = blood;
}
toString() {
return `防御力: ${this.define},攻击力:${this.attack},血量:${this.blood}`
}
}
let houyi = new Hero();
console.log(`当前状态 ===> ${houyi}`)
//输出:当前状态 ===> 防御力:10,攻击力20,血量20
function decorateCloth(target,key,descriptor) {
const method= descriptor.value;
let moreDef = 100;
let ret;
descriptor.value = (...args) => {
args[0] += moreDef;
ret = method.apply(target,args);
return ret;
}
return descriptor;
}
class Hero {
constructor(define = 10,attack = 20, blood = 20) {
this.init(define,attack,blood)
}
@decorateCloth
init(define,attack,blood) {
this.define = define;
this.attack = attack;
this.blood = blood;
}
toString() {
return `防御力: ${this.define},攻击力:${this.attack},血量:${this.blood}`
}
}
let houyi = new Hero();
console.log(`当前状态 ===> ${houyi}`)
//输出:当前状态 ===> 防御力:110,攻击力20,血量20
代理模式
代理模式是使用者无权访问目标对象,中间加代理,通过代理做授权和控制
前端:事件代理
观察者
观察者模式就是只要你作为订阅者订阅了某个事件,当事件触发的时候,发布者就会通知你。这里可以类比我们去咖啡厅点咖啡,当我们点了咖啡之后,就可以去做别的事情,当咖啡完成时,服务员就会叫你来取,你到时候取走咖啡即可
网页事件绑定
nodejs的eventemitter
const EventEmitter = require('events').EventEmitter
const emitter1 = new EventEmitter();
emitter1.on('some',()=> {
//监听some事件
console.log('some event is occured 1')
})
emitter1.on('some',()=> {
//监听some事件
console.log('some event is occured 2')
})
emitter.emit('some')
状态模式
状态总数(state)是有限的。•任一时刻,只处在一种状态之中。•某种条件下,会从一种状态转变(transition)到另一种状态。
如:ES6中的promise、(promise是ES6新增的一个特性,它是异步编程的一种解决方案,比传统的解决方案更加方便,可以使用.then()操作避免了回调嵌套带来的回调地狱式的写法)