Вы освоили эти девять часто используемых шаблонов проектирования?

Шаблоны проектирования TypeScript
Вы освоили эти девять часто используемых шаблонов проектирования?

В программной инженерии шаблоны проектирования — это решения различных проблем, которые преобладают (повторяющиеся) при разработке программного обеспечения. По назначению шаблоны проектирования GoF (Gang of Four) можно разделить на следующие три типа:

1. Режим создания: используется для описания того, «как создавать объекты», его основная особенность — «разделение создания и использования объектов». Включая 5 шаблонов синглтона, прототипа, фабричного метода, абстрактной фабрики и построителя.

2. Структурный шаблон: он используется для описания того, как организовать класс или объект в более крупную структуру в соответствии с определенным макетом. В том числе 7 режимов прокси, адаптера, моста, оформления, внешнего вида, привеса и композиции.

3. Поведенческие модели: используются для выявления общих моделей общения между объектами и распределения обязанностей. В том числе шаблонный метод, стратегия, команда, цепочка ответственности, состояние, наблюдатель, посредник, итератор, посетитель, памятка и интерпретатор 11 шаблонов.

Затем брат А Бао познакомит вас с 9 часто используемыми шаблонами дизайна, комбинируя некоторые сцены из жизни и используя красивые картинки.

Режим строителя

Шаблон Builder разбивает сложный объект на относительно простые части, затем создает их отдельно в соответствии с различными потребностями и, наконец, строит сложный объект.

Маленький автомобиль 🚗 обычно состоит изДвигатель, шасси, кузов и электрооборудованиеОн состоит из четырех частей. Внутренняя структура автомобильного электрооборудования сложна, для простоты рассмотрим только три части: двигатель, шасси и кузов.

В реальной жизни автомобили тоже собираются из разных частей, например, на картинке выше мы разделяем автомобиль на три части: двигатель, шасси и кузов. Давайте посмотрим, как использовать режим строителя для сборки автомобиля.

1.1 Код реализации
class Car {
  constructor(
    public engine: string,
    public chassis: string, 
    public body: string
  ) {}
}

class CarBuilder {
  engine!: string; // 引擎
  chassis!: string; // 底盘
  body!: string; // 车身

  addChassis(chassis: string) {
    this.chassis = chassis;
    return this;
  }

  addEngine(engine: string) {
    this.engine = engine;
    return this;
  }

  addBody(body: string) {
    this.body = body;
    return this;
  }

  build() {
    return new Car(this.engine, this.chassis, this.body);
  }
}

В приведенном выше коде мы определяемCarBuilderкласс и обеспечиваетaddChassis,addEngineа такжеaddBody3 метода используются для сборки различных частей автомобиля. Когда три части автомобиля собраны, они называются.buildспособ начать сборку автомобиля.

1.2 Пример использования
const car = new CarBuilder()
  .addEngine('v12')
  .addBody('镁合金')
  .addChassis('复合材料')
  .build();
1.3 Сценарии и случаи применения
  • Объекты продукта, которые необходимо сгенерировать, имеют сложную внутреннюю структуру, и эти объекты продукта обычно содержат несколько свойств-членов.
  • Свойства объектов продукта, которые необходимо сгенерировать, взаимозависимы, и необходимо указать порядок, в котором они создаются.
  • Изолируйте создание и использование сложных объектов и включите один и тот же процесс создания для создания различных продуктов.
  • Github - node-sql-query:GitHub.com/geosen's/no…

Во-вторых, заводской режим

В реальной жизни фабрики отвечают за производство продуктов, таких как молоко, хлеб или подарки, которые удовлетворяют наши повседневные физиологические потребности.

Среди множества шаблонов проектирования есть шаблон factory, который обеспечивает наилучший способ создания объектов. Фабричный узор можно разделить на:Простой заводской шаблон, заводской шаблон метода и абстрактный заводской шаблон.

2.1 Простая фабрика

Простая заводская модель также называетсяшаблон статического метода, потому что фабричный класс определяет статический метод для создания объектов. Простые фабрики позволяют пользователям создавать желаемый класс «продукта», не зная конкретных параметров, то есть пользователь может напрямую потреблять продукт, не зная конкретных деталей производства продукта.

На картинке выше брат А. Бао смоделировал процесс покупки автомобиля. Сяо Ван и Сяо Цинь заказали на заводе BMW модели BMW 730 и BMW 840 соответственно. Затем завод сначала оценивает модель, выбранную пользователем, а затем производит в соответствии с соответствующей моделью и доставляется пользователям после завершения производства.

Давайте посмотрим, как использовать простую фабрику для описания процесса, с помощью которого завод BMW производит автомобиль данной модели.

2.1.1 Код реализации
abstract class BMW {
  abstract run(): void;
}

class BMW730 extends BMW {
  run(): void {
    console.log("BMW730 发动咯");
  }
}

class BMW840 extends BMW {
  run(): void {
    console.log("BMW840 发动咯");
  }
}

class BMWFactory {
  public static produceBMW(model: "730" | "840"): BMW {
    if (model === "730") {
      return new BMW730();
    } else {
      return new BMW840();
    }
  }
}

В приведенном выше коде мы определяемBMWFactoryкласс, который предоставляет статическийproduceBMW()метод, который используется для создания разных моделей автомобилей по разным параметрам модели.

2.1.2 Пример использования
const bmw730 = BMWFactory.produceBMW("730");
const bmw840 = BMWFactory.produceBMW("840");

bmw730.run();
bmw840.run();
2.1.3 Сценарии применения
  • Фабричный класс отвечает за создание меньшего количества объектов: поскольку создается меньше объектов, бизнес-логика в фабричном методе не будет слишком сложной.
  • Клиенту нужно знать только параметры, переданные статическому методу фабричного класса, и ему не нужно заботиться о деталях создания объекта.

2.2 Заводской метод

Фабричный шаблон метода (Factory Method Pattern), также известный как фабричный шаблон, также называемый шаблоном полиморфной фабрики (Polymorphic Factory), относится к шаблону создания класса.

В шаблоне метода фабрики родительский класс фабрики отвечает за определение открытого интерфейса для создания объектов продукта, а подкласс фабрики отвечает за создание конкретных объектов продукта.Цель этого состоит в том, чтобы отложить создание экземпляра класса продукта до подкласса фабрики., то есть через подкласс фабрики, чтобы определить, какой конкретный класс продукта должен быть создан.

На картинке выше брат А Бао смоделировал процесс покупки автомобиля Сяо Ван и Сяо Цинь заказали модели BMW 730 и BMW 840 на заводах BMW 730 и BMW 840 соответственно. завершена доставка пользователям.

Точно так же давайте посмотрим, как можно использовать заводской метод для описания процесса, посредством которого завод BMW производит автомобиль данной модели.

2.2.1 Код реализации
abstract class BMWFactory {
  abstract produceBMW(): BMW;
}

class BMW730Factory extends BMWFactory {
  produceBMW(): BMW {
    return new BMW730();
  }
}

class BMW840Factory extends BMWFactory {
  produceBMW(): BMW {
    return new BMW840();
  }
}

В приведенном выше коде мы создалиBMW730Factoryа такжеBMW840FactoryДва фабричных класса, а затем использовать экземпляры этих двух классов для производства разных моделей автомобилей.

2.2.2 Пример использования
const bmw730Factory = new BMW730Factory();
const bmw840Factory = new BMW840Factory();

const bmw730 = bmw730Factory.produceBMW();
const bmw840 = bmw840Factory.produceBMW();

bmw730.run();
bmw840.run();
2.2.3 Сценарии применения
  • Класс не знает класс нужного ему объекта: в шаблоне метода фабрики клиенту не нужно знать имя класса конкретного класса продукта, а нужно знать только соответствующую фабрику, а конкретный объект продукта созданный конкретным фабричным классом; Клиент должен знать фабричный класс, который создает конкретный продукт.
  • Класс указывает, какой объект создавать, через свой подкласс: в шаблоне фабричного метода для абстрактного фабричного класса необходимо предоставить только интерфейс для создания продукта, а его подкласс определяет конкретный создаваемый объект, используя объектно-ориентированный полиморфизм. В соответствии с принципом замены пола и Лискова, когда программа работает, объект подкласса переопределяет объект родительского класса, что упрощает расширение системы.

продолжить чтение:Фабричный метод шаблона проектирования машинописного текста

2.3 Абстрактная фабрика

Шаблон Abstract Factory предоставляет интерфейс для создания ряда связанных или взаимозависимых объектов без указания их конкретных классов.

В паттерне фабричный метод конкретная фабрика отвечает за производство конкретных продуктов, каждая конкретная фабрика соответствует конкретному продукту, а фабричный метод также уникален. конкретный завод...Но иногда нам нужна фабрика, которая может предоставить несколько объектов продукта вместо одного объекта продукта.

На картинке выше брат Абао смоделировал процесс покупки автомобиля для пользователей.Сяо Ван заказал BMW 730 на заводе BMW.Завод произвел модель в соответствии с 730 и доставил ее Сяо Вану после завершения производства. Сяо Цинь заказал BMW840 на том же заводе BMW, и завод изготовил его по модели, соответствующей 840, и доставил его Сяо Циню после завершения производства.

Давайте посмотрим, как использовать абстрактную фабрику для описания описанного выше процесса покупки автомобиля.

2.3.1 Код реализации
abstract class BMWFactory {
  abstract produce730BMW(): BMW730;
  abstract produce840BMW(): BMW840;
}

class ConcreteBMWFactory extends BMWFactory {
  produce730BMW(): BMW730 {
    return new BMW730();
  }

  produce840BMW(): BMW840 {
    return new BMW840();
  }
}
2.3.2 Пример использования
const bmwFactory = new ConcreteBMWFactory();

const bmw730 = bmwFactory.produce730BMW();
const bmw840 = bmwFactory.produce840BMW();

bmw730.run();
bmw840.run();
2.3.3 Сценарии применения
  • Система не должна зависеть от деталей создания, составления и выражения экземпляров класса продукта, что важно для всех типов фабричных шаблонов.
  • В системе имеется более одного семейства продуктов, и одновременно используется только одно из них.
  • Система предоставляет библиотеку классов продуктов, и все продукты отображаются с одинаковым интерфейсом, так что клиент не зависит от конкретной реализации.

продолжить чтение:Как лучше всего создать объект?

3. Одноэлементный режим

Шаблон Singleton является распространенным шаблоном, и есть некоторые объекты, которые нам часто нужны только по одному, такие как глобальный кеш, объект окна в браузере и т. д. Шаблон singleton используется для обеспечения существования только одного экземпляра класса и предоставления глобальной точки доступа к нему.

На картинке выше брат А Бао симулировал процесс одолжения автомобиля. Сяо Ван попросил брата А Бао одолжить машину, когда он торопился. Машина брата А Бао была просто бесполезна, поэтому он одолжил ее Сяо Вану. В тот день Сяо Цинь также нуждался в машине, поэтому он попросил брата А Бао одолжить машину, потому что у брата А Бао дома была только одна машина, поэтому у него не было машины, которую можно было бы одолжить.

Что касается автомобиля, хотя он приносит большое удобство в жизнь, содержание автомобиля также требует больших расходов (плата за парковочное место, плата за топливо и плата за обслуживание и т. д.), поэтому у брата Абао дома только одна машина. При разработке программных систем, если мы сталкиваемся с объектами, для создания которых требуется слишком много времени или ресурсов, но которые часто используются, мы можем рассмотреть возможность использования шаблона singleton.

Давайте посмотрим, как использовать TypeScript для реализации режима единого регистра.

3.1 Код реализации
class Singleton {
  // 定义私有的静态属性,来保存对象实例
  private static singleton: Singleton;
  private constructor() {}

  // 提供一个静态的方法来获取对象实例
  public static getInstance(): Singleton {
    if (!Singleton.singleton) {
      Singleton.singleton = new Singleton();
    }
    return Singleton.singleton;
  }
}
3.2 Пример использования
let instance1 = Singleton.getInstance();
let instance2 = Singleton.getInstance();

console.log(instance1 === instance2); // true
3.3 Сценарии применения
  • Объекты, которые необходимо часто создавать, а затем уничтожать.
  • Объекты, на создание которых уходит слишком много времени или ресурсов, но которые часто используются.
  • Системе нужен только один объект-экземпляр, например, системе требуется генератор уникальных серийных номеров или менеджер ресурсов, или разрешено создание только одного объекта, поскольку потребление ресурсов слишком велико.

продолжить чтение:Одноэлементный шаблон шаблона проектирования TypeScript

В-четвертых, режим адаптера

В реальной жизни также существуют сценарии использования адаптеров, таких как штепсельные преобразователи гонконгского типа, адаптеры питания и адаптеры USB.В разработке программного обеспечения роль шаблона адаптера заключается в решении проблемы несовместимости интерфейсов между двумя программными объектами.С шаблоном адаптера два программных объекта, которые в противном случае не работали бы из-за несовместимых интерфейсов, могут работать вместе.

4.1 Код реализации
interface Logger {
  info(message: string): Promise<void>;
}

interface CloudLogger {
  sendToServer(message: string, type: string): Promise<void>;
}

class AliLogger implements CloudLogger {
  public async sendToServer(message: string, type: string): Promise<void> {
    console.info(message);
    console.info('This Message was saved with AliLogger');
  }
}

class CloudLoggerAdapter implements Logger {
  protected cloudLogger: CloudLogger;

  constructor (cloudLogger: CloudLogger) {
    this.cloudLogger = cloudLogger;
  }

  public async info(message: string): Promise<void> {
    await this.cloudLogger.sendToServer(message, 'info');
  }
}

class NotificationService {
  protected logger: Logger;
  
  constructor (logger: Logger) {    
    this.logger = logger;
  }

  public async send(message: string): Promise<void> {
    await this.logger.info(`Notification sended: ${message}`);
  }
}

В приведенном выше коде, потому чтоLoggerа такжеCloudLoggerДва интерфейса не совпадают, поэтому мы вводимCloudLoggerAdapterадаптеры для решения проблем совместимости.

4.2 Пример использования
(async () => {
  const aliLogger = new AliLogger();
  const cloudLoggerAdapter = new CloudLoggerAdapter(aliLogger);
  const notificationService = new NotificationService(cloudLoggerAdapter);
  await notificationService.send('Hello semlinker, To Cloud');
})();
4.3 Сценарии и случаи применения
  • Ранее разработанная система имеет классы, соответствующие функциональным требованиям новой системы, но ее интерфейс не соответствует интерфейсу новой системы.
  • Используйте компонент, предоставленный третьей стороной, но определение интерфейса компонента отличается от определения интерфейса, требуемого вами.
  • Github - axios-mock-adapter:GitHub.com/CTI ММ и М/Ашин…

продолжить чтение:Шаблон адаптера шаблона проектирования TypeScript

5. Режим наблюдателя и режим публикации-подписки

5.1 Шаблон наблюдателя

Режим наблюдателя, определяющий отношение "один ко многим", позволяющее нескольким объектам-наблюдателям одновременно отслеживать объект-субъект.При изменении состояния объекта-субъекта все объекты-наблюдатели будут уведомлены, чтобы их можно было автоматически обновить. Собственный.

В узоре наблюдателя есть две главные роли: субъект и наблюдатель.

На картинке выше тема — тематическая статья брата А Бао о ТС, а наблюдатели — Сяо Цинь и Сяо Ван. Поскольку шаблон наблюдателя поддерживает простую широковещательную связь, все наблюдатели автоматически уведомляются об обновлении сообщения.

Давайте посмотрим, как реализовать шаблон Observer с помощью TypeScript.

5.1.1 Код реализации
interface Observer {
  notify: Function;
}

class ConcreteObserver implements Observer{
  constructor(private name: string) {}

  notify() {
    console.log(`${this.name} has been notified.`);
  }
}

class Subject { 
  private observers: Observer[] = [];

  public addObserver(observer: Observer): void {
    console.log(observer, "is pushed!");
    this.observers.push(observer);
  }

  public deleteObserver(observer: Observer): void {
    console.log("remove", observer);
    const n: number = this.observers.indexOf(observer);
    n != -1 && this.observers.splice(n, 1);
  }

  public notifyObservers(): void {
    console.log("notify all the observers", this.observers);
    this.observers.forEach(observer => observer.notify());
  }
}
5.1.2 Пример использования
const subject: Subject = new Subject();
const xiaoQin = new ConcreteObserver("小秦");
const xiaoWang = new ConcreteObserver("小王");
subject.addObserver(xiaoQin);
subject.addObserver(xiaoWang);
subject.notifyObservers();

subject.deleteObserver(xiaoQin);
subject.notifyObservers();
5.1.3 Сценарии и случаи применения

продолжить чтение:Шаблон наблюдателя шаблона проектирования TypeScript

5.2 Модель публикации-подписки

В архитектуре программного обеспечения публикация/подписка — это парадигма сообщения.Отправитель сообщения (называемый издателем) не отправляет сообщение непосредственно конкретному получателю (называемому подписчиком). Вместо этого опубликованные сообщения группируются в разные категории и отправляются разным подписчикам.Аналогичным образом подписчики могут проявлять интерес к одной или нескольким категориям и получать только интересующие их сообщения, не зная, какие существуют издатели.

В модели публикации-подписки есть три основные роли: Publisher (издатель), Channels (канал) и Subscriber (подписчик).

На приведенном выше рисунке издатель — Brother A Bao, тема A и тема B в каналах соответствуют темам TS и темам Deno соответственно, а подписчики — Xiao Qin, Xiao Wang и Xiao Chi.

Давайте посмотрим, как использовать TypeScript для реализации режима подписки на выпуск.

5.2.1 код реализации
type EventHandler = (...args: any[]) => any;

class EventEmitter {
  private c = new Map<string, EventHandler[]>();

  // 订阅指定的主题
  subscribe(topic: string, ...handlers: EventHandler[]) {
    let topics = this.c.get(topic);
    if (!topics) {
      this.c.set(topic, topics = []);
    }
    topics.push(...handlers);
  }

  // 取消订阅指定的主题
  unsubscribe(topic: string, handler?: EventHandler): boolean {
    if (!handler) {
      return this.c.delete(topic);
    }

    const topics = this.c.get(topic);
    if (!topics) {
      return false;
    }
    
    const index = topics.indexOf(handler);

    if (index < 0) {
      return false;
    }
    topics.splice(index, 1);
    if (topics.length === 0) {
      this.c.delete(topic);
    }
    return true;
  }

  // 为指定的主题发布消息
  publish(topic: string, ...args: any[]): any[] | null {
    const topics = this.c.get(topic);
    if (!topics) {
      return null;
    }
    return topics.map(handler => {
      try {
        return handler(...args);
      } catch (e) {
        console.error(e);
        return null;
      }
    });
  }
}
5.2.2 Пример использования
const eventEmitter = new EventEmitter();
eventEmitter.subscribe("ts", (msg) => console.log(`收到订阅的消息:${msg}`) );

eventEmitter.publish("ts", "TypeScript发布订阅模式");
eventEmitter.unsubscribe("ts");
eventEmitter.publish("ts", "TypeScript发布订阅模式");
5.2.3 Сценарии применения

продолжить чтение:Как элегантно реализовать обмен сообщениями?

6. Режим стратегии

Паттерн стратегии определяет ряд алгоритмов, инкапсулирует их один за другим и делает взаимозаменяемыми. В центре внимания шаблона стратегии находится не то, как реализовать алгоритмы, а то, как организовать и вызвать эти алгоритмы, чтобы сделать структуру программы более гибкой, удобной в сопровождении и расширяемой.

В настоящее время на некоторых основных веб-сайтах предусмотрено множество различных способов входа в систему. Например, логин с паролем учетной записи, логин с кодом подтверждения мобильного телефона и сторонний логин. Чтобы упростить обслуживание различных методов входа в систему, мы можем инкапсулировать разные методы входа в разные политики входа.

Давайте посмотрим, как использовать шаблон стратегии для инкапсуляции различных методов входа в систему.

6.1 Код реализации

Чтобы лучше понять следующий код, давайте сначала посмотрим на соответствующую диаграмму классов UML:

interface Strategy {
  authenticate(...args: any): any;
}

class Authenticator {
  strategy: any;
  constructor() {
    this.strategy = null;
  }

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

  authenticate(...args: any) {
    if (!this.strategy) {
      console.log('尚未设置认证策略');
      return;
    }
    return this.strategy.authenticate(...args);
  }
}

class WechatStrategy implements Strategy {
  authenticate(wechatToken: string) {
    if (wechatToken !== '123') {
      console.log('无效的微信用户');
      return;
    }
    console.log('微信认证成功');
  }
}

class LocalStrategy implements Strategy {
  authenticate(username: string, password: string) {
    if (username !== 'abao' && password !== '123') {
      console.log('账号或密码错误');
      return;
    }
    console.log('账号和密码认证成功');
  }
}
6.2 Пример использования
const auth = new Authenticator();

auth.setStrategy(new WechatStrategy());
auth.authenticate('123456');

auth.setStrategy(new LocalStrategy());
auth.authenticate('abao', '123');
6.3 Сценарии и случаи применения
  • Когда системе необходимо динамически выбирать один из нескольких алгоритмов, каждый алгоритм может быть инкапсулирован в класс стратегии.
  • Несколько классов отличаются только своим поведением производительности.Вы можете использовать шаблон стратегии для динамического выбора конкретного поведения, которое будет выполняться во время выполнения.
  • Класс определяет различные варианты поведения, и эти варианты поведения проявляются в работе класса в виде нескольких условных операторов, которые можно заменить, переместив каждую условную ветвь в соответствующий класс стратегии.
  • Github - passport-local:GitHub.com/Джаред Хэнсон…
  • Github - passport-oauth2:GitHub.com/Джаред Хэнсон…
  • Github - zod:GitHub.com/v ria/найти или найти/нет…

Семь обязательных цепочек.

Шаблон цепочки ответственности позволяет нескольким объектам иметь возможность обрабатывать запрос, тем самым избегая связывающих отношений между отправителем и получателем запроса. В шаблоне цепочки ответственности многие объекты связаны в цепочку посредством ссылки каждого объекта на его подчиненный. Запросы передаются по этой цепочке до тех пор, пока объект в цепочке не решит обработать запрос.

Разные должности в компании имеют разные обязанности и полномочия. Взяв в качестве примера описанный выше процесс запроса на отпуск, когда брат А Бао просит выходной, его нужно только утвердить руководителем группы, и его не нужно передавать супервайзеру и директору. Если звено в цепочке ответственности не может обработать текущий запрос, при наличии следующего звена запрос будет перенаправлен на следующее звено для обработки.

В ежедневном процессе разработки программного обеспечения для цепочки ответственности распространенным сценарием приложения является промежуточное программное обеспечение.Давайте посмотрим, как использовать цепочку ответственности для обработки запросов.

7.1 Код реализации

Чтобы лучше понять следующий код, давайте сначала посмотрим на соответствующую диаграмму классов UML:

interface IHandler {
  addMiddleware(h: IHandler): IHandler;
  get(url: string, callback: (data: any) => void): void;
}

abstract class AbstractHandler implements IHandler {
  next!: IHandler;
  addMiddleware(h: IHandler) {
    this.next = h;
    return this.next;
  }

  get(url: string, callback: (data: any) => void) {
    if (this.next) {
      return this.next.get(url, callback);
    }
  }
}

// 定义Auth中间件
class Auth extends AbstractHandler {
  isAuthenticated: boolean;
  constructor(username: string, password: string) {
    super();

    this.isAuthenticated = false;
    if (username === 'abao' && password === '123') {
      this.isAuthenticated = true;
    }
  }

  get(url: string, callback: (data: any) => void) {
    if (this.isAuthenticated) {
      return super.get(url, callback);
    } else {
      throw new Error('Not Authorized');
    }
  }
}

// 定义Logger中间件
class Logger extends AbstractHandler {
  get(url: string, callback: (data: any) => void) {
    console.log('/GET Request to: ', url);
    return super.get(url, callback);
  }
}

class Route extends AbstractHandler {
  URLMaps: {[key: string]: any};
  constructor() {
    super();
    this.URLMaps = {
      '/api/todos': [{ title: 'learn ts' }, { title: 'learn react' }],
      '/api/random': Math.random(),
    };
  }

  get(url: string, callback: (data: any) => void) {
    super.get(url, callback);

    if (this.URLMaps.hasOwnProperty(url)) {
      callback(this.URLMaps[url]);
    }
  }
}
7.2 Пример использования
const route = new Route();
route.addMiddleware(new Auth('abao', '123')).addMiddleware(new Logger());

route.get('/api/todos', data => {
  console.log(JSON.stringify({ data }, null, 2));
});

route.get('/api/random', data => {
  console.log(data);
});
7.3 Сценарии применения
  • Обработка запроса может представлять собой набор объектов, которые должны быть динамически назначены.
  • Хотите отправить запрос одному из нескольких объектов без явного указания получателя.
  • Есть несколько объектов, которые могут обрабатывать запрос, и какой объект обрабатывает запрос, автоматически определяется во время выполнения, и клиенту нужно только отправить запрос в цепочку.

Восемь, шаблон метода шаблона

Шаблон метода шаблона состоит из двух частей: абстрактного родительского класса и конкретного подкласса реализации.Обычно структура алгоритма подкласса инкапсулируется в абстрактный родительский класс, включая реализацию некоторых общедоступных методов и порядок выполнения всех методов в инкапсулированном подклассе.. Наследуя этот абстрактный класс, подклассы также наследуют всю структуру алгоритма и могут переопределить методы родительского класса.

На приведенном выше рисунке Abao Ge использует разные синтаксические анализаторы для анализа файлов CSV и Markup соответственно. Хотя анализируются файлы разных типов, процесс обработки файлов одинаков. В основном это включает три шага: чтение файла, анализ файла и печать данных. Для этого сценария мы можем ввести шаблонный метод для инкапсуляции последовательности обработки трех вышеуказанных шагов.

Давайте посмотрим, как использовать метод шаблона для достижения описанного выше процесса синтаксического анализа.

8.1 Код реализации

Чтобы лучше понять следующий код, давайте сначала посмотрим на соответствующую диаграмму классов UML:

import fs from 'fs';

abstract class DataParser {
  data: string = '';
  out: any = null;

  // 这就是所谓的模板方法
  parse(pathUrl: string) {
    this.readFile(pathUrl);
    this.doParsing();
    this.printData();
  }

  readFile(pathUrl: string) {
    this.data = fs.readFileSync(pathUrl, 'utf8');
  }

  abstract doParsing(): void;
  
  printData() {
    console.log(this.out);
  }
}

class CSVParser extends DataParser {
  doParsing() {
    this.out = this.data.split(',');
  }
}

class MarkupParser extends DataParser {
  doParsing() {
    this.out = this.data.match(/<\w+>.*<\/\w+>/gim);
  }
}
8.2 Пример использования
const csvPath = './data.csv';
const mdPath = './design-pattern.md';

new CSVParser().parse(csvPath);
new MarkupParser().parse(mdPath);
8.3 Сценарии применения
  • Общий шаг алгоритма очень фиксированный, но отдельные части являются переменными, и в это время можно использовать режим метода шаблона, частичное абстрагирование легко изменяемого, реализуется субсидия.
  • Когда необходимо управлять расширением подклассов, метод шаблона вызывает операцию ловушки только в определенных точках, таким образом разрешая расширение только в этих точках.

9. Справочные ресурсы