你应该知道的 5 种 TypeScript设计模式

做者:Fernando Doglio
译者:前端小智
来源:medium
点赞再看,养成习惯

本文 GitHub https://github.com/qq44924588... 上已经收录,更多往期高赞文章的分类,也整理了不少个人文档,和教程资料。欢迎Star和完善,你们面试能够参照考点复习,但愿咱们一块儿有点东西。前端

最近开源了一个 Vue 组件,还不够完善,欢迎你们来一块儿完善它,也但愿你们能给个 star 支持一下,谢谢各位了。vue

github 地址:https://github.com/qq44924588...git

设计模式是能够帮助开发人员解决问题的模板。在本中涉及的模式太多了,并且它们每每针对不一样的需求。可是,它们能够被分为三个不一样的组:github

  • 结构模式处理不一样组件(或类)之间的关系,并造成新的结构,以提供新的功能。结构模式的例子有CompositeAdapterDecorator
  • 行为模式将组件之间的公共行为抽象成一个独立的实体。行为模式的例子有命令、策略和我我的最喜欢的一个:观察者模式
  • 建立模式 专一于类的实例化,让咱们更容易建立新的实体。我说的是工厂方法,单例和抽象工厂。

单例模式

单例模式多是最著名的设计模式之一。它是一种建立模式,由于它确保不管咱们尝试实例化一个类多少次,咱们都只有一个可用的实例。面试

处理数据库链接之类的能够单例模式,由于咱们但愿一次只处理一个,而没必要在每一个用户请求时从新链接。typescript

class MyDBConn {
  protected static instance: MyDBConn | null = null
  private id:number = 0

  constructor() {
    this.id = Math.random()
  }

  public getID():number {
    return this.id
  }

  public static getInstance():MyDBConn {
    if (!MyDBConn.instance) {
      MyDBConn.instance = new MyDBConn()
    }
    return MyDBConn.instance
  }
}

const connections = [
  MyDBConn.getInstance(),
  MyDBConn.getInstance(),
  MyDBConn.getInstance(),
  MyDBConn.getInstance(),
  MyDBConn.getInstance()
]

connections.forEach( c => {
    console.log(c.getID())
})

如今,虽然不能直接实例化类,可是使用getInstance方法,能够确保不会有多个实例。在上面的示例中,能够看到包装数据库链接的伪类如何从该模式中获益。数据库

这个事例展现了不管咱们调用getInstance方法多少次,这个链接老是相同的。设计模式

上面的运行结果:微信

0.4047087250990713
0.4047087250990713
0.4047087250990713
0.4047087250990713
0.4047087250990713

工厂模式

工厂模式是一种建立模式,就像单例模式同样。可是,这个模式并不直接在咱们关心的对象上工做,而是只负责管理它的建立。dom

解释一下:假设咱们经过编写代码来模拟移动车辆,车有不少类型,例如汽车、自行车和飞机,移动代码应该封装在每一个vehicle类中,可是调用它们的move 方法的代码能够是通用的。

这里的问题是如何处理对象建立?能够有一个具备3个方法的单一creator类,或者一个接收参数的方法。在任何一种状况下,扩展该逻辑以支持建立更多vehices都须要不断增加相同的类。

可是,若是决定使用工厂方法模式,则能够执行如下操做:

clipboard.png

如今,建立新对象所需的代码被封装到一个新类中,每一个类对应一个车辆类型。这确保了若是未来须要添加车辆,只须要添加一个新类,而不须要修改任何已经存在的东西。

接着来看看,咱们如何使用TypeScript来实现这一点:

interface Vehicle {
    move(): void
}

class Car implements Vehicle {

    public move(): void {
        console.log("Moving the car!")
    }
}

class Bicycle implements Vehicle {

    public move(): void {
        console.log("Moving the bicycle!")
    }
}

class Plane implements Vehicle {

    public move(): void {
        console.log("Flying the plane!")
    }
}

// VehicleHandler 是“抽象的”,由于没有人会实例化它instantiate it
// 咱们要扩展它并实现抽象方法
abstract class VehicleHandler {

    // 这是真正的处理程序须要实现的方法
    public abstract createVehicle(): Vehicle 

    public moveVehicle(): void {
        const myVehicle = this.createVehicle()
        myVehicle.move()
    }
} 

class PlaneHandler extends VehicleHandler{

    public createVehicle(): Vehicle {
        return new Plane()
    }
}

class CarHandler  extends VehicleHandler{

    public createVehicle(): Vehicle {
        return new Car()
    }
}

class BicycleHandler  extends VehicleHandler{

    public createVehicle(): Vehicle {
        return new Bicycle()
    }
}

/// User code...
const planes = new PlaneHandler()
const cars = new CarHandler()

planes.moveVehicle()
cars.moveVehicle()

上面的代码不少,但咱们可使用上面的图表来理解它。本质上最后,咱们关心的是自定义处理程序,这里称它为处理程序,而不是创造者,由于他们不仅是建立的对象,他们也有逻辑,使用它们(moveVehicle方法)。

这个模式的美妙之处在于,若是您你要添加一个新的vehicle类型,所要作的就是添加它的vehicle类和它的处理程序类,而不增长任何其余类的LOC。

观察者模式

在全部的模式,我最喜欢的是观察者模式,由于类型的行为咱们能够实现它。

它是如何工做的呢?本质上,该模式代表你拥有一组观察者对象,这些对象将对被观察实体状态的变化作出反应。为了实现这一点,一旦在被观察端接收到一个更改,它就负责经过调用它的一个方法来通知它的观察者。

在实践中,此模式的实现相对简单,让咱们快速查看一下代码,而后回顾一下

type InternalState = {
  event: String
}

abstract class Observer {
  abstract update(state:InternalState): void
}

abstract class Observable {
  protected observers: Observer[] = []
  protected state:InternalState = { event: ""}

  public addObserver(o: Observer):void {
    this.observers.push(o)
  }

  protected notify () {
    this.observers.forEach(o => o.update(this.state))
  }
}


class ConsoleLogger extends Observer  {

    public update(newState: InternalState) {
        console.log("New internal state update: ", newState)
    }
}

class InputElement extends Observable {

    public click():void {
        this.state = { event: "click" }
        this.notify()
    }

}

const input = new InputElement()
input.addObserver(new ConsoleLogger())

input.click()

正如你所看到的,经过两个抽象类,咱们能够定义Observer,该观察者将表示对Observable实体上的更改作出反应的对象。 在上面的示例中,咱们假设具备一个被单击的InputElement实体(相似于在前端具备HTML输入字段的方式),以及一个ConsoleLogger,用于记录控制台发生的全部事情。

这种模式的优势在于,它使咱们可以了解Observable的内部状态并对其作出反应,而没必要弄乱其内部代码。 咱们能够继续添加执行其余操做的观察者,甚至包括对特定事件作出反应的观察者,而后让它们的代码决定对每一个通知执行的操做。

装饰模式

装饰模式试图在运行时向现有对象添加行为。 从某种意义上说,咱们能够将其视为动态继承,由于即便没有建立新类来添加行为,咱们也正在建立具备扩展功能的新对象。

这样考虑:假设咱们拥有一个带有move方法的Dog类,如今您想扩展其行为,由于咱们想要一只超级狗和一只能够游泳的狗。

一般,咱们须要在 Dog 类中添加move 行为,而后以两种方式扩展该类,即SuperDogSwimmingDog类。 可是,若是咱们想将二者混合在一块儿,则必须再次建立一个新类来扩展它们的行为,可是,有更好的方法。

组合让咱们能够将自定义行为封装在不一样的类中,而后使用该模式经过将原始对象传递给它们的构造函数来建立这些类的新实例。 让咱们看一下代码:

abstract class Animal {

    abstract move(): void
}

abstract class SuperDecorator extends Animal {
    protected comp: Animal
    
    constructor(decoratedAnimal: Animal) {
        super()
        this.comp = decoratedAnimal
    }
    
    abstract move(): void
}

class Dog extends Animal {

    public move():void {
        console.log("Moving the dog...")
    }
}

class SuperAnimal extends SuperDecorator {

    public move():void {
        console.log("Starts flying...")
        this.comp.move()
        console.log("Landing...")
    }
}

class SwimmingAnimal extends SuperDecorator {

    public move():void {
        console.log("Jumps into the water...")
        this.comp.move()
    }
}


const dog = new Dog()

console.log("--- Non-decorated attempt: ")
dog.move()

console.log("--- Flying decorator --- ")
const superDog =  new SuperAnimal(dog)
superDog.move()

console.log("--- Now let's go swimming --- ")
const swimmingDog =  new SwimmingAnimal(dog)
swimmingDog.move()

注意几个细节:

  • 实际上,SuperDecorator类扩展了Animal类,与Dog类扩展了相同的类。 这是由于装饰器须要提供与其尝试装饰的类相同的公共接口。
  • SuperDecorator类是abstract ,这意味着并无使用它,只是使用它来定义构造函数,该构造函数会将原始对象的副本保留在受保护的属性中。 公共接口的覆盖是在自定义装饰器内部完成的。
  • SuperAnimalSwimmingAnimal是实际的装饰器,它们是添加额外行为的装饰器。

进行此设置的好处是,因为全部装饰器也间接扩展了Animal类,所以若是你要将两种行为混合在一块儿,则能够执行如下操做:

const superSwimmingDog =  new SwimmingAnimal(superDog)

superSwimmingDog.move()

Composite(组合)

关于Composite模式,其实就是组合模式,又叫部分总体模式,这个模式在咱们的生活中也常用。

好比编写过前端的页面,确定使用过<div>等标签订义一些格式,而后格式之间互相组合,经过一种递归的方式组织成相应的结构,这种方式其实就是组合,将部分的组件镶嵌到总体之中。

关于此模式的有趣之处在于,它不是一个简单的对象组,它能够包含实体或实体组,每一个组能够同时包含更多组,这就是咱们所说的树。

看一个例子:

interface IProduct {
  getName(): string
  getPrice(): number
}

class Product implements IProduct {
  private price:number
  private name:string

  constructor(name:string, price:number) {
    this.name = name
    this.price = price
  }

  public getPrice():number {
    return this.price
  }

  public getName(): string {
    return this.name
  }
}

class Box implements IProduct {

    private products: IProduct[] = []
    
    contructor() {
        this.products = []
    }
    
    public getName(): string {
        return "A box with " + this.products.length + " products"
    } 
    
    add(p: IProduct):void {
        console.log("Adding a ", p.getName(), "to the box")
        this.products.push(p)
    }

    getPrice(): number {
        return this.products.reduce( (curr: number, b: IProduct) => (curr + b.getPrice()),  0)
    }
}

//Using the code...
const box1 = new Box()
box1.add(new Product("Bubble gum", 0.5))
box1.add(new Product("Samsung Note 20", 1005))

const box2 = new Box()
box2.add( new Product("Samsung TV 20in", 300))
box2.add( new Product("Samsung TV 50in", 800))

box1.add(box2)

console.log("Total price: ", box1.getPrice())

在上面的示例中,咱们能够将product 放入Box中,也能够将Box放入其余Box中,这是组合的经典示例。由于咱们要实现的是得到完整的交付价格,所以须要在大box里添加每一个元素的价格(包括每一个小box的价格)。

上面运行的结果:

Adding a  Bubble gum to the box
Adding a  Samsung Note 20 to the box
Adding a  Samsung TV 20in to the box
Adding a  Samsung TV 50in to the box
Adding a  A box with 2 products to the box
Total price:  2105.5

所以,在处理遵循同一接口的多个对象时,请考虑使用此模式。 经过将复杂性隐藏在单个实体(组合自己)中,您会发现它有助于简化与小组的互动方式。

今天的分享就到这里了,感谢你们的观看,咱们下期再见。


原文:https://blog.bitsrc.io/design...

代码部署后可能存在的BUG无法实时知道,过后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给你们推荐一个好用的BUG监控工具 Fundebug

交流

文章每周持续更新,能够微信搜索「 大迁世界 」第一时间阅读和催更(比博客早一到两篇哟),本文 GitHub https://github.com/qq449245884/xiaozhi 已经收录,整理了不少个人文档,欢迎Star和完善,你们面试能够参照考点复习,另外关注公众号,后台回复福利,便可看到福利,你懂的。