前奏
恰当的设计模式能够让代码变得更加容易维护,更加容易让人理解,同时也更加容易扩展,更新迭代。
一、设计模式基本原则
1、单一职责原则:一个功能一个方法只干一件事情。
当下几乎所有的计算机都遵循冯诺依曼结构,将计算机分为五大部分,各部分之间相互独立相互协调,共同构成计算机的基本构架。
(1)输入设备负责将信息输入到计算机中。
(2)存储器负责将各种数据存储到硬盘里,并提供访问的接口。
(3)运算器负责计算机的所有运算工作。
(4)控制器控制各部件之间的相互协调。
(5)输出设备负责将计算机的处理结果输出到指定的设备中。
这样做的好处就是能够最大程度的将复杂功能分离开来,使里面的功能能够最大化的复用, 同时减少模块间的耦合,便于更新迭代,逻辑也更加清晰。
function calculator(before, after, operator) {
if (operator === '+') {
return before + after
} else if (operator === '-') {
return before - after
} else if (operator === '*') {
return before * after
} else if (operator === '/') {
return before / after
}
}
class Operator {
add(before, after) {
return before + after
}
reduce(before, after) {
return before - after
}
multiply(before, after) {
return before * after
}
devise(before, after) {
return before / after
}
}
let operatorObj = {
'+': 'add',
'-': 'reduce',
'*': 'multiply',
'/': 'devise'
}
function calculator(before, after, operator) {
return new Operator()[operatorObj[operator]](before, after)
}
/**
* @func 获取活动详情列表
*/
getActivityDetailInfoList (detailInfoList) {
let { activityDetailInfoList } = this.data
activityDetailInfoList.push(this.dealActivityDetailInfoList(detailInfoList))
this.setData({
activityDetailInfoList
})
},
dealActivityDetailInfoList (activityDetailInfoList) {
for (let prop of activityDetailInfoList) {
if (prop.activityDetailType === 3 && typeof prop.activityDetail === 'string') {
prop.activityDetail = JSON.parse(prop.activityDetail)
}
}
return activityDetailInfoList
},
2、里氏代换原则: 任何基类可以出现的地方,他的子类一定也可以出现。
简单来说就是在继承的关系中,子类拥有父类的所有功能,并且功能具有相同的作用,也就是不去重写父类的方法。
在实际开发中不可避免的要去重写父类的方法,如果需要重写父类方法的时候就得考虑这个重写是否真的有必要,或者重新增加一个私有的方法来处理这类额外的场景。
3、依赖倒置原则: 只依赖接口,不依赖方法。
在继承中,父类仅提供一些空的方法,当继承发生后子类中定义该方法的具体细节。
class Person {
constructor(name, age) {
this.name = name
this.age = age
}
sayHello() {}
}
class Chinese extends Person {
constructor(name, age, tel) {
super(name, age);
this.tel = tel;
}
sayHello() {
console.log('你好!')
}
}
class American extends Person {
constructor(name, age, tel) {
super(name, age);
this.tel = tel;
}
sayHello() {
console.log('hello!')
}
}
let cPerson = new Chinese('中国人', 18, '123')
let aPerson = new American('外国人', 18, '456')
cPerson.sayHello()
aPerson.sayHello()
4、接口分离原则(接口最小化原则): 把大接口拆分成小接口,将功能拆分未许多小的功能 最终组成完整的功能。
一个对象该有的功能齐全,同时不引入不必要的功能, 比如汽车,需要有跑的功能,刹车功能,但是货箱只属于货车的功能, 就不能定义在汽车这个父类中,需要定义在货车的类中。
5、最小知道原则: 传入的参数越少越好 降低复杂度 耦合度。
6、开闭原则: 面对拓展开放,面对修改关闭。
通常只提供给外部拓展的接口,用于功能的拓展,封装类避免对类的修改操作,类似jquery的extend方法
(function () {
let myJquery = null
myJquery.extend = function (fn) {
myJquery[fn.name] = fn
}
context.jauery = myJquery
})(context)
听过很多道理,依然过不好这一生。
二、设计模式举例
1、单例模式
保证一个类仅有一个实例, 后台系统的账号,一次只会有一个账号
每次对象的创建的需要js向内存申请一片空间用来存储新的对象的空间,并且消耗一定的时间,单例模式能够一次创建终身使用,只会跟cpu产生一次联系,在内存中只会有一个固定的内存空间。
// 直接申明方式创建独一无二的对象
let singleObject = {}
// 利用构造函数属性实现单例
function PropObject(...props) {
if (PropObject.single) {
return PropObject.single
}
this.props = props
PropObject.single = this
}
// 修改构造函数指向实现单例
function SingleName(props) {
this.props = props
let instence = this
SingleName = function() {
return instence
}
}
let singlePerson = (() => {
let instence = null
return function (name, sex) {
if (instence) {
return instence
}
this.name = name
this.sex = sex
instence = this
}
})()
let singleFunc = (func) => {
let intence = null
return function () {
let args = arguments
if (instence) {
return instence
}
intence = new func.apply(this, args)
return intence
}
}
2、观察者模式
js事件机制就是典型的观察者模式,将事件的处理逻辑和事件的调用分离开来,可以方便的提前编写好很多具体的功能逻辑,用的时候只需要调用具体的方法即可。
事件机制中需要有几个步骤:
(1)需要有一个对象,这个对象有两个功能,第一是实现绑定事件的方法,能够将事件的执行方法保存起来;第二是要实现触发事件的方法,将已经保存起来的事件函数执行。
(2)在使用的时候,需要先创建这个对象,根据需要向对象中注册指定事件的方法,然后在需要的时候触发指定的事件,调用存储下来的执行函数。
借助Object类提供的新方法,在对象的seter和getter中能够实现处理方法的自动注入和存储,我们只需要通过修改对象的属性值,就可以触发对应的方法,达到目的。
let bValue
Object.defineProperty(this.data, 'assistStep', {
get : function(){
return bValue;
},
set : newValue => {
bValue = newValue;
console.log('---------', bValue)
this.updateAssistStep(bValue)
},
enumerable : true,
configurable : true
})
class Event {
constructor() {
this.cache = {}
}
on(type, handle) {
if (!this.cache[type]) {
this.cache[type] = [handle]
} else {
this.cache[type].push(handle)
}
}
emit(type, ...args) {
this.cache[type].forEach(ele => {
ele(...args)
})
}
}
let event = new Event()
event.on('run', () => {
console.log('run now')
})
event.emit('run')
3、代理模式
代理对象起到类似中介的作用, 会增加一些功能,也会去掉一下原有的功能
如果用户直接访问服务提供者,往往会出现很多问题,比如,服务状态变动频繁导致错误,高并发增加服务的负担,权限不足,安全问题等等。
如果用户不直接与服务提供方通信,而是通过接入第三方agent来间接与服务提供者通信,这样的话在agent中,就能够处理很多异常情况或者进行功能增强,不仅能够解决用户请求资源不稳定的问题,也能在一定程度上降低服务的压力,同时可以对请求和应答数据做不同程度的包装。
中介 ,快递员, 外卖员
代理的分类
远程代理:同时监控多个对象的状态,总店监控分店的状态
虚拟代理:将开销很大的对象,延迟到真正需要的时候执行, 图片上传 文件上传。
安全代理:访问权限
智能代理: 增加额外的功能 服务
......
let demander = {
recivedResponse: '',
contactSeller: function(target) {
console.log(target.responseDemander())
},
contactAgent: function(agent) {
agent.reciveProxy(this, seller, 800)
}
}
let seller = {
hasHouse: false,
responseDemander: function() {
return this.hasHouse ? 'ok' : 'no'
},
changeHasHouse: function() {
setInterval(() => {
this.hasHouse = Math.random() > 0.5
}, 400)
}
}
let agent = {
efficiency: 100,
listen: function(target) {
let agentTimer = setInterval(() => {
if (target.hasHouse) {
this.client.contactSeller(target)
clearInterval(agentTimer)
}
}, this.efficiency)
},
reciveProxy: function(client, target, fare) {
this.client = client
this.efficiency = 1000 - fare
this.listen(target)
}
}
seller.changeHasHouse()
demander.contactAgent(agent)
4、策略模式
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
提供不同的方法供调用的同时提供扩展接口增加新的方法,始终维护一个算法列表,控制列表中方法的新增和删除,用于完成不同的操作。
每一个方法就是一个执行策略,不同策略的组合能够完成复杂的逻辑判断,同时能够很方便的添加新的策略,同时策略之间互不干扰,相互独立。
class Validator {
constructor() {
this.strategies = {
isNonEmpty (value) {
return !!value.length
},
limitLength(value, minLength = 0, maxLength = 32) {
return value.length >= minLength && value.length <= maxLength
},
minLength(value, minLength) {
return value.length >= minLength
},
maxLength(value, maxLength) {
return value.length <= maxLength
},
regTest(value, reg) {
return reg.test(value)
}
}
this.cache = []
}
add(strategyName, ...args) {
// 还可以传入回调函数 处理策略方法的检测结果
this.cache.push(() => this.strategies[strategyName](...args))
}
start() {
let result = []
this.cache.forEach(ele => {
result.push(ele())
})
return result
}
extend(strategy) {
this.strategies[strategy.name] = strategy
}
}
5、工厂模式
工厂模式定义创建对象的接口,但是让子类去真正的实例化。也就是工厂方法将类的实例化延迟到子类。
类似一个汽车工厂,有生产汽车的功能,生产汽车的具体过程需要在不同的生产线上完成,汽车的不同部分也是一样需要在不同生产线甚至不同厂家完成。
比如一个汽车生产的工厂,里面包括生产不同汽车的流水线,用来生产不同的汽车,汽车工厂本身可能只是提供一些基础的私有的功能来对生产出来的汽车进行一些包装。
function CarFectory() {
this.run = function () {
console.log(this)
console.log(this.name + 'is running')
}
}
CarFectory.prototype.bus = function () {
this.name = 'bus'
}
CarFectory.prototype.truck = function () {
this.name = 'truck'
}
CarFectory.prototype.passengerar = function () {
this.name = 'passengerar'
}
CarFectory.prototype.creat = function (type) {
if (!CarFectory.prototype[type]) {
console.log('这里不生产这种车')
return null
}
if (CarFectory.prototype[type].prototype.__proto__ !== CarFectory) {
CarFectory.prototype[type].prototype = new CarFectory()
}
return new CarFectory.prototype[type]()
}
6、装饰者模式
在程序运行过程中动态的为对象新增一下方法和属性或者对已有的属性和方法进行修改增强,能够使得对象随着程序的执行,展现不同的特性。
在具体的实现中,属性的新增可以间接的通过执行方法来完成,所有主要是通过执行不同的方法来改变对象的特征。
CarFectory.prototype.decorate = function (decorate) {
this.decorateList.push(decorate)
}
CarFectory.prototype.doDecorates = function () {
this.decorateList.forEach(ele => {
this.decorates[ele] && this.decorates[ele]()
})
}
CarFectory.prototype.clearDecorate = function () {
this.decorateList = []
}
三、总结
知行合一,难点在知,践行其次!