Patrones de diseño front-end y principios de diseño patrones de diseño

Como desarrollador front-end, practicará más o menos patrones de diseño al codificar, pero ¿sabe qué patrones de diseño está utilizando?

¿Por qué los desarrolladores front-end deben comprender los patrones de diseño?

Si no sigues los patrones de diseño al codificar, ¿qué puedes hacer?

Las preguntas anteriores se pueden dejar para pensar. Aquí primero presentamos algunos patrones y principios de diseño que a menudo se encuentran en el desarrollo front-end.

Patrones de diseño front-end comunes

1 modo singleton

Un patrón que permite crear solo una instancia en toda la aplicación.
En el desarrollo front-end, a menudo se utiliza para gestionar el estado o los recursos globales, como por ejemplo:

  • En aplicaciones React , use la biblioteca Redux para administrar el estado de la aplicación;
  • En una aplicación Vue , la biblioteca Vuex se utiliza para gestionar el estado de la aplicación;
  • En aplicaciones Angular , el Servicio se utiliza para gestionar el estado de la aplicación;

Los servicios angulares son clases inyectables que se utilizan para proporcionar datos, funcionalidad o lógica compartida a los componentes de toda la aplicación; dado que los servicios existen en forma de singleton, se devolverá la misma instancia cada vez que se inyecta el servicio .

El uso @Injectable({ providedIn: 'root' })del decorador se MyServiceregistrará como proveedor de nivel raíz, lo que significa que toda la aplicación tiene acceso a una única instancia del servicio.

// my.service.ts

import {
    
     Injectable } from '@angular/core';

@Injectable({
    
    
  providedIn: 'root'
})

export class MyService {
    
    
  private count: number = 0;

  incrementCount() {
    
    
    this.count++;
  }

  getCount(): number {
    
    
    return this.count;
  }
}

En componentes, este servicio se puede utilizar mediante inyección de dependencia ;

// my.component.ts

import {
    
     Component } from '@angular/core';
import {
    
     MyService } from './my.service';

@Component({
    
    
  selector: 'my-component',
  template: `
    <h2>Count: {
     
     { myService.getCount() }}</h2>
    <button (click)="incrementCount()">Increment Count</button>
  `
})
export class MyComponent {
    
    
  constructor(private myService: MyService) {
    
    }

  incrementCount() {
    
    
    this.myService.incrementCount();
  }
}

expandir

Si no se usa en la configuración @Injectablede un decorador en Angular , pero se especifica en otro módulo o componente, el servicio se convertirá en un singleton dentro del alcance de ese módulo o componente .providedIn"root"

Esto significa que el servicio compartirá la misma instancia dentro de un módulo o componente específico y sus subcomponentes, en lugar de en toda la aplicación. Esto es útil para escenarios donde es necesario compartir datos o funcionalidades dentro de un alcance específico .

Por ejemplo, supongamos que tiene dos componentes ComponentAy ComponentBambos hacen referencia al mismo servicio SharedServicey configuran el servicio como proveedor en sus respectivos módulos.

// shared.service.ts

import {
    
     Injectable } from '@angular/core';

@Injectable({
    
    
  providedIn: 'other-module' // 指定其他模块,而不是 'root'
})
export class SharedService {
    
    
  public sharedData: string = 'Shared data';
}

// component-a.component.ts

import {
    
     Component } from '@angular/core';
import {
    
     SharedService } from './shared.service';

@Component({
    
    
  selector: 'component-a',
  template: `
    <h2>{
     
     { sharedService.sharedData }}</h2>
  `
})
export class ComponentA {
    
    
  constructor(public sharedService: SharedService) {
    
    }
}

// component-b.component.ts

import {
    
     Component } from '@angular/core';
import {
    
     SharedService } from './shared.service';

@Component({
    
    
  selector: 'component-b',
  template: `
    <h2>{
     
     { sharedService.sharedData }}</h2>
  `
})
export class ComponentB {
    
    
  constructor(public sharedService: SharedService) {
    
    }
}

En este caso, la misma instancia SharedServicese comparte entre los componentes y sus subcomponentes, pero su alcance es único ComponentA.ComponentB

Este uso permite a los desarrolladores tener un control más detallado sobre el alcance compartido de los servicios, de modo que diferentes módulos o componentes puedan tener instancias de servicio independientes.

2 modo observador

2.1 Vue.js

Una aplicación común del patrón de observador en el front-end es Vue.jsun sistema reactivo que utiliza un marco. Vue.jsUtilice el patrón Observer para realizar un seguimiento de los cambios en los datos y actualizar las vistas.

<!-- index.html -->

<!DOCTYPE html>
<html>
<head>
    <title>Vue.js Observer Example</title>
    <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
</head>
<body>
    <div id="app">
        <h2>{
   
   { message }}</h2>
        <input v-model="message" type="text" placeholder="Type something...">
    </div>

    <script>
        // Initialize Vue
        new Vue({
      
      
            el: '#app',
            data: {
      
      
                message: 'Hello, Vue!'
            }
        });
    </script>
</body>
</html>

El código anterior se utiliza Vue.jspara crear una aplicación simple con enlace de datos bidireccional. El valor del atributo del mensaje se mostrará en la etiqueta h2 de la página y se puede editar a través del cuadro de entrada.

Vue.jsEl sistema responsivo messageactualizará automáticamente el contenido correspondiente en la página cuando los atributos cambien. Esto lo logra Vue.js usando el patrón Observer internamente . Cuando el valor del atributo del mensaje cambie, se notificará al observador y realizará las actualizaciones correspondientes.

La aplicación de este patrón de observador elimina la necesidad de que los desarrolladores modifiquen explícitamente el DOM para actualizar la vista, sino que solo deben prestar atención a los cambios de datos y Vue.js manejará automáticamente el proceso de actualización. Esto simplifica enormemente la tarea de manejar las actualizaciones de vistas en el desarrollo front-end.

2.2 angulares

En AngularPython, el patrón Observer se implementa principalmente utilizando RxJSla biblioteca (Extensiones reactivas). RxJSEs una poderosa biblioteca de procesamiento de eventos que proporciona una variedad de implementaciones de patrones de operador y observador para manejar flujos de eventos asincrónicos.

En Angular, al utilizar Observables(objetos observables) y Subjects(temas), los desarrolladores pueden implementar los efectos del patrón de observador y realizar comunicación de eventos o compartir datos entre componentes .

A continuación se muestra un ejemplo sencillo que muestra cómo Angularutilizar el patrón Observer para implementar la comunicación entre componentes:

// message.service.ts

import {
    
     Injectable } from '@angular/core';
import {
    
     Subject } from 'rxjs';

@Injectable()
export class MessageService {
    
    
  private messageSubject = new Subject<string>();

  message$ = this.messageSubject.asObservable();

  sendMessage(message: string) {
    
    
    this.messageSubject.next(message);
  }
}

El código anterior crea un MessageServiceservicio llamado , que Subjectcrea un tema de mensaje usando . Al llamar a next()métodos sobre un tema, puede enviar mensajes a los observadores suscritos al tema.

// component-a.component.ts

import {
    
     Component } from '@angular/core';
import {
    
     MessageService } from './message.service';

@Component({
    
    
  selector: 'component-a',
  template: `
    <button (click)="sendMessage()">Send Message</button>
  `
})
export class ComponentA {
    
    
  constructor(private messageService: MessageService) {
    
    }

  sendMessage() {
    
    
    this.messageService.sendMessage('Hello from Component A!');
  }
}

El código anterior crea un ComponentAcomponente llamado , al que se hace referencia mediante inyección de dependencia MessageService. En el evento de clic del botón, llamamos sendMessage()a un método para enviar un mensaje.

// component-b.component.ts

import {
    
     Component } from '@angular/core';
import {
    
     MessageService } from './message.service';

@Component({
    
    
  selector: 'component-b',
  template: `
    <h2>{
     
     { receivedMessage }}</h2>
  `
})
export class ComponentB {
    
    
  receivedMessage: string = '';

  constructor(private messageService: MessageService) {
    
    }

  ngOnInit() {
    
    
    this.messageService.message$.subscribe(message => {
    
    
      this.receivedMessage = message;
    });
  }
}

El código anterior crea un ComponentBcomponente llamado y se suscribe al observable ngOnInit()en el gancho del ciclo de vida . Una vez que se envía un nuevo mensaje, el observador recibirá el mensaje y actualizará las propiedades.MessageServicemessage$receivedMessage

Usando el código anterior ComponentA, cuando se hace clic en el botón, se ComponentBenviará un mensaje y ComponentBse actualizará el mensaje mostrado en. Esto permite la comunicación bidireccional entre componentes a través del patrón Observer .

2.3 Reaccionar

En , el patrón de observador se puede implementar Reactutilizando las funciones y de gancho. Context APIHe aquí un ejemplo sencillo:

Primero, cree un contexto de observador ( ObserverContext):

import React, {
    
     createContext, useContext, useState } from 'react';

const ObserverContext = createContext();

export const ObserverProvider = ({
     
      children }) => {
    
    
  const [observers, setObservers] = useState([]);

  const addObserver = (observer) => {
    
    
    setObservers((prevObservers) => [...prevObservers, observer]);
  };

  const removeObserver = (observer) => {
    
    
    setObservers((prevObservers) =>
      prevObservers.filter((o) => o !== observer)
    );
  };

  const notifyObservers = () => {
    
    
    observers.forEach((observer) => observer());
  };

  const contextValue = {
    
    
    addObserver,
    removeObserver,
    notifyObservers,
  };

  return (
    <ObserverContext.Provider value={
    
    contextValue}>
      {
    
    children}
    </ObserverContext.Provider>
  );
};

export const useObserver = (observer) => {
    
    
  const {
    
     addObserver, removeObserver } = useContext(ObserverContext);

  // 添加观察者
  useEffect(() => {
    
    
    addObserver(observer);

    // 组件卸载时移除观察者
    return () => removeObserver(observer);
  }, [observer, addObserver, removeObserver]);
};

Luego, utilice enlaces en los componentes que deben observarse useObserverpara suscribirse y responder a los cambios.

import React, {
    
     useState } from 'react';
import {
    
     useObserver } from './ObserverContext';

const Counter = () => {
    
    
  const [count, setCount] = useState(0);

  const handleIncrement = () => {
    
    
    setCount((prevCount) => prevCount + 1);
  };

  // 添加观察者
  useObserver(() => {
    
    
    console.log('Count has changed:', count);
  });

  return (
    <div>
      <p>Count: {
    
    count}</p>
      <button onClick={
    
    handleIncrement}>Increment</button>
    </div>
  );
};

En el ejemplo anterior, ObserverProviderse proporciona el contexto del observador y se definen los métodos para agregar, eliminar y notificar al observador. Utilice useObserverganchos para suscribirse a cambios en el patrón del observador. Cuando el estado (aquí count) cambie, el observador será notificado y realizará las acciones apropiadas.

Con este enfoque, Reactse puede implementar un patrón de observador simple para que los componentes puedan suscribirse y responder a eventos específicos o cambios de estado.

Patrón de fábrica de 3 códigos

El patrón de fábrica de código es un patrón de diseño para crear objetos y encapsula el proceso de creación de objetos mediante el uso de funciones o clases de fábrica . En este modo, no llamamos directamente al constructor del objeto para crear el objeto, sino que utilizamos un método de fábrica especial para administrar de manera uniforme la creación del objeto .

El objetivo principal del patrón Code Factory es ocultar los detalles de la creación de objetos específicos y proporcionar una forma extensible y flexible de crear objetos. Encapsula la lógica de creación de instancias de un objeto en un componente independiente, de modo que el proceso de creación de un objeto se pueda gestionar de forma centralizada en lugar de estar disperso por toda la aplicación.

3.1 angulares

A continuación se muestra un ejemplo sencillo que muestra cómo Angularutilizar el patrón Code Factory en:

import {
    
     Injectable } from '@angular/core';

@Injectable({
    
    
  providedIn: 'root',
})
export class UserService {
    
    
  private users: string[] = [];

  addUser(user: string): void {
    
    
    this.users.push(user);
  }

  getUsers(): string[] {
    
    
    return this.users;
  }
}

@Injectable({
    
    
  providedIn: 'root',
})
export class UserFactory {
    
    
  constructor(private userService: UserService) {
    
    }

  createUser(name: string): void {
    
    
    this.userService.addUser(name);
  }
}

En el ejemplo anterior, UserServicees una clase de servicio que gestiona la información del usuario. UserFactoryes una clase de fábrica responsable de crear usuarios y agregarlos a UserService.

En Angular, al utilizar @Injectable()el decorador y providedIn: 'root'las opciones, podemos registrarnos UserServicecomo UserFactoryservicios inyectables y asegurarnos de que estén disponibles en cualquier componente de la aplicación.

Estos servicios luego se pueden utilizar en otros componentes mediante inyección de dependencia:

import {
    
     Component } from '@angular/core';
import {
    
     UserFactory } from './user.factory';

@Component({
    
    ...})
export class AppComponent {
    
    
  constructor(private userFactory: UserFactory) {
    
    }

  createUser() {
    
    
    this.userFactory.createUser('John');
  }
}

En el ejemplo anterior, AppComponentel componente utiliza el patrón de fábrica para crear el usuario mediante inyección de dependencia UserFactory.

Al Angularutilizar el patrón de fábrica de código en , podemos encapsular la lógica de creación e inicialización de objetos en servicios inyectables y aprovechar la inyección de dependencias para utilizar estos servicios de manera conveniente cuando sea necesario. Esto mejora la capacidad de mantenimiento, escalabilidad y capacidad de prueba del código.

3.2 Reaccionar

A continuación se muestra un ejemplo sencillo que muestra cómo Reactutilizar el patrón Code Factory en:

import React from 'react';

// 工厂函数
function createButton(type) {
    
    
  const Button = (props) => {
    
    
    let button;

    if (type === 'primary') {
    
    
      button = (
        <button className="primary-button" onClick={
    
    props.onClick}>
          {
    
    props.children}
        </button>
      );
    } else if (type === 'secondary') {
    
    
      button = (
        <button className="secondary-button" onClick={
    
    props.onClick}>
          {
    
    props.children}
        </button>
      );
    } else {
    
    
      throw new Error('Invalid button type');
    }

    return button;
  };

  return Button;
}

// 使用工厂函数创建按钮组件
const PrimaryButton = createButton('primary');
const SecondaryButton = createButton('secondary');

// 使用按钮组件
const App = () => (
  <div>
    <PrimaryButton onClick={
    
    () => console.log("Primary button clicked")}>
      Primary Button
    </PrimaryButton>
    <SecondaryButton onClick={
    
    () => console.log("Secondary button clicked")}>
      Secondary Button
    </SecondaryButton>
  </div>
);

En el ejemplo anterior, createButtones una función de fábrica que typedevuelve un componente de botón de un tipo específico según los parámetros pasados. Cree botones con diferentes estilos, comportamientos o funciones según diferentes tipos.

Al llamar a createButtonlas funciones de fábrica, puede crear fácilmente diferentes tipos de componentes de botones y usarlos en su aplicación. De esta manera, evita escribir repetidamente código similar en varios lugares y, en su lugar, administra y crea componentes de forma centralizada a través del patrón de fábrica.

Utilizando el patrón de fábrica, se pueden crear y personalizar rápidamente múltiples componentes según sea necesario, y modificarlos o ampliarlos fácilmente. Este patrón proporciona una forma más flexible, fácil de mantener y reutilizable de crear y gestionar Reactcomponentes.

4 modo de estrategia

El patrón de estrategia se utiliza para definir una serie de algoritmos y encapsularlos en objetos independientes para que puedan reemplazarse entre sí. En el desarrollo front-end, el patrón de estrategia se puede utilizar para manejar múltiples algoritmos o situaciones lógicas, como la validación de acuerdo con diferentes reglas en la validación de formularios.

A continuación se muestra un ejemplo sencillo para ordenar una matriz según diferentes estrategias de clasificación :

// 排序策略对象
const sortingStrategies = {
    
    
  quickSort: (arr) => arr.sort((a, b) => a - b),
  mergeSort: (arr) => arr.sort((a, b) => b - a),
};

// 排序上下文对象
class SortContext {
    
    
  constructor(strategy) {
    
    
    this.strategy = strategy;
  }

  setStrategy(strategy) {
    
    
    this.strategy = strategy;
  }

  sort(arr) {
    
    
    return this.strategy(arr);
  }
}

// 使用策略模式进行排序
const arr = [5, 2, 8, 1, 4];
const context = new SortContext(sortingStrategies.quickSort);
console.log(context.sort(arr)); // 输出: [1, 2, 4, 5, 8]

context.setStrategy(sortingStrategies.mergeSort);
console.log(context.sort(arr)); // 输出: [8, 5, 4, 2, 1]

En el ejemplo anterior, sortingStrategieses un objeto que contiene diferentes estrategias de clasificación, donde quickSorty mergeSortson dos algoritmos de clasificación diferentes.

SortContextEs un objeto de contexto de clasificación que recibe una estrategia de clasificación como parámetro y proporciona setStrategymétodos para cambiar dinámicamente la estrategia de clasificación actual. sortEl método ordena la matriz dada utilizando la estrategia de clasificación actual.

Al crear SortContextinstancias y establecer diferentes estrategias de clasificación, podemos elegir un algoritmo de clasificación específico para ordenar la matriz según nuestras necesidades. De esta manera, podemos cambiar de algoritmos de manera flexible según las necesidades en tiempo de ejecución sin modificar la lógica de clasificación en todas partes.

El patrón Estrategia hace que las aplicaciones sean más escalables y flexibles porque podemos agregar fácilmente nuevas estrategias o modificar las existentes sin modificar el código existente . Al mismo tiempo, mejora la legibilidad y el mantenimiento del código al desacoplar la selección del algoritmo de la lógica de ejecución real.

Supongo que te gusta

Origin blog.csdn.net/weixin_45678402/article/details/132810545
Recomendado
Clasificación