常用的设计模式(js) 笔记
工厂模式(三种)
简单工厂模式
- 特点:客户端需要知道传进去的参数,工厂函数内部有条件判断。
- 应用场景:适用于创建的对象类型少,逻辑简单的情况。因为工厂函数内部有对传参值的判断。
let factory = function(type){function Type(type){this.name = type.namethis.material = type.material}Type.prototype.produce = function(){console.log(`material:${this.material},name:${this.name}`)}switch (type.name) {case 'S':return new Type(type)break;case 'Q':return new Type(type)break;default:throw new Error('无法创建对象')break;}
}let s = factory({name:'S',material:'s'})
s.produce()
工厂方法模式
- 特点:工厂函数的内部不再关注生产产品的逻辑,而是生成工厂子类实例,所以重点被转移到了工厂子类上
- 应用场景:一个类通过子类来指定创建哪个对象。
class produceS{ //产品类的类名run(){console.log('生产S')}
}
class produceQ{run(){console.log('生产Q')}
}
class produceSFactory{ //对应的工厂produceChart(){return new produceS()}
}
class produceQFactory{produceChart(){return new produceQ()}
}let sObj = new produceSFactory() //生产出来的工厂类
let qObj = new produceQFactory()let sExample = sObj.produceChart()//工厂类生产出来的实例对象
let qExample = qObj.produceChart()sExample.run() //实例对象调用内部方法
qExample.run()
抽象工厂模式
- 特点:一个工厂是一个产品簇,可以生产一类产品,产品工厂类继承抽象工厂,用来规范产品工厂类。
- 应用场景:一个工厂(工厂函数)中可以生产多种产品,形成一个产品簇。
- 层次:抽象工厂类,工厂类,产品类
class AbstractFactory {//抽象类constructor() {if (new.target === AbstractFactory) {throw new error("抽象类不能实例化");}}run() {throw new error("run函数需要被重写");}
}
class ProduceS extends AbstractFactory{constructor(){super()//子类不调用super就没有this}//产品类(也是产品工厂类)run() {console.log("生产S");}
}
class ProduceQ extends AbstractFactory{constructor(){super()}run() {console.log("生产Q");}
}
class ProduceChartFactory {//工厂类produceSFactory() {return new ProduceS();}produceQFactory() {return new ProduceQ();}
}
let chartExample = new ProduceChartFactory();let sExample = chartExample.produceSFactory();
let qExample = chartExample.produceQFactory();sExample.run();
qExample.run();
单例模式
- 特点:一个类只能有一个实例对象,如果有class的实例对象就返回这个对象,没有就生成并保存下来
class LoginState {constructor(state) {this.state = state;}static example = null;hide() {if (this.state === "hide") {console.log("目前是隐藏状态");return;}this.state = "hide";console.log("已隐藏");}show(){if(this.state === 'show'){console.log("目前是显示状态");return;}this.state = "show";console.log("已显示");}static makeExample(state){if(!this.example){this.example = new LoginState(state)}return this.example}
}let obj1 = LoginState.makeExample('hide')
let obj2 = LoginState.makeExample('show')
console.log(obj1 === obj2)
obj1.show()
obj2.show()
建造者模式
- 特点:将生成对象和添加对象的属性或功能分离,拥有一定的顺序,可以根据需求调用方法给对象添加属性或方法,最后生成不同的对象
class MakeChar{makeNum(num){console.log(`制作数字${num}`)this.num = numreturn this}makeStr(str){console.log(`制作字符串${str}`)this.str = strreturn this}bulidFunc(){console.log(`制作了:${this.num},${this.str}`)}
}let obj = new MakeChar().makeNum(123).makeStr('qqq').bulidFunc()
适配器模式
- 特点:当两种类的数据格式不同时,添加适配器类将他们转为类似格式
class adaptee {selfFunc() {console.log("被适配者的函数");}
}class adaptor extends adaptee{constructor() {super();}publicFunc() {super.selfFunc();}
}
let obj = new adaptor();
obj.publicFunc();
观察者模式、发布与订阅模式
- 特点:都是一对多
- 区别:在观察者模式中,观察者与被观察者直接产生联系,当被观察者发生变化(触发事件)时,由被观察者直接通知观察者做出响应。但在发布订阅模式中,消息的订阅者与发布者没有直接联系,当消息需要发布时,由消息中心通知消息的订阅者做出响应,
观察者模式例子
//通常有一个被观察者、多个观察者 被观察者需要收集储存观察者 观察者需要有一个update函数 用来对观察者的动作做出响应
//观察者与被观察者 类比学生与老师 老师发出指令 学生响应//被观察者
class Teacher {constructor(name) {this.name = name;this.studentList = [];}//被观察者添加观察者add(student) {//如果列表中没有这个观察者就加进去let has = this.studentList.some((item) => item.code === student.code);if (!has) {this.studentList.push(student);}}//被观察者移除观察者remove(student) {let index = null;this.studentList.forEach((item, idx) => {if (student.code === item.code) {index = idx;}});this.studentList.splice(index, 1);console.log(`${this.name}取消了${student.name}的观察`)}//被观察者动作action(event, name) {this.studentList.forEach((student) => {if (student.update) {student.update(event, this.name);}});}
}//观察者
class Student {constructor(name, code) {this.name = name;this.code = code;}update(event, name) {console.log(`${name}进行了${event},${this.name}同学进行了响应`);}
}let zhang = new Teacher("张老师");let a = new Student("a", 111);
let b = new Student("b", 222);
let c = new Student("c", 333);zhang.add(a);
zhang.add(b);
zhang.add(c);zhang.action("点名");zhang.remove(b)zhang.action("起立");
发布与订阅模式例子
class eventEmit {constructor() {this.eventMap = new Map(); //事件map合集 用来储存事件名以及对应的回调函数}//订阅on(eventName, callback) {if (this.eventMap.has(eventName)) {this.eventMap.get(eventName).push(callback);} else {this.eventMap.set(eventName, [callback]);}}//发布emit(eventName, ...args) {let calls = this.eventMap.get(eventName);if (calls && calls.length) {calls.forEach((callback) => {callback.call(this, ...args);});} else {console.log("未找到对应事件");}}//取消订阅off(eventName,callback){let calls = this.eventMap.get(eventName)if(calls && calls.length){let filt = calls.filter(cb => cb === callback)this.eventMap.set(eventName,filt)}}
}//使用
let emitter = new eventEmit()
const cb1 = (a) => {console.log('回调函数1',a)
}
const cb2 = (a) => {console.log('回调函数2',a)
}emitter.on('test',cb1)
emitter.on('test',cb2)setTimeout(() => {emitter.emit('test','qqq')
}, 1000);//取消事件的订阅
setTimeout(() => {emitter.off('test')console.log('已经取消事件的订阅')emitter.emit('test','aaa')
}, 1500);
策略模式
- 特点:封装一系列算法,将算法的定义与使用分离开。需要了解策略规则用来定义算法内容。
//要求 根据员工等级发放基础工资的对应工资
//普通情况下
function getSalary(rank, salary) {if (rank === "S") {return salary * 5;} else if (rank === "A") {return salary * 4;} else if (rank === "B") {return salary * 3;}
}
//策略模式中
let strategy = {S: function (salary) {return salary * 5;},A: function (salary) {return salary * 4;},B: function (salary) {return salary * 3;},
};
function getSalary2(level, salary) {return strategy[level](salary);
}
// console.log(getSalary2('A',10000))
应用场景举例:表单的校验
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta http-equiv="X-UA-Compatible" content="IE=edge" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><form id="form">姓名:<input type="text" placeholder="姓名" id="name" />手机号:<input type="text" placeholder="手机号" id="phone"/></form><button id="btn">开始校验</button><script>let strategies = {isNotEmpty: function (value, msg) {if (value === "") {return msg;}},length: function (val, length, msg) {if (val.length < length) {return msg;}},};class Validata {constructor() {this.catch = []; //校验规则}//添加校验规则add(dom, rule, errorMsg) {//此处dom用来绑定thislet ary = rule.split(":");this.catch.push(function () {let strategy = ary.shift();ary.unshift(dom.value);ary.push(errorMsg);return strategies[strategy].apply(dom, ary);});}//开始校验start() {for (let i = 0; i < this.catch.length; i++) {let validatorFunc = this.catch[i];let msg = validatorFunc();if (msg) {return msg;}}}}let name = document.getElementById("name");let phone = document.getElementById("phone");let form = document.getElementById("form");let btn = document.getElementById("btn");let validataFunc = function () {let v = new Validata();v.add(form.children[0], "isNotEmpty", "不能为空");v.add(form.children[1], "length:11", "手机号长度不对");let msg = v.start();return msg;};btn.onclick = function () {let errMsg = validataFunc();if (errMsg) {console.log(errMsg);}};</script></body>
</html>
职责链模式
- 特点:将多个对象形成一条链,并且将请求按照这条链传递下去,直到有一个对象处理了这个请求
//需求:
//预付500定金 返100优惠
//预付200定金 返50优惠
//普通购买 有库存就卖出 没有就缺货
class Order500 {constructor() {this.rank = 1;}handle(orderRank, pay, stock) {if (orderRank === this.rank && pay) {return "预付500定金 返100优惠";} else {return "nextSuccessor";}}
}
class Order200 {constructor() {this.rank = 2;}handle(orderRank, pay, stock) {if (orderRank === this.rank && pay) {return "预付200定金 返50优惠";} else {return "nextSuccessor";}}
}
class nomalPay {constructor() {this.stock = 0;}handle(orderRank, pay, stock) {if (stock > this.stock) {return "普通购买";} else {return "库存不足";}}
}class Chain {constructor(order) {this.order = order;this.successor = null;}nextSuccessor(successor) {this.successor = successor;return this.successor;}passHandle(...args) {let res = this.order.handle.apply(this.order, args);if(res === 'nextSuccessor'){return this.successor && this.successor.passHandle.apply(this.successor,args)}else{return res}}
}
let chainOrder500 = new Chain(new Order500())
let chainOrder200 = new Chain(new Order200())
let chainNormal = new Chain(new nomalPay())chainOrder500.nextSuccessor(chainOrder200)
chainOrder200.nextSuccessor(chainNormal)console.log(chainOrder500.passHandle(1,true,100))
console.log(chainOrder500.passHandle(2,true,100))
console.log(chainOrder500.passHandle(1,false,100))
console.log(chainOrder500.passHandle(1,false,0))