面向对象设计原则
- 单一职责原则
单一职责原则指的是,就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变化的原因。
如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能会有多个。面向对象设计鼓励将行为分布到细粒度的对象之中,如果一个对象承担的职责过多,等于把这些职责耦合到了一起,这种耦合会导致脆弱和低内聚设计。当变化发生时,设计可能会遭到破坏。
设计模式
- 单例模式
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点
如:线程池、全局缓存、全局事件总线、window对象等
function Single(age) {
this.age = age;
}
var createSingle = (function() {
var instance = null;
return function(age) {
if (!instance) instance = new Single(age);
return instance;
};
})();
var a = createSingle(12)
console.log(a) // Single{age:12}
var b = createSingle(13)
console.log(b) // Single{age:12}
console.log(a===b) // true
- 策略模式
定义:定义一系列的算法,把它们一个个封装起来,并且使他们可以相互替换。
策略模式的核心思想:策略模式至少包括两类。一组是策略类,策略类封装了具体的算法,并负责计算。第二组是环境类,环境接受客户的请求,随后把请求委托给某一个策略。同时也意味着,环境类要保留策略类的引用。
下面以表单验证为例:
/* 策略类 */
var strategies = {
isNonEmpty: function(value, errorMsg) {
if (value === "") {
return errorMsg;
}
},
minLength: function(value, length, errorMsg) {
if (value.length < length) {
return errorMsg;
}
},
isMobile:function(value,errorMsg){
if (!/^1[0-9]{9}$/.test(value)) {
return errorMsg
}
}
};
/* 环境类 */
var Validator = function(){
this.cache = [] // 保存校验规则集
}
/* 添加规则集 */
Validator.prototype.add = function(obj,rule,errorMsg){
var arr = rule.split(':') // 第一个是规则集,后面的为参数(如果有的话)
this.cache.push(function(){
var strategy = arr.shift(); // 弹出规则集 如果有参数则为剩余参数,如果没有则为空数组
arr.unshift(obj) // 添加校验值
arr.push(errorMsg) // 添加错误提示
return strategies[strategy](...arr) // 返回执行结果
})
}
/* 执行规则集 */
Validator.prototype.start = function(){
for (const item of this.cache) {
var msg = item()
if (msg) {
return msg
}
}
}
var formData = {
name:'tom',
password:'12345',
phoneNumber:'123123'
}
var checker = new Validator()
checker.add(formData.name,'isNonEmpty','姓名不能为空')
checker.add(formData.password,'minLength:6','密码不能少于6位')
checker.add(formData.phoneNumber,'isMobile','手机号不正确')
var checkerResult = checker.start()
console.log(checkerResult) // 密码不能少于6位
- 代理模式
定义:代理模式是为一个对象提供一个代用品或占位符来控制对对象的访问。
// 已缓存代理为例
var createCacheProxy = function(fn) {
var cache = {};
return function() {
var args = Array.prototype.join.call(arguments, ",");
if (args in cache) {
return cache[args];
}
return (cache[args] = fn.apply(this, arguments));
};
};
var getName = createCacheProxy(function(name){
console.log(name + Date.now())
})
getName('tom')
getName('tom')
getName('jerry')
- 迭代器模式
定义:迭代器模式是指提供一种方法顺序访问一个聚合对象的各个元素,而又不暴露该对象的内部表示。
Array.prototype.myEach = function(callback, thisArg) {
var __self = this;
for (let i = 0, len = __self.length; i < len; i++) {
var item = __self[i];
callback.call(thisArg || undefined, item, i);
}
};
var test = [1, 2, 4];
test.myEach(function(item, index) {
console.log(item, index);
});
// 1 0
// 2 1
// 4 2
- 发布-订阅模式
定义:发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
var Event = {
list: {},
listen: function(key, fn) {
if (!this.list[key]) {
this.list[key] = [];
}
this.list[key].push(fn);
},
trigger: function() {
var key = Array.prototype.shift.call(arguments),
fnlists = this.list[key];
// 如果没有订阅直接返回
if (!fnlists || fnlists.length === 0) {
return false;
}
for (const item of fnlists) {
item.apply(this, arguments);
}
},
remove: function(key, fn) {
var fns = this.list[key];
if (!fns) {
return false;
}
// 取消所有事件
if (!fn) {
fns.length && (fns.length = 0);
}
// 反向遍历 避免splice后跳过一个
for (let l = fns.length-1; l >= 0; l--) {
var item = fns[l];
if (item === fn) {
fns.splice(l, 1);
}
}
}
};
var a = function(text) {
console.log(text + ' world')
}
Event.listen("hello", a);
Event.trigger("hello",'hello')
Event.remove('hello',a)
Event.trigger("hello",'hello')
- 命令模式
命令模式中的命令指的是某些特定事情的指令。
命令模式最常见的场景是:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么。
这里举个栗子:学校决定举行才艺比赛,每班抽取一枚童鞋来比赛。
现在创建
setCommand = function(student){
return function(){
student.perform()
}
}
刚好某班小明会rap,就表演rap
// 小明会rap
var xiaoming = {
rap:function(){
console.log('鸡你太美')
},
perform:function(){
this.rap()
}
}
// 给小明下个命令
var command = setCommand(xiaoming)
// 小明执行了命令
command() // 鸡你太美
- 职责链模式
使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系,并将这些对象形成一条链,沿着这条链一直将请求传递,直到有一个对象处理它为止。
1.传统对象的链式
var Chain = function(fn){
this.successor = null
this.fn = fn
}
Chain.prototype.setNextSuccessor = function(successor){
return this.successor = successor
}
Chain.prototype.passRequest = function(){
var res = this.fn.apply(this,arguments)
if (!res) {
return this.successor.passRequest.apply(this.successor,arguments)
}
return res
}
Chain.prototype.done = function(){
this.successor = {
passRequest:function(){
console.log('the chain is finish')
return true
}
}
}
var check1 = new Chain(function(number){
if (number === 1) {
console.log('check1')
return true
}
return false
})
var check2 = new Chain(function(number){
if (number === 2) {
console.log('check2')
return true
}
return false
})
var check3 = new Chain(function(number){
if (number === 3) {
console.log('check3')
return true
}
return false
})
check1.setNextSuccessor(check2).setNextSuccessor(check3).done()
check1.passRequest(3)
2.AOP
Function.prototype.after = function(fn){
let self = this;
return function(){
let res = self.apply(this,arguments)
if (!res) {
return fn.apply(this,arguments)
}
return res
}
}
var check1 = function(number){
console.log('check1')
return number === 1
}
var check2 = function(number){
console.log('check2')
return number === 2
}
var check3 = function(number){
console.log('check3')
return number === 3
}
var go = check1.after(check2).after(check3)
go(1)
- 中介者模式
中介者模式的作用就是解除对象与对象之间的紧耦合关系。增加一个中介者对象,所有的对象都通过中介者来通信,而不是相互引用。当一个对象发生变化时,只需要通知中介者即可。
var PlaneFactory = function(name){
this.name = name
this.availableLine = ''
}
PlaneFactory.prototype.queryLine = function(){
this.availableLine = planeDirector.queryLine()
}
PlaneFactory.prototype.takeLine = function(){
planeDirector.takeLine(this,this.availableLine)
}
var planeDirector = (function(){
var lines = {
line1:null,
line2:null
}
// 查询哪个跑道可用
var queryLine = function(){
for (const i in lines) {
if (lines.hasOwnProperty(i)) {
if (lines[i] === null) return i
}
}
}
// 确认占用跑道
var takeLine = function(plane,line){
if (lines[line] === null) {
lines[line] = plane
}
console.log(lines)
}
return {
queryLine,
takeLine
}
})()
var plane1 = new PlaneFactory('tome')
var plane2 = new PlaneFactory('jerry')
plane1.queryLine()
plane1.takeLine()
plane2.queryLine()
plane2.takeLine()
【说明】文章属于 《JavaScript设计模式与开发实践》(曾探)学习笔记,并未包含所有的内容。有兴趣的同学可以自己去阅读,文章示例仅供参考(因为确实是有点懒,就随便写写)