本文内容来自于JavaScript 设计模式核心原理与应用实践掘金小册。总结是为了让自己更好的学习。
设计模式的核心思想------封装变化。将变与不变分离,确保变化的部分灵活、不变的部分稳定。其实上研究生的时候看过设计的书,但当时技术水平有限,所以虽然对着书都敲了一遍,但其实应该是没有理解,因为没有足够多的项目经验,其实看了没啥用。过了这么多年重看,虽然有一些会在项目中用到,但用过的还是常见的单例模式、策略模式、观察者模式,其他就很少了。但是设计模式本来是后端总结的东西,应用到前端来其实并不会全部适应。那就掌握常用的,常考的吧,这部分还是有必要掌握的。
SOLID设计原则
首先说到设计模式,要先了解设计模式的原则。这几个原则中,感觉单一功能和开放封闭是最有用的,另外三个其实也没太理解。
"SOLID" 是由罗伯特·C·马丁在 21 世纪早期引入的记忆术首字母缩略字,指代了面向对象编程和面向对象设计的五个基本原则。
- 单一功能原则(Single Responsibility Principle)
- 开放封闭原则(Opened Closed Principle)
- 里式替换原则(Liskov Substitution Principle)
- 接口隔离原则(Interface Segregation Principle)
- 依赖反转原则(Dependency Inversion Principle)
简单工厂模式
工厂模式其实就是将创建对象的过程单独封装。和构造函数相关,将创建对象的过程封装起来。看起来挺简单的,有时候用了,可能都不知道。
javascript
function User(name, age, career, work) {
this.name = name
this.age = age
this.career = career
this.work = work
}
function Factory(name, age, career) {
let work;
switch (career) {
case 'coder':
work = ['写代码','写系分', '修Bug'];
break;
case 'pm':
work = ['订会议室', '写PRD', '催更'];
break;
case 'boss':
work = ['喝茶', '看报', '见客户'];
break;
case 'xxx':
break;
default:
break;
}
return new User(name, age, career, work);
}
抽象工厂模式
这个有点难想起来,没有想到很好的应用场景,给的一个demo是手机制造相关。
抽象工厂模式的定义,是围绕一个超级工厂创建其他工厂。
- 先创建一个抽象工厂,作为顶级boss,是创建工厂的工厂,是爸爸,继承这个抽象工厂可以创建不同的工厂,再从具体的工厂new出来新的产品。
javascript
// 抽象工厂
class MobilePhoneFactory {
createOS() {
throw new Error('抽象工厂方法不允许直接调用,你需要将我重写');
}
createHardWare() {
throw new Error('抽象工厂方法不允许直接调用,你需要将我重写');
}
}
// 具体工厂继承自抽象工厂
class FakeStarFactory extends MobilePhoneFactory {
createOS() {
return new AndroidOS();
}
createHardWare() {
return new QualcommHardWare();
}
}
- 具体的工厂有不同的功能,例如对于手机有操作系统,有硬件,而操作系统又有ios、Android,硬件更多,高通等等,对于ios、android是具体的功能,再他们之上呢,又有一层抽象层作为操作系统的抽象层,便于扩展新的操作系统。
scala
// 定义操作系统这类产品的抽象产品类
class OS {
controlHardWare() {
throw new Error('抽象工厂方法不允许直接调用,你需要将我重写');
}
}
// 定义具体操作系统的具体产品类
class AndroidOS extends OS {
controlHardWare() {
console.log('android');
}
}
class AppleOS extends OS {
controlHardWare() {
console.log('apple');
}
}
对于硬件同理,有个抽象层作为爸爸,爸爸不允许操作,儿子继承爸爸实现具体功能,同时可以生出好多不同的儿子。
scala
class HardWare {
operateByOrder() {
throw new Error('抽象产品方法不允许直接调用,你需要将我重写!');
}
}
class QualcommHardWare extends HardWare {
operateByOrder() {
console.log('高通1');
}
}
class QualcommHardWare2 extends HardWare {
operateByOrder() {
console.log('高通2');
}
}
上面一堆类造好了,就来看看具体的创建实例的过程:
ini
// 构造具体手机时
const myPhone = new FakeStarFactory();
// 创建操作系统
const myOS = myPhone.createOS();
// 创建硬件
const myHareWare = myPhone.createHardWare();
// 启动操作系统
myOS.controlHardWare();
// 唤醒硬件
myHareWare.operateByOrder();
- 创建具体手机
- 添加操作系统和硬件,这里可以对上面不同的类做组合
- 然后手机可以用了,启动系统。
这个时候,如果FakeStarFactory这个手机过时了,需要生产新的手机产品,则无需修改子类,只需要从最大的boss继承来创建一个新的具体工厂,然后从工厂创建实例即可。就是把内部操作系统和硬件都封闭了起来,留出了扩展的接口。
scala
// 如果构造一个新的手机,
class NewStarFactory extends MobilePhoneFactory {
createOS() {
return new AppleOS();
}
createHardWare() {
return new QualcommHardWare2();
}
}
// 构造具体手机时
const myPhone2 = new NewStarFactory();
// 创建操作系统
const myOS2 = myPhone.createOS();
// 创建硬件
const myHareWare2 = myPhone.createHardWare();
// 启动操作系统
myOS2.controlHardWare();
// 唤醒硬件
myHareWare2.operateByOrder();
看起来就创建示例有区别,其他都一样。
上面总共涉及到了4个角色:
- 抽象工厂:抽象类,它不能被用于生成具体实例,用于声明最终目标产品的共性。
MobilePhoneFactory
- 具体工厂:用于生成产品族里的一个具体的产品,继承自抽象工厂、实现了抽象工厂里声明的那些方法,用于创建具体的产品的类。
FakeStarFactory、NewStarFactory
- 抽象产品:抽象类,它不能被用于生成具体实例。上面我们看到,具体工厂里实现的接口,会依赖一些类,这些类对应到各种各样的具体的细粒度产品(比如操作系统、硬件等),这些具体产品类的共性各自抽离,便对应到了各自的抽象产品类。
OS、HardWare
- 具体产品:用于生成产品族里的一个具体的产品所依赖的更细粒度的产品,比如我们上文中具体的一种操作系统、或具体的一种硬件等。
AndroidOS、AppleOS、QualcommHardWare、QualcommHardWare2
学习完觉得写的挺好,但是没有具体应用的话,不知道会记多久,只有真正在实际项目中应用了,才会成为自己的东西吧,这也是为什么要记录下来,看一遍是最浅的,记录下来可以加深自己的印象。如果应用的话才会真正的掌握,如果把这个讲出来的话就理解的更深了。这就是学习的几个层次。
为了方便大家看,把上面整体代码列一下:
scala
// 抽象工厂
class MobilePhoneFactory {
createOS() {
throw new Error('抽象工厂方法不允许直接调用,你需要将我重写');
}
createHardWare() {
throw new Error('抽象工厂方法不允许直接调用,你需要将我重写');
}
}
// 具体工厂继承自抽象工厂
class FakeStarFactory extends MobilePhoneFactory {
createOS() {
return new AndroidOS();
}
createHardWare() {
return new QualcommHardWare();
}
}
// 定义操作系统这类产品的抽象产品类
class OS {
controlHardWare() {
throw new Error('抽象工厂方法不允许直接调用,你需要将我重写');
}
}
// 定义具体操作系统的具体产品类
class AndroidOS extends OS {
controlHardWare() {
console.log('android');
}
}
class AppleOS extends OS {
controlHardWare() {
console.log('apple');
}
}
class HardWare {
operateByOrder() {
throw new Error('抽象产品方法不允许直接调用,你需要将我重写!');
}
}
class QualcommHardWare extends HardWare {
operateByOrder() {
console.log('高通1');
}
}
class QualcommHardWare2 extends HardWare {
operateByOrder() {
console.log('高通2');
}
}
class MiWare extends HardWare {
operateByOrder() {
console.log('小米');
}
}
// 构造具体手机时
const myPhone = new FakeStarFactory();
// 创建操作系统
const myOS = myPhone.createOS();
// 创建硬件
const myHareWare = myPhone.createHardWare();
// 启动操作系统
myOS.controlHardWare();
// 唤醒硬件
myHareWare.operateByOrder();
// 如果构造一个新的手机,
class NewStarFactory extends MobilePhoneFactory {
createOS() {
return new AppleOS();
}
createHardWare() {
return new QualcommHardWare2();
}
}
// 构造具体手机时
const myPhone2 = new NewStarFactory();
// 创建操作系统
const myOS2 = myPhone.createOS();
// 创建硬件
const myHareWare2 = myPhone.createHardWare();
// 启动操作系统
myOS2.controlHardWare();
// 唤醒硬件
myHareWare2.operateByOrder();
小册最后总结的一些话我觉得挺好,摘抄出来给大家分享:
- 前端工程师首先是软件工程师。只会写 JavaScript、只理解 JavaScript、只通过 JavaScript 去理解软件世界,是一件可怕的事情,它会窄化你的技术视野------因为 JavaScript 只是编程语言中的一个分支。
这个确实有所感悟,工作这么多年,一直局限在前端,虽然node也会,但是服务端很少写,当然如果有了这么多年软件经验,任何技术花时间都是能学会的,但是并没有去扩展,暂时还并不想去扩展,自己的视野还是不够大。
-
抽象工厂是佐证"开放封闭原则"的良好素材,就是SOLID中的O的原则,这个是一直记住的。就记住了两个原则,单一职责和开放封闭,确实是有用的。也会在实际的项目代码中应用上。
-
不要小看那些看似"无用"的知识。仅仅因为"这个技术点我现在用不到"而推开摆在眼前的知识,是一种非常糟糕的学习方法------它会极大地限制你的能力和你职业生涯的可能性。
这一条给萌新们看,技术的学习路上,总是在想只学用的,学了看似用不上的东西到底有啥用呢?不知道,当时确实没有,但就像之前看到的一句话:
我们读了很多书,最终依然不可避免地会忘记,但其实它们最终都被吸收到体内,成为了我们的骨血和肌肉。我们享受到了阅读的快感,滋养了灵魂,拥有了自由丰富的思想。
读书对一个人的影响是潜移默化的,时间久了可能读完并不一定会记得看了什么,世界上任何书籍都不能带给你好运,但它们能让你悄悄成为最好的自己。
这句话送给自己,送给大家,一起共勉,愿我们不忘初心,在技术的道路上稳扎稳打。
愿你的指下有代码,眼里有星辰。
本文由mdnice多平台发布