跳至主要內容

设计模式

zfh大约 7 分钟约 2057 字

提示

掘金小册《JavaScript 设计模式核心原理与应用实践》 阅读笔记

前端工程师的成长论

  1. 未来的关键在于培养扎实的基本功、过硬的业务素质和灵活的适应能力,而不是过分关注行业的发展方向

  2. 在竞争激烈的环境中,需要思考自己的核心竞争力是什么,以突出自己的特点

  3. 一个前端工程师的本质并非取决于瞬息万变的技术点,而是三个层次的能力:使用健壮的代码解决具体问题、应对复杂系统的抽象思维能力,以及规划大规模业务的工程化思维。后两种能力可以说是对架构师的要求。事实上,能做到第一点并且把它做到扎实、做到娴熟的人,已经堪称同辈楷模

  4. 基础理论知识是一个人的基线,理论越强,基线越高。设定目标并不断向上攀升,达到目标只是时间问题。相比之下,许多野路子工程师即使经过多年努力也无法达到优秀工程师的基线,他们所追求的高深学问对于正规工程师而言是很自然的东西

  5. 架构是许多人渴望追求的高大上话题,然而,很多人缺乏的并非高瞻远瞩的激情,而是“不变能力”中最基本的一点,即用健壮的代码解决具体问题的能力。在软件工程领域中,设计模式正是对应于这种能力的经典知识体系

  6. 要成为靠谱的开发者,首先需要掌握设计模式

设计模式的”道“与”术“

  1. 模式的含义:模式描述了我们周围不断重复发生的问题以及解决方案的核心。通过使用模式,我们能够反复应用该方案,避免重复劳动

  2. 在实际开发中,代码很少是不变的,我们需要处理变化。封装变化的目标是将变化的影响最小化,将可变部分与不变部分分离开来。这样做可以确保变化的部分具有灵活性,而不变的部分保持稳定。通过这种方式,我们可以编写出健壮的代码,即能够经受住变化考验的代码。而设计模式出现的意义,就是帮我们写出这样的代码

创建型

工厂模式·简单工厂

区分变与不变

使用构造函数去初始化对象,就是应用了构造器模式:

function User(name, age, career) {
  this.name = name
  this.age = age
  this.career = career
}

创建一个 user 过程中,变的是每个 user 的姓名、年龄、工种,这是用户的个性,不变的是每个员工都具备姓名、年龄、工种这些属性,这是用户的共性

用构造器模式的时候,我们本质上是去抽象了每个对象实例的变与不变。那么使用工厂模式时,我们要做的就是去抽象不同构造函数(类)之间的变与不变

现在要给每个工种的用户加上一个个性化的字段,来描述他们的工作内容

CoderProductManager 两个工种的员工,是不是仍然存在都拥有 nameagecareerwork 这四个属性这样的共性?它们之间的区别,在于每个字段取值的不同,以及 work 字段需要随 career 字段取值的不同而改变

相同的逻辑封装回 User 类里,然后把这个承载了共性的 User 类和个性化的逻辑判断写入同一个函数,我们只需要像以前一样无脑传参

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 'product manager':
            work = ['订会议室', '写PRD', '催更']
            break
        case 'boss':
            work = ['喝茶', '看报', '见客户']
        case 'xxx':
            // 其它工种的职责分配
            ...

    return new User(name, age, career, work)
}
}

工厂模式的简单之处,在于它的概念相对好理解:将创建对象的过程单独封装,这样的操作就是工厂模式。同时它的应用场景也非常容易识别:有构造函数的地方,我们就应该想到简单工厂;在写了大量构造函数、调用了大量的 new、自觉非常不爽的情况下,我们就应该思考是不是可以掏出工厂模式重构我们的代码了

工厂模式·抽象工厂

理解”开放封闭“

开放封闭:对拓展开放,对修改封闭。软件实体(类、模块、函数)可以扩展,但是不可修改

JavaScript中没有抽象类的概念,所以本节示例使用TypeScript编写

// 手机抽象类 最上级工厂
abstract class MobilePhoneFactory {
  // 提供操作系统的接口
  abstract createOS(): void
  // 提供硬件的接口
  abstract createHardWare(): void
}

// 系统抽象类
abstract class OS {
  abstract controlHardWare(): void
}

// 硬件抽象类
abstract class HardWare {
  abstract operateByOrder(): void
}

// 具体工厂
class IphoneFactory extends MobilePhoneFactory {
  createOS() {
    // 提供苹果系统实例
    return new Ios()
  }
  createHardWare() {
    // 提供苹果硬件实例
    return new AppleHardWare()
  }
}

//ios
class Ios extends OS {
  controlHardWare() {
    console.log('苹果系统操控硬件')
  }
}

// 苹果硬件
class AppleHardWare extends HardWare {
  operateByOrder() {
    console.log('苹果硬件运转')
  }
}

// 这是我的手机
const myPhone = new IphoneFactory()
// 让它拥有操作系统
const myOS = myPhone.createOS()
// 让它拥有硬件
const myHardWare = myPhone.createHardWare()
// 启动操作系统
myOS.controlHardWare()
// 唤醒硬件
myHardWare.operateByOrder()

如果我们想生产安卓手机,不需要对抽象工厂 MobilePhoneFactory 做任何修改,只需要拓展它的种类

对原有的系统不会造成任何潜在影响 所谓的“对拓展开放,对修改封闭”就这么圆满实现了。之所以要实现抽象产品类(OS 类,硬件类)是一样的道理

抽象工厂和简单工厂的异同:

  • 共同点:都是分离一个系统中变与不变的部分

  • 不同点:不同点在于场景的复杂度。在简单工厂的使用场景里,处理的对象是类,它们的共性容易抽离且逻辑比较简单,故而不苛求代码可扩展性。抽象工厂本质上处理的其实也是类,但是是一帮复杂的类,它们存在着千变万化的扩展可能性——这使得我们必须对共性作更特别的处理、使用抽象类去降低扩展的成本,同时需要对类的性质作划分,于是有了这样的四个关键角色:

    • 抽象工厂(抽象类,它不能被用于生成具体实例): 用于声明最终目标产品的共性。在一个系统里,抽象工厂可以有多个(大家可以想象我们的手机厂后来被一个更大的厂收购了,这个厂里除了手机抽象类,还有平板、游戏机抽象类等等),每一个抽象工厂对应的这一类的产品,被称为“产品族”。
    • 具体工厂(用于生成产品族里的一个具体的产品): 继承自抽象工厂、实现了抽象工厂里声明的那些方法,用于创建具体的产品的类。
    • 抽象产品(抽象类,它不能被用于生成具体实例): 上面我们看到,具体工厂里实现的接口,会依赖一些类,这些类对应到各种各样的具体的细粒度产品(比如操作系统、硬件等),这些具体产品类的共性各自抽离,便对应到了各自的抽象产品类。
    • 具体产品(用于生成产品族里的一个具体的产品所依赖的更细粒度的产品): 比如我们上文中具体的一种操作系统、或具体的一种硬件等。

    抽象工厂模式是围绕一个超级工厂创建其他工厂

单例模式

vuex 的数据管理哲学

上次编辑于:
本站勉强运行 小时