封面
封面

设计模式

什么是模式

[[模式]]最早诞生于建筑学。20世纪70年代,哈佛大学建筑学博士Christopher Alexander和他的研究团队花了约20年的时间,研究了为解决同一个问题而设计出的不同建筑结构,从中发现了那些高质量设计中的相似性,并且用“模式”来指代这种相似性

什么是设计模式

烹饪有菜谱,游戏有攻略,每个领域都存在一些能够让我们又好又快地达成目标的“套路”。在程序世界,编程的“套路”就是设计模式。
设计模式在很多时候其实都体现了语言的不足之处。比如,JavaScript没有类的概念,所以我们在实现类的时候,就需要用到工厂模式。
设计模式的原则就是:找出程序变化的地方,并将变化封装起来。----“封装变化”
与齐说操心前端未来不如在当下何如练下扎实的基本功、过硬的业务素质和灵活的适应能力。而且,设计模式本身的目的就是为了让我们的代码具备更强的应对变化的能力。
从不缺少优秀的语言,而是缺少能凭借自己深刻的架构思想和工程思想去支配这门语言、取长补短的人。这种“不变的东西”,就是驾驭技术的内功。
  • 能用健壮的代码去解决具体的问题;
  • 能用抽象的思维去应对复杂的系统;
  • 能用工程化的思想去规划更大规模的业务。(永远能扩充当下的模块)
基础理论知识就好像是人物弧光,是推动角色发展轨迹的根基。更能构建角色的维度和深度,更能与知识产生共鸣。
很多人缺乏的并不是高瞻远瞩的激情,而是我们提到的基础能力----用健壮的代码去解决具体问题的能力,恰恰,这就是设计模式。

设计模式里需要注意的点

设计模式好比守望先锋里的回声,英雄联盟里的塞拉斯。好用的东西直接偷来。
用数学题来打比方,完成解题的过程好比完成需求需要的代码一样。公式和定理可以直接拿来用,设计模式也类似。
我们需要注意的就是:
  • 识别题目特征
  • catch题目想考察的知识点
  • 快速在脑海中映射出解题方法。

SOLID设计原则

这是设计模式的指导理论,可以帮助我们规避不良的软件设计。
(⭐代表主要的设计模式基本围绕这两点来展开)
  • 单一功能原则 (Single Responsibility)⭐
  • 开放封闭原则(Opened Closed)⭐
  • 里式替换原则(Liskov Substitution)
  • 接口隔离原则(Interface Segregation)
  • 依赖反转原则(Dependency Inversion)

工厂模式

工厂模式其实就是将创建的对象的过程单独封装。比如去餐馆点菜,我要一份桥头三嫩,我并不需要关心腰花怎么买,怎么切花刀,需要用哪些调味料码味,在锅里倒多少油,翻炒多久的制作窜稀套餐的过程问题。 在工厂模式里,菜品和调料就是传参,工厂函数就相当于厨师的动作和方法。我们并不必须要关心菜是如何生成的,我们只需要关心怎么蹿就行了。
总结: 工厂模式的目的,就是为了实现无脑梭哈传参

抽象整个工厂

我们需要遵循开放封闭的内容:对扩展开放,对修改关闭。说人话就是,软件实体(类、模块、函数)可以扩展,但是不能修改
// 生产PC的流水线
class PCFactory{
    // 提供的操作系统
    createOS(){
        throw new Error('抽象工厂方法不允许直接调用,你需要将我重写!')
    }
    // 提供的硬件
    createHardWare(){
        throw new Error('抽象工厂方法不允许直接调用,你需要将我重写!')
    }
}
这个模拟的抽象类,你new一个PCFactory,除了约定通用能力以外,啥也不干,主打的就是一个制定方针。调用它里面的实例方法,还会给你报错。无不是在提醒你:“家人们,我只是个定规矩的。” 这个类就是我们最顶端的Boss-AbstractFactory(抽象工厂)。
抽象工厂不干活,具体工厂(ConcreteFactory)来干活!比如我要设计一个PC,专门为练习时长两年半的iKun定制PC。
class iKunPCFactory extends PCFactory {
    createOS() {
        // 提供windows实例
        return new WindowsOS()
    }
    createHardWare() {
        // 提供的CPU
        return new AMD5800X3DHardWare()
    }
}
我们在具体工厂(iKunPCFactory)里的这种拿来用于new出具体对象的类,(WindowsOS和AMD5800X3D)叫做具体产品类(ConcreteProduct)。 所谓有卧龙的地方,必定有凤雏。具体产品类往往不会孤立存在,不同的具体产品类往往都有共同的功能,比如windowsOS和MacOS,他们都是操作系统。因此我们可以用一个抽象产品(AbstractProduct)类,来声明这一类产品应该具有的基本功能
// 定义这类操作系统这类产品的抽象产品类
class OS {
    controlHardWare() {
        throw new Error('抽象产品方法不允许直接调用,你需要将我重写!')
    }
}
// 定义具体操作系统的具体产品类
class WindowOS {
    controlHardWare() {
        console.log('windows,启动!')
    }
}

class MacOS {
    controlHardWare() {
        console.log('MacOS,启动!')
    }
}
//
硬件产品类同理
// 定义PC硬件这类产品的抽象产品类
class HardWare {
    open() {
        throw new Error('启动!')
    }
}

class AMD5800X3DHardWare extends HardWare {
    open() {
        console.log('5800X3D,启动!')
    }
}

class AppleM3HardWare extends HardWare {
    open() {
        console.log('Apple M3,启动!')
    }
}
如此如此,当我们要做一台iKunPC时,我们只需要这样做:
// 这个我的PC
const myiKunPC = new iKunPCFactory()
// 让这个PC拥有操作系统
const myOS = myiKunPC.createOS()
// 让它拥有硬件
const myHardWare = myiKunPC.createHardWare()
// 让这个操作系统去接管这个硬件
myiKunPC.controlHardWare()
// 点亮这台PC
myiKunPC.open()
加入有一天,市场迎来了6800X3D,和5090,这个时候,我们不需要对抽象工厂PCFactory做任何修改,只需要拓展它的种类。
class iKunPCProMaxUltraLimitNBPlusFactory extends PCFatory {
    // 提供的操作系统
    createOS(){
        throw new Error('抽象工厂方法不允许直接调用,你需要将我重写!')
    }
    // 提供的硬件
    createHardWare(){
        throw new Error('抽象工厂方法不允许直接调用,你需要将我重写!')
    }
}
对原有的系统不会造成任何潜在影响,达到了“对拓展开放,对修改封闭”的目的。这就是抽象产品类

抽象工厂与简单工厂的思路

共同点,都在尝试去分离系统中的变与不变的部分。不同在于场景的复杂度
使用抽象类去降低扩展的成本,同时需要对类的性质作划分:
  • 抽象工厂(抽象类,它不能被用于生成具体实例):用于声明最终的产品的共性。在一个系统里,抽象工厂可以有多个,类似有多个产品的不同的共性(类似就是被一个更大的工厂收购了,有更多的流水线,比如不止PC抽象类,还有路由器抽象类,平板抽象类)。每一个抽象工厂对应这一类的产品,称为“产品族”。
  • 具体工厂(用于生成产品族里的具体的一个产品):继承自抽象工厂、实现了抽象工厂里不可被实例的方法,用于创建具体的产品的类。
  • 抽象产品(抽象类,他不能被用于生成具体实例):具体工厂里面实现的接口,会依赖一些类,这些类会对应到各种具体的细粒度产品(比如OS,HardWare等)。这些具体产品类的共性各自抽离,便对应到了各自的抽象产品类。
  • 具体产品(用于生产产品族里具体一个产品所依赖更细粒度的产品):比如我们提到的具体某一种操作系统、或者具体的硬件CPU型号等。
抽象工厂模式的定义,是围绕一个超级工厂创建其他工厂:
  • 学会用ES6模拟JAVA中的抽象类;⭐
  • 了解抽象工厂模式中四个角色的例子和作用;⭐
  • 对“开放封闭原则”有自己的理解,知道应该如何用,如何改。因为抽象工厂是佐证开放封闭原则的良好素材。⭐