设计模式是帮助开发者解决问题的模板。本书涵盖的模式太多,而且它们往往针对不同的需求。但是,它们可以分为三个不同的组:结构模式处理不同组件(或类)之间的关系并形成新结构以提供新功能。结构模式的示例有Composite、Adapter和Decorator。行为模式将组件之间的共同行为抽象为一个独立的实体。行为模式的例子有命令、策略和我个人最喜欢的:观察者模式。创建型模式专注于类的实例化,使我们更容易创建新的实体。我说的是工厂方法、单例和抽象工厂。单例模式单例模式可能是最著名的设计模式之一。这是一种创建型模式,因为它确保无论我们尝试实例化一个类多少次,我们都只有一个实例可用。处理数据库连接之类的事情可以是单例的,因为我们一次只想处理一个,而不必在每个用户请求时都重新连接。classMyDBConn{protectedstaticinstance:MyDBConn|null=nullprivateid:number=0constructor(){this.id=Math.random()}publicgetID():number{returnthis.id}publicstaticgetInstance():MyDBConn{if(!MyDBConn.instance){MyDBConn.instance=newMyDBConn()}returnMyDBConn.instance}}constconnections=[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工厂模式工厂模式是一种创建模式,和单例模式一样。然而,这种模式并不直接作用于我们关心的对象,而只是管理它的创建。解释一下:假设我们通过编写代码来模拟一辆行驶中的车辆。有许多类型的交通工具,例如汽车、自行车和飞机。移动代码应该封装在每个车辆类中,但是调用它们的移动方法的代码可以通用。这里的问题是如何处理对象创建?可以有一个具有3个方法的创建者类,或者一个接收参数的方法。在任何一种情况下,扩展该逻辑以支持创建更多车辆都需要不断增加相同的类别。但是,如果您决定使用工厂方法模式,那么您可以这样做:现在,创建新对象所需的代码被封装到一个新类中,每个类对应一种车辆类型。这样可以确保如果将来需要添加车辆,只需添加一个新类,而无需修改任何已经存在的内容。让我们看看如何使用TypeScript实现这一点:Movingthebicycle!")}}classPlaneimplementsVehicle{publicmove():void{console.log("Flyingtheplane!")}}//VehicleHandler是“抽象的”,因为没有人会实例化它instantiateit//我们将Extend它并实现抽象方法abstractclassVehicleHandler{//这个是realhandler需要实现的方法}}classCarHandlerextendsVehicleHandler{publiccreateVehicle():Vehicle{returnnewCar()}}classBicycleHandlerextendsVehicleHandler{publiccreateVehicle():Vehicle{returnnewBicycle()}}///用户代码...constplanes=newCarPlaneHandler()planes.moveVehicle()cars.moveVehicle()上面的代码很多,但是我们可以通过上面的图来理解。本质上说到底,我们关心的是自定义处理程序,这里我们称它们为处理程序,而不是创建者,因为它们不只是创建对象,它们还有使用它们的逻辑(moveVehicle方法)。这种模式的美妙之处在于,如果你想添加一个新的车辆类型,你所要做的就是添加它的车辆类和它的handler类,而无需增加任何其他类的LOC。观察者模式在所有模式中,我最喜欢的是观察者模式,因为我们可以实现它的行为类型。它是如何工作的?本质上,该模式声明您有一组观察者对象,这些观察者对象将对被观察实体的状态变化做出反应。为实现这一点,它负责在被观察端收到更改后通过调用其方法之一来通知其观察者。在实践中,这个模式的实现比较简单,我们先快速看一下代码,再回顾一下[]protectedstate:InternalState={event:""}publicaddObserver(o:Observer):void{this.observers.push(o)}protectednotify(){this.observers.forEach(o=>o.update(this.state))}}classConsoleLoggerextendsObserver{publicupdate(newState:InternalState){console.log("Newinternalstateupdate:",newState)}}classInputElementextendsObservable{publicclick():void{this.state={event:"click"}this.notify()}}constinput=newInputElement()input.addObserver(newConsoleLogger())input.click()如你所见,通过两个抽象类,我们可以定义Observer来表示对Observable实体对象的变化做出反应。在上面的示例中,我们假设有一个被单击的InputElement实体(类似于您在前端有一个HTML输入字段的方式),以及一个记录控制台发生的所有事情的ConsoleLogger。这种模式的美妙之处在于它允许我们理解Observable的内部状态并对其做出反应,而不会弄乱其内部代码。我们可以不断添加做其他事情的观察者,甚至是对特定事件做出反应的观察者,并让它们的代码决定如何处理每个通知。装饰器模式装饰器模式试图在运行时向现有对象添加行为。从某种意义上说,我们可以将其视为动态继承,因为即使我们没有创建新类来添加行为,我们也正在创建具有扩展功能的新对象。这样想:假设我们有一个带有move方法的Dog类,现在您想扩展它的行为,因为我们想要一只超级狗和一只会游泳的狗。通常,我们需要在Dog类中添加移动行为,然后通过两种方式扩展该类,SuperDog和SwimmingDog类。然而,如果我们想混合这两者,我们又必须创建一个新类来扩展它们的行为,但是,还有更好的方法。组合允许我们将自定义行为封装在不同的类中,然后使用此模式通过将原始对象传递给它们的构造函数来创建这些类的新实例。让我们看一下代码:log("Movingthedog...")}}classSuperAnimalextendsSuperDecorator{publicmove():void{console.log("Startsflying...")this.comp.move()console.log("Landing...")}}classSwimmingAnimalextendsSuperDecorator{publicmove():void{console.log("跳入水中...")this.comp.move()}}constdog=newDog()console.log("---非装饰尝试:")dog.move()console.log("---Flyingdecorator---")constsuperDog=newSuperAnimal(dog)superDog.move()console.log("---Nowlet'sgoswimming---")constswimmingDog=newSwimmingAnimal(dog)swimmingDog.move()注意一些细节:事实上,SuperDecorator类扩展了Animal类,与Dog类扩展的类相同。这是因为装饰器需要提供与其试图装饰的类相同的公共接口。SuperDecorator类是抽象的,这意味着它没有被使用,它只是用来定义一个构造函数,该构造函数将在受保护的属性中保留原始对象的副本。公共接口的覆盖是在自定义装饰器内部完成的。SuperAnimal和SwimmingAnimal是实际的装饰器,它们是添加额外行为的装饰器。进行此设置的好处在于,由于所有装饰器也间接扩展了Animal类,如果您想将这两种行为混合在一起,您可以这样做:composite)关于Composite模式,其实是一种组合模式,也称为局部整体模式。这种模式在我们的生活中经常会用到。比如你写了一个前端页面,肯定是用etc.等标签定义了一些格式,然后把这些格式相互组合起来,递归的组织成相应的结构。这种方法实际上是一种组合,将部分组件嵌入到整体中。中间。这种模式的有趣之处在于它不是简单的一组对象,它可以包含实体或实体组,每个组可以同时包含更多组,这就是我们所说的树。看一个例子:interfaceIProduct{getName():stringgetPrice():number}classProductimplementsIProduct{privateprice:numberprivatename:stringconstructor(name:string,price:number){this.name=namethis.price=price}publicgetPrice():number{returnthis.price}publicgetName():string{returnthis.name}}classBoximplementsIProduct{privateproducts:IProduct[]=[]contructor(){this.products=[]}publicgetName():string{return"Aboxwith"+this.products。长度+“产品”}添加(p:IProduct):void{console.log(“Addinga”,p.getName(),“tothebox”)this.products.push(p)}getPrice():number{returnthis.products.reduce((curr:number,b:IProduct)=>(curr+b.getPrice()),0)}}//使用代码...constbox1=newBox()box1.add(newProduct("泡泡糖",0.5))box1.add(newProduct("SamsungNote20",1005))constbox2=newBox()box2.add(newProduct("SamsungTV20in",300))box2.add(newProduct("SamsungTV50in",800))box1.add(box2)console.log("总价:",box1.getPrice())在上面的例子中,我们可以把产品放在Box中,我们可以把这个Box放在其他的Box中,这是一个经典的组合例子,因为我们要实现的是得到完整的发货价格,所以需要在大箱子里加上每个元素的价格(包括每个小箱子的价格)。上面运行的结果:AddingaBubblegumtotheboxAddingaSamsungNote20totheboxAddingaSamsungTV20intotheboxAddingaSamsungTV50intotheboxAddingaAboxwith2productstotheboxTotalprice:2105.5因此,在处理遵循同一接口的多个对象时,请考虑使用此模式。通过将复杂性隐藏在单个实体(组合本身)中,您会发现它有助于简化您与团队的互动方式。今天的分享就到这里,感谢大家的收看,我们下期再见。作者:FernandoDoglio译者:前端小智来源:medium原文:https://blog.bitsrc.io/design-patterns-in-typescript-e9f84de40449本文转载自微信公众号《伟大的走向世界》,可以使用以下二维码关注。转载本文请联系大千世界公众号。
