Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

What are the TypeScript design patterns?

2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "what are TypeScript design patterns". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn what TypeScript design patterns are.

Design patterns are templates that can help developers solve problems. There are too many patterns involved in this article, and they tend to address different needs. However, they can be divided into three different groups:

Structural patterns deal with the relationships between different components (or classes) and form new structures to provide new functions. Examples of structural patterns are Composite, Adapter, and Decorator.

The behavior pattern abstracts the common behavior between components into an independent entity. Examples of behavior patterns are commands, strategies, and one of my personal favorites: observer mode.

The creation pattern focuses on class instantiation, making it easier for us to create new entities. I'm talking about factory methods, singletons and abstract factories.

Singleton mode

The singleton pattern is probably one of the most famous design patterns. It is a creation mode because it ensures that no matter how many times we try to instantiate a class, we have only one instance available.

Dealing with database connections and the like can be singleton mode because we want to process only one at a time without having to reconnect when each user requests.

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 ())})

Now, although you cannot instantiate a class directly, you can use the getInstance method to ensure that there will not be multiple instances. In the above example, you can see how the pseudo-class that wraps the database connection benefits from this schema.

This example shows that no matter how many times we call the getInstance method, the connection is always the same.

The result of the above operation:

0.4047087250990713 0.4047087250990713 0.4047087250990713

Factory model

The factory pattern is a creation pattern, just like the singleton pattern. However, this pattern does not work directly on the objects we care about, but is only responsible for managing its creation.

Let's explain this: suppose we write code to simulate mobile vehicles, there are many types of cars, such as cars, bicycles, and airplanes, the mobile code should be encapsulated in each vehicle class, but the code that calls their move methods can be generic.

The question here is how to deal with object creation? You can have a single creator class with three methods, or a method that receives parameters. In either case, extending this logic to support the creation of more vehices requires the growing number of the same classes.

However, if you decide to use the factory method mode, you can do the following:

Now, the code required to create a new object is encapsulated in a new class, each corresponding to a vehicle type. This ensures that if you need to add a vehicle in the future, you only need to add a new class and you don't need to modify anything that already exists.

Let's take a look at how we can use TypeScript to do this:

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 is "abstract" Because no one is going to instantiate it instantiate it / / We are going to extend it and implement the abstract method abstract class VehicleHandler {/ / this is the method that the real processor needs to implement 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 ()

There is a lot of code above, but we can use the diagram above to understand it. In essence, in the end, we are concerned with custom handlers, which are called handlers, not creators, because they not just create objects, they also have logic to use them (moveVehicle methods).

The beauty of this pattern is that if you add a new vehicle type, all you have to do is add its vehicle class and its handler class without adding the LOC of any other class.

Observer mode

Of all the patterns, my favorite is the observer pattern, because of the type of behavior we can implement it.

How does it work? In essence, the pattern indicates that you have a set of observer objects that will respond to changes in the state of the entity being observed. To achieve this, once a change is received at the observed terminal, it is responsible for notifying its observer by calling one of its methods.

In practice, the implementation of this pattern is relatively simple, so let's take a quick look at the code and then review

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 ()

As you can see, through two abstract classes, we can define Observer, and the observer will represent the object that responds to changes on the Observable entity. In the above example, we assume that we have an InputElement entity that is clicked (similar to the way we have a HTML input field at the front end) and a ConsoleLogger to record everything that happens to the console.

The advantage of this model is that it allows us to understand and react to the internal state of Observable without having to mess up its internal code. We can continue to add observers who perform other actions, even those who react to specific events, and then let their code decide what to do for each notification.

Decoration mode

Decoration mode attempts to add behavior to existing objects at run time. In a sense, we can think of it as dynamic inheritance, because even if we don't create a new class to add behavior, we are creating new objects with extended capabilities.

Consider this: suppose we have a Dog class with the move method, and now you want to extend its behavior because we want a super dog and a dog that can swim.

Typically, we need to add move behavior to the Dog class, and then extend the class in two ways, namely, the SuperDog class and the SwimmingDog class. However, if we want to mix the two, we must create a new class again to extend their behavior, but there is a better way.

Composition allows us to encapsulate custom behaviors in different classes and then use this pattern to create new instances of those classes by passing the original objects to their constructors. Let's take a look at the code:

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 ()

Note a few details:

In fact, the SuperDecorator class extends the Animal class, extending the same class as the Dog class. This is because the decorator needs to provide the same public interface as the class it is trying to decorate.

The SuperDecorator class is abstract, which means that it is not used, but is used to define the constructor, which keeps a copy of the original object in the protected property. The coverage of the public interface is done inside the custom decorator.

SuperAnimal and SwimmingAnimal are actual decorators, and they are decorators that add extra behavior.

The advantage of this setting is that since all decorators also indirectly extend the Animal class, if you want to mix the two behaviors together, you can do the following:

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

Composite (combination)

About the Composite pattern, it is actually the combination pattern, also known as the partial whole pattern, which is also often used in our lives.

For example, if you have written a front-end page, you must have used it.

The tags define some formats, and then the formats are combined with each other and organized into a corresponding structure in a recursive way, which is actually a combination, embedding some of the components into the whole.

The interesting thing about this pattern is that it is not a simple group of objects, it can contain entities or groups of entities, and each group can contain more groups at the same time, which is what we call a tree.

Look at an example:

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", 1005) box1.add (new Product ("Samsung Note 20", 1005)) const box2 = new Box () box2.add (new Product ("Samsung TV 20in", 1005) box2.add (new Product ("Samsung TV 50in", 800) box1.add (box2) console.log ("Total price:", box1.getPrice ())

In the above example, we can put product in Box or Box in other Box, which is a classic example of composition. Because what we want to achieve is to get the full delivery price, we need to add the price of each element in the big box (including the price of each small box).

The result of running above:

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 an A box with 2 products to the box Total price: 2105.5

Therefore, consider using this pattern when working with multiple objects that follow the same interface. By hiding complexity in a single entity (the combination itself), you will find that it helps to simplify the way you interact with the team.

Thank you for your reading, the above is the content of "what are the TypeScript design patterns?" after the study of this article, I believe you have a deeper understanding of what TypeScript design patterns have, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report