设计模式与原则精要
设计原则
单一职责原则- SRP
开放封闭原则 - OCP
里氏替换原则 - LSP
接口隔离原则 - ISP
依赖倒转原则 - DIP
迪米特法则 - DP
合成复用原则 - CSP
什么时候去解耦
如果两个职责在修改的时候,都是同时变化,就不必解耦开;
如果未来可能会独立复用,可以考虑单一职责。
单一职责原则- SRP
class UserOptions {//修改用户的名称、修改用户的密码updateUserInfo(user, type) {if (type === "username") {this.user.username = user.username;} else if (type === "password") {this.user.password = user.password;}}
}
const userOpts = new UserOptions();
userOpts.updateUserInfo({ username: '张三' }, "username");class UserOptionsSRP {updateUserName(username) {this.user.username = username;}updatePassword(password) {this.user.password = password;}
}
开放封闭原则 - OCP基础

里氏替换原则 - LSP
在设计的子类的时候,可以完整地继承父类的职责,而不是去修改。
组合的方式,可能会优于继承;
多考虑多态逻辑的实现和应用。
接口隔离原则 - ISP
接口隔离原则
意义和目的是,尽量减少接口之间的耦合存在,在大型的软件架构设计中,多个接口的组合使用,好过一个大而全的接口。1接口尽量小;
2接口要高内聚
依赖倒转原则 - DIP
依赖倒转原则的核心,是减少高层模块和底层模块之间的耦合。
高层模块不应该直接依赖底层模块,两者都应该依赖于抽象
抽象不应该依赖于细节,细节应该依赖于抽象。
设计模式
创建型:工厂、单例、建造者
结构型:装饰器模式、适配器模式、代理模式
行为型:观察者模式、发布订阅模式、策略模式
构造器模式
function Employee(name, age) {this.name = name;this.age = age;this.say = function () {console.log(`我的名字是${this.name},今年${this.age}岁`);};
}var employee1 = new Employee("小王", 100);
var employee2 = new Employee("tiechui", 18);console.log(employee1, employee2);
employee1.say();
employee2.say();
原型模式
function Employee(name, age) {this.name = name;this.age = age;
}Employee.prototype.say = function () {console.log(`我的名字是${this.name},今年${this.age}岁`);
};var employee1 = new Employee("小王", 100);
var employee2 = new Employee("tiechui", 18);console.log(employee1, employee2);
employee1.say();
employee2.say();
class Employee { constructor(name, age) {this.name = name;this.age = age;}say() {console.log(`我的名字是${this.name},今年${this.age}岁`);}
}var employee1 = new Employee("小王", 100);
var employee2 = new Employee("tiechui", 18);console.log(employee1, employee2);
employee1.say();
employee2.say();
建造者模式
function Car() {};Car.prototype.init = function (door,options,color,bland
) {this.initDoor(door).initOptions(options).initColor(color).initBland(bland)
}Car.prototype.initDoor = function (door) {this.door = door;return this;
}Car.prototype.initOptions = function (options) {this.options = options;return this;
}Car.prototype.initColor = function (color) {this.color = color;return this;
}Car.prototype.initBland = function (bland) {this.bland = bland;return this;
}const MyCar = new Car();
MyCar.initDoor(4).initOptions(['ABS', 'GPS']).initColor('white').initBland('Benz');
console.log(MyCar);
工厂模式
当满足以下任一条件时,即可考虑使用:
对象创建步骤多、逻辑复杂;
需根据条件动态选择创建的对象类型;
希望统一管理对象的生命周期(如复用、监控);
需降低代码对具体类的依赖,便于后续扩展或替换

简单工厂
es5
function UserFactory(role) {function User(role, pages) {this.role = role;this.pages = pages;}switch (role) {case 'superadmin':return new User('superadmin', ['home', 'user-manage', 'right-manage', 'news-manage']);case 'admin':return new User('admin', ['home', 'user-manage', 'news-manage']);case 'editor':return new User('editor', ['home', 'news-manage']);default:throw new Error('参数错误');}
}var user = UserFactory('superadmin');
console.log(user);
es6
class User {constructor(role, pages) {this.role = role;this.pages = pages;}static UserFactory(role) {switch (role) {case 'superadmin':return new User('superadmin', ['home', 'user-manage', 'right-manage', 'news-manage']);case 'admin':return new User('admin', ['home', 'user-manage', 'news-manage']);case 'editor':return new User('editor', ['home', 'news-manage']);default:throw new Error('参数错误');}}
}var user = User.UserFactory('superadmin');
console.log(user);
抽象工厂
假如对于实际的生产来说,我的BYD和dongfeng不可能是一个工厂生产
class AbstratFacrtory {createCar() {};createTruck() {};
};class Car {constructor() {this.door = 4;this.options = ['drive', 'drived'];}
};class Truck {constructor() {this.door = 2;this.options = ['drive', 'pullGoods'];}
};class DongfengCar extends Car { constructor(color) {super();this.bland = 'dongfeng';this.color = color;}}class BYDCar extends Car { constructor(color) {super();this.bland = 'byd';this.color = color;}}class DongfengTruck extends Truck { constructor() {super();this.bland = 'dongfeng';}}class BYDTruck extends Truck { constructor() {super();this.bland = 'byd';}}class DongfengFactory {createCar(color) {return new DongfengCar(color);}createTruck() {return new Truck();}
}class BYDFactory {createCar(color) {return new BYDCar(color);}createTruck() {return new BYDTruck();}
}const myFactory = new BYDFactory();
const myCar = myFactory.createCar('blue');
console.log(myCar);
class User {constructor(name, role, pages) {this.name = name;this.role = role;this.pages = pages;}welcome() {console.log(`欢迎回来`, this.name);}dataShow() {throw new Error('抽象方法需要被实现');}
}class SuperAdmin extends User {constructor(name) {super(name, 'superadmin', ['home', 'user-manage', 'right-manage', 'news-manage']);}dataShow() {console.log(`superadmin-datashow`);}addRight() {}addUser() {}
}class Admin extends User {constructor(name) {super(name, 'admin', ['home', 'user-manage', 'news-manage']);}dataShow() {console.log(`superadmin-datashow`);}addRight() {}addUser() {}
}class Editor extends User {constructor(name) {super(name, 'editor', ['home', 'news-manage', 'new-manage']);}dataShow() {console.log(`superadmin-datashow`);}addRight() {}addUser() {}
}function getAbstratctUserFactory(role) { switch (role) {case 'superadmin':return new SuperAdmin('superadmin');case 'admin':return new Admin('admin');case 'editor':return new Editor('editor');default:throw new Error('参数错误');}
}let user = getAbstratctUserFactory('superadmin');
console.log(user);
单例模式
是全局的,唯一的实例
好处
全局唯一
可以实现状态状态管理
坏处
副作用太大
不符合单一职责的原则。Vue项目中的Vue实例
Node项目中的App实例
Vuex React-Redux中的store
// ###########################################################构造函数的实现,实例在外部
let singleton;function Singleton() {if (!singleton) {singleton = this;}return singleton;
}Singleton.prototype.getName = function () {console.log('我是单例');
}let singleton1 = new Singleton();
let singleton2 = new Singleton();console.log(singleton1 === singleton2);// ###########################################################实例在内部
function Singleton2() {this.instance = null;
}Singleton2.getInstance = function () {if (!this.instance) {this.instance = new Singleton2();}
}Singleton2.prototype.getName = function () {console.log('我是单例');
}const single2A = Singleton2.getInstance();
const single2B = Singleton2.getInstance();
console.log(single2A === single2B);// #####################################################################使用闭包
const Singleton3 = (function () {let instance;function createInstance() {instance = '单例';return instance;}return {getInstance: function () {if (!instance) {instance = createInstance();}return instance;}}
})();const ins1 = Singleton3.getInstance();
const ins2 = Singleton3.getInstance();
console.log(ins1 === ins2);
es6
class Singleton {static instance;constructor() {if (!Singleton.instance) {Singleton.instance = this;}return Singleton.instance;}
}const instance1 = new Singleton();
const instance2 = new Singleton();console.log(instance1 === instance2);
代理模式
VUE
object.defineProperty
new Proxy();数据缓存的代理
localStorage的缓存 – localStorage优先使用
观察者模式
class Subject {constructor() {this.deps = [];// state发生改变时,通知所有的observerthis.state = 0;}attach(observer) {this.deps.push(observer);}setState(num) {this.state = num;this.notifyAllObservers();}notifyAllObservers() {this.deps.forEach(obs => obs.run(this.state))}
}class Observer { constructor(subject) {this.subject = subject;this.subject.attach(this);}run() {}
}class BinaryObserver extends Observer { constructor(subject) {super(subject);}run(data) {console.log(`hello, this is binaryObserver: ${data}`);}
}
class ArrayObserver extends Observer { constructor(subject) {super(subject);}run(data) {console.log(`hello, this is arrayObserver: ${data}`);}
}const subject = new Subject();
const bobs = new BinaryObserver(subject);
const aobs = new ArrayObserver(subject);subject.setState(15);
class Subject {constructor() {this.observers = [];}add(observer) {this.observers.push(observer);}remove(observer) {const index = this.observers.indexOf(observer);if (index !== -1) {this.observers.splice(index, 1);}}notify(data) {this.observers.forEach(obs => obs.update(data));}
}class Observer {constructor(name) {this.name = name;}update(data) {console.log(`${this.name} 收到数据:${data}`);}
}const subject = new Subject();
const observer1 = new Observer('张三');
const observer2 = new Observer('李四');subject.add(observer1);
subject.add(observer2);setTimeout(() => {subject.remove(observer1)
}, 1000);setTimeout(() => {subject.notify('Hello World!');
}, 1000);
发布订阅者模式
解耦代码

装饰器模式
保证原有函数功能不变的同时,增加一个新的功能(AOP面向切面编程)
ES和TypeScript的Decoratori语法
类装饰器,函数(方法)装饰器,属性装饰器实现方式:class、高阶函数

适配器模式
class Target {api() {console.log('this is the api');}
}class Adaptee { newLogic() {console.log('new logic');}
}class Adaptor extends Target { constructor(adaptee) {super();this.adaptee = adaptee;}api() {this.adaptee.newLogic();}
}const adaptor = new Adaptor(new Adaptee());
adaptor.api();
class TencentMap {show() {console.log('开始渲染腾讯地图');}
}class BaiduMap {display() {console.log('开始渲染百度地图');}
}class TencentAdapater extends TencentMap {constructor() {super();}display() {this.show();}
}function renderMap(map) {map.display();
}renderMap(new TencentAdapater());
renderMap(new BaiduMap());
策略模式
let strategry = {A: function (salary) {return salary * 4;},B: function (salary) {return salary * 3;},C: function (salary) {return salary * 2;}
}function calBonus(level, salary) {return strategry[level](salary);
}console.log(calBonus('A', 2000));
模块模式

桥接模式
桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。
使用场景:一个类存在两个或多个独立变化的维度,且这两个维度都需要进行扩展
优点:
把抽象与实现隔离开,有助于独立地管理各组成部分。
缺点:
每使用一个桥接元素都要增加一次函数调用,这对应用程序的性能会有一些负面影响一一提高了系统的复杂程度。
组合模式
命令模式
宏命令
class MacroCommand {constructor() {this.list = [] //子命令对象}add(command) {this.list.push(command)}execute() {for (let item of this.list) {item.execute()}}
}const Tabs = {execute(){console.log('选项卡执行');}
}const Swipe = {execute(){console.log('轮播执行');}
}const macroCommand = new MacroCommand()
macroCommand.add(Tabs)
macroCommand.add(Swipe)macroCommand.execute()
模版方法模式
var Container = function (params = {}) {var F = function () { };F.prototype.init = function () {this.getData();this.render();}F.prototype.getData = params.getData || function () {throw new Error('必须传入getData方法')}F.prototype.render = function () {console.log('render');}return F;
};var Myclass = Container({getData() {console.log('获取comingsoon');return [4, 5, 6]}
});
new Myclass().init();
迭代器模式
迭代器模式是指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭
代器模式可以把选代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,
也可以按顺序访问其中的每个元素。
1.为遍历不同数据结构的“集合”提供统一的接口;
2.能遍历访问“集合”数据中的项,不关心项的数据结构
var obj = {codeL: 200,name: 'kerwin',list: ['aaaa', 'bbbb', 'ccc'],[Symbol.iterator]: function () { var index = 0;return {next: () => {if (index < this.list.length) {return {value: this.list[index++],done: false}} else {return {value: undefined,done: true}}}}}
}for (let item of obj) {console.log(item)
}
职责链模式
使多个对象都有机会处理请求,从而避免了请求的发送者与多个接收者直接的耦合关系,将这些接收者连接
成一条链,顺着这条链传递该请求,直到找到能处理该请求的对象。优点:
1.符合单一职责,使每个方法中都只有一个职责。
2.符合开放封闭原则,在需求增加时可以很方便的扩充新的责任
3.使用时候不需要知道谁才是真正处理方法,减少大量的if或switch语法。
缺点:
1.团队成员需要对责任链存在共识,否则当看到一个方法莫名其妙的返回一个nxt时一定会很奇怪。
2.出错时不好排查问题,因为不知道到底在哪个责任中出的错,需要从链头开始往后找
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Document</title></head><body><input type="text" id="input" /><button id="btn">注册</button><script>btn.onclick = function () {checks.check();};function checkEmpty() {if (input.value.length === 0) {console.log("这里不能为空");return;}return "next";}function checkNumber() {if (Number.isNaN(+input.value)) {console.log("这里只能是数字");return;}return "next";}function checkLength() {if (input.value.length < 6) {console.log("长度不能小于6");return;}return "next";}class Chain {constructor(fn) {this.checkRule = fn;this.nextRule = null;}addRule(nextRule) {this.nextRule = new Chain(nextRule);return this.nextRule;}end() {this.nextRule = {check: () => "end"};}check() {this.checkRule() === "next" ? this.nextRule.check() : null;}}const checks = new Chain(checkEmpty);checks.addRule(checkNumber).addRule(checkLength).end();</script></body>
</html>