什么是设计
- 按哪一种思路或者标准来实现功能;
- 功能相同,可以有不同设计的方式;
- 需求如果不断变化,设计的作用才能体现出来;
SOLID 五大设计原则
| 首字母 | 原则 | 概念 |
|---|---|---|
| S | 单一职责 | 「对象应该仅具有一种单一功能」的概念 |
| O | 开放封闭 | 「软件体应该是对于扩展开放的,但是对于修改封闭的」的概念 |
| L | 里氏替换 | 「程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的」的概念,参考契约式设计 |
| I | 接口隔离 | 「多个特定客户端接口要好于一个宽泛用途的接口」的概念 |
| D | 依赖反转 | 「一个方法应该遵从依赖于抽象而不是一个实例」的概念,依赖注入是该原则的一种实现方式 |
S 单一职责原则
- Single responsibility principle
- 一个程序只做好一件事
- 如果功能特别复杂就进行拆分
- bad
<body> <form id="userform" action=""> <input type="text" name="username" id="username" /> <input type="text" name="email" id="email" /> <button onclick="check()">提交</button> </form> </body> <script> function check() { let username = document.querySelector('#username').value; if (!username || username.length < 6 || username.length > 12) { return alert('用户名不合法'); } let email = document.querySelector('#email').value; if (!email || email.length < 6 || email.length > 12 || !email.includes('@')) { return alert('邮箱不合法'); } } </script> - good
<body> <form id="userform" action=""> <input type="text" name="username" id="username" data-validate="checkUsername" /> <input type="text" name="email" id="email" data-validate="checkEmail" /> <button onclick="check()" type="button">提交</button> </form> </body> <script> function check() { let form = document.querySelector('#userform'); let inputs = form.querySelectorAll('input'); for (let index = 0; index < inputs.length; index++) { let validate = inputs[index].dataset.validate; let validateFn = window[validate]; if (validateFn) { let error = validateFn(inputs[index]); if (error) { return alert(error); } } } } function checkUsername() { let username = document.querySelector('#username').value; if (!username || username.length < 6 || username.length > 12) { return alert('用户名不合法'); } } function checkEmail() { let email = document.querySelector('#email').value; if (!email || email.length < 6 || email.length > 12 || !email.includes('@')) { return alert('邮箱不合法'); } } </script>
O 开放封闭原则
- Open Closed Principle
- 对扩展开放,对修改关闭
- 增加需求时,扩展新代码,而非修改已有代码
- 这是软件设计的终极目标
- bad
export default function request(url, options) { return fetch(url, options) .then((response) => { if (response.status >= 200 && response.status < 300) { let data = response.data.json(); console.log(data) } const error = new Error(response.statusText); error.response = response; throw error; }) .catch((err) => ({ err })); } - good
function parseJSON(response) { return response.json(); } function checkStatus(response) { if (response.status >= 200 && response.status < 300) { return response; } const error = new Error(response.statusText); error.response = response; throw error; } export default function request(url, options) { return fetch(url, options) .then(checkStatus) .then(parseJSON) .then((data) => { // 每一个 then 都是一个功能(更优雅灵活) data; }) .catch((err) => ({ err })); }
L 里氏替换原则
- Liskov Substitution Principle
- 子类能覆盖父类
- 父类能出现的地方子类就能出现
- JS 使用比较少
- good
export { } class AbstractDrink { getPrice(): any { return 1; } sum(a: number, b: number) { return a + b; } } class CocaCola extends AbstractDrink { getPrice(): any { return '我是一瓶可口可乐'; } } class Fanta extends AbstractDrink { getPrice(): any { return 5; } } class Customer { // 里氏代换可以替换,任何可以传父类的地方,都可以把子类传进去 // 里氏代换是一个原则,要求子类不能违反父类的功能和规定 // 此处必须是 AbstractDrink,否则违背了原则 drink(abstractDrink: AbstractDrink) { console.log("花费" + abstractDrink.getPrice()); } } let c1 = new Customer(); c1.drink(new AbstractDrink()); c1.drink(new CocaCola()); c1.drink(new Fanta());
I 接口隔离原则
- Interface Segregation Principle
- 保持接口的单一独立,避免出现胖接口
- 类似于单一职责原则,更关注接口
- JS 中没有接口,使用较少
- bad
interface Running { run(): void; } interface Flying { fly(): void; } interface Swimming { swim(): void; } interface AutomobileInterface { run(): void; fly(): void; swim(): void } class Automobile implements AutomobileInterface { run() { } fly() { } swim() { } } - good
interface Running { run(): void; } interface Flying { fly(): void; } interface Swimming { swim(): void; } /** * 1.为了复用 * 2.为了低耦合 * 3.为了单一职责 */ class Automobile implements Running, Flying, Swimming { run() { } fly() { } swim() { } } class Car implements Running { run() { } }
D 依赖倒置原则
- Dependence Inversion Principle
- 面向接口编程,依赖于抽象而不依赖于具体实现
- 使用方只关注接口而不关注具体类的实现
- JS 中使用较少(没有接口,弱类型)
- good
interface Girlfriend { age: number; height: number; cook(): void } class LinChiling implements Girlfriend { age: number = 35; height: number = 178; cook() { console.log('泡面'); } } class HanMeimei implements Girlfriend { age: number = 35; height: number = 178; cook() { console.log('泡面'); } } class SingleDog { // Girlfriend 依赖抽象,而非依赖依赖具体的实现 constructor(public girlfriend: Girlfriend) { } } let dog1 = new SingleDog(new LinChiling()); let dog2 = new SingleDog(new HanMeimei());
23 种设计模式
创建型模式
- 工厂模式(工厂方法模式、抽象工厂模式、简单工厂模式)
- 建造者模式
- 单例模式
- 原型模式
结构型模式
- 适配器模式
- 装饰器模式
- 代理模式
- 外观模式
- 桥接模式
- 组合模式
- 享元模式
行为型模式
- 策略模式
- 模版方法模式
- 观察者模式
- 迭代器模式
- 职责链模式
- 命令模式
- 备忘录模式
- 状态模式
- 访问者模式
中介者模式解释器模式
vuex 中辅助函数的实现
上一篇