深入了解TypeScript中的5种设计模式

本篇文章带大家深入了解5 种 typescript设计模式。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

深入了解TypeScript中的5种设计模式

设计模式是可以帮助开发人员解决问题的模板。在本中涉及的模式太多了,而且它们往往针对不同的需求。但是,它们可以被分为三个不同的组:

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

单例模式

单例模式可能是最著名的设计模式之一。它是一种创建模式,因为它确保无论我们尝试实例化一个类多少次,我们都只有一个可用的实例。

处理数据库连接之类的可以单例模式,因为我们希望一次只处理一个,而不必在每个用户请求时重新连接。

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.40470872509907130.40470872509907130.40470872509907130.40470872509907130.4047087250990713

登录后复制

工厂模式

工厂模式是一种创建模式,就像单例模式一样。但是,这个模式并不直接在我们关心的对象上工作,而是只负责管理它的创建。

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

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

但是,如果决定使用工厂方法模式,则可以执行以下操作:

1.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 行为,然后以两种方式扩展该类,即SuperDog和SwimmingDog类。 但是,如果我们想将两者混合在一起,则必须再次创建一个新类来扩展它们的行为,但是,有更好的方法。

组合让我们可以将自定义行为封装在不同的类中,然后使用该模式通过将原始对象传递给它们的构造函数来创建这些类的新实例。 让我们看一下代码:

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 ,这意味着并没有使用它,只是使用它来定义构造函数,该构造函数会将原始对象的副本保留在受保护的属性中。 公共接口的覆盖是在自定义装饰器内部完成的。SuperAnimal和SwimmingAnimal是实际的装饰器,它们是添加额外行为的装饰器。

进行此设置的好处是,由于所有装饰器也间接扩展了Animal类,因此如果你要将两种行为混合在一起,则可以执行以下操作:

const superSwimmingDog =  new SwimmingAnimal(superDog)superSwimmingDog.move()

登录后复制

Composite(组合)

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

比如编写过前端的页面,肯定使用过

等标签定义一些格式,然后格式之间互相组合,通过一种递归的方式组织成相应的结构,这种方式其实就是组合,将部分的组件镶嵌到整体之中。

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

看一个例子:

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 boxAdding a  Samsung Note 20 to the boxAdding a  Samsung TV 20in to the boxAdding a  Samsung TV 50in to the boxAdding a  A box with 2 products to the boxTotal price:  2105.5

登录后复制

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

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

英文原文地址:https://blog.bitsrc.io/design-patterns-in-typescript-e9f84de40449作者:Fernando Doglio译者:前端小智

更多编程相关知识,请访问:编程视频!!

以上就是深入了解TypeScript中的5种设计模式的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2712595.html

(0)
上一篇 2025年3月7日 21:15:13
下一篇 2025年3月7日 21:15:22

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • FacePoke— 开源的实时面部编辑工具,拖拽操作面部表情

    facepoke:一款基于ai的开源实时面部编辑工具 FacePoke是一款利用AI技术实现实时面部编辑的开源工具。用户只需简单的鼠标拖拽,即可轻松调整照片中人物的头部姿态(抬头、低头、摇头)和面部表情(眨眼、眼球转动、眉毛、嘴巴等),让静…

    2025年4月5日
    100
  • 详解js在html中的加载执行顺序

    js在html中的加载执行顺序 1.加载顺序:引入标记的出现顺序, 页面上的js代码是js的一部分,所以Javascript在页面装载时执行的顺序就是其引入标记的出现顺序, 标记里面的或者通过src引入的外部JS,都是按照其语句出现的顺序执…

    编程技术 2025年4月4日
    100
  • HTML和JS实现简单的计算器

    HTML和JS实现计算器功能的也是很容易的,本文主要和大家分享HTML和JS实现简单的计算器,希望能帮助到大家。 下面是代码:    nbsp;html>无标题文档 var result=””; function jisuan(num…

    编程技术 2025年4月4日
    100
  • 实现在HTML页面加载完毕后运行js方法

    本文主要和大家分享实现在HTML页面加载完毕后运行js方法,主要以代码的方法和大家分享,希望能帮助到大家。 Js方法: window.onload=function (){ var userName=”xiaoming”; alert(us…

    编程技术 2025年4月4日
    100
  • JavaScript实现获取远程的html到当前页面中

    今天做个项目,需要在当前的html页面中引用一个远程的html页面,百度了一下,发现一个非常好用的代码,这里分享给大家,有相同需求的小伙伴可以来看看 html代码 登录后复制 立即学习“Java免费学习笔记(深入)”; javascript…

    编程技术 2025年4月4日
    100
  • JavaScript模块优化

    给大家分享一下如何才能开发出更好的javascript模块和功能,有兴趣的朋友参考学习下吧。 不少人都曾经在 npm 上发布过自己开发的 JavaScript 模块,而在使用一些模块的过程中,我经常产生“这个模块很有用,但如果能 xxx 就…

    编程技术 2025年4月4日
    100
  • Java框架中设计模式与架构模式的区别

    在 java 框架中,设计模式和架构模式的区别在于:设计模式定义了在软件设计中解决常见问题的抽象解决方案,关注类和对象之间的交互,如工厂模式。架构模式定义了系统结构和模块之间的关系,关注系统组件的组织和交互,如分层架构。 Java框架中的设…

    2025年4月2日
    100
  • MyBatis框架中设计模式的应用

    mybatis框架广泛应用设计模式,包括:工厂模式:创建sqlsessionfactory对象,管理数据库连接和查询;代理模式:实现延迟加载和懒加载,提高性能。外观模式:封装对数据库访问,简化代码维护。其中,一对多关系映射可以通过user和…

    2025年4月2日
    100
  • 如何选择适合Java框架的设计模式?

    要选择适合 java 框架的设计模式,需按以下步骤进行:识别问题域:明确要解决的问题。考虑实现成本:评估模式的复杂性和维护成本。确保可扩展性:选择允许应用程序轻松适应变化需求的模式。关注可维护性:模式应具有良好的可读性和可理解性。 如何选择…

    2025年4月2日
    100
  • Hibernate框架中设计模式的应用

    hibernate 框架广泛运用设计模式来实现其功能,其中包括:工厂模式:创建 sessionfactory 对象以创建 session 对象。代理模式:延迟加载实体,仅在需要时加载实际实体。单元状态模式:跟踪实体在数据库中的生命周期状态。…

    2025年4月2日
    100

发表回复

登录后才能评论