Шаблон проектирования — это шаблон, который решает часто повторяющуюся проблему при проектировании программного обеспечения.

Паттерн состояния — это поведенческий паттерн, который позволяет объекту изменять свое поведение при изменении его внутреннего состояния.

Здесь вы узнаете, как использовать шаблон состояния в TypeScript.

Что такое шаблон состояния?

Шаблон проектирования состояния тесно связан с конечным автоматом, который описывает программу, существующую в конечный количество состояний в любой момент и ведет себя по-разному в каждом состоянии.

Существуют ограниченные, предопределенные правила — переходы — которые управляют другими состояниями, в которые может переключаться каждое состояние.

Для контекста, в интернет-магазине, если заказ клиента был «доставлен», его нельзя «отменить», потому что он уже «доставлен». «Доставлено» и «Отменено» — это конечные состояния заказа, и заказ будет вести себя по-разному в зависимости от его состояния.

Шаблон состояния создает класс для каждого возможного состояния с поведением, зависящим от состояния, содержащимся в каждом классе.

instagram viewer

Пример приложения, основанного на состоянии

Например, предположим, что вы создаете приложение, отслеживающее состояние статьи для издательской компании. Статья может либо ожидать одобрения, быть написана писателем, отредактирована редактором или опубликована. Это конечные состояния статьи, которую нужно опубликовать; в каждом уникальном состоянии статья ведет себя по-разному.

Вы можете визуализировать различные состояния и переходы приложения статьи с помощью диаграммы состояний ниже:

Реализуя этот сценарий в коде, вам сначала нужно объявить интерфейс для статьи:

интерфейсСтатьяИнтерфейс{
подача(): пустота;
черновик(): пустота;
редактировать(): пустота;
публиковать(): пустота;
}

Этот интерфейс будет иметь все возможные состояния приложения.

Далее создадим приложение, реализующее все методы интерфейса:

// Приложение
сортСтатьяреализуетСтатьяИнтерфейс{
конструктор() {
этот.showCurrentState();
}

частныйпоказать текущее состояние(): пустота{
//...
}

публичныйподача(): пустота{
//...
}

публичныйчерновик(): пустота{
//...
}

публичныйредактировать(): пустота{
//...
}

публичныйпубликовать(): пустота{
//...
}
}

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

Обработка переходов состояний

Далее вам нужно будет обработать переходы между состояниями. Обработка перехода состояния в вашем классе приложения потребует многих условные операторы. Это приведет к повторяющемуся коду, который будет труднее читать и поддерживать. Чтобы решить эту проблему, вы можете делегировать логику перехода для каждого состояния в отдельный класс.

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

Например:

абстрактныйсортСтатьяШтатреализуетСтатьяИнтерфейс{
шаг (): Статус статьи {
бросатьновыйОшибка(«Неверная операция: невозможно выполнить задачу в Текущее состояние");
}

draft(): СтатусСтатьи {
бросатьновыйОшибка(«Неверная операция: невозможно выполнить задачу в Текущее состояние");
}

edit (): Статус статьи {
бросатьновыйОшибка(«Неверная операция: невозможно выполнить задачу в Текущее состояние");
}

опубликовать (): Статус статьи {
бросатьновыйОшибка(«Неверная операция: невозможно выполнить задачу в Текущее состояние");
}
}

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

Каждое приложение имеет состояние бездействия, которое инициализирует приложение. Состояние ожидания для этого приложения установит приложение в черновик состояние.

Например:

сортPendingDraftStateрасширяетСтатьяШтат{
шаг (): Статус статьи {
возвращатьсяновый Состояние Проекта();
}
}

подача метод в классе выше инициализирует приложение, устанавливая текущее состояние в DraftState.

Затем переопределите остальные методы следующим образом:

сортDraftStateрасширяетСтатьяШтат{
draft(): СтатусСтатьи {
возвращатьсяновый Состояние Редактирования();
}
}

Этот код переопределяет черновик метод и возвращает экземпляр состояние редактирования.

сортсостояние редактированиярасширяетСтатьяШтат{
edit (): Статус статьи {
возвращатьсяновый ОпубликованоСостояние();
}
}

Блок кода выше переопределяет редактировать метод и возвращает экземпляр Опубликованное состояние.

сортОпубликованное состояниерасширяетСтатьяШтат{
опубликовать (): Статус статьи {
возвращатьсяновый Состояние Ожидания Проекта();
}
}

Блок кода выше переопределяет публиковать метод и возвращает приложение в состояние ожидания, PendingDraftState.

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

частный состояние: Состояние статьи = новый Состояние Ожидания Проекта();

Далее обновите показать текущее состояние метод для печати текущего значения состояния:

частныйпоказать текущее состояние(): пустота{
консоль.бревно(этот.состояние);
}

показать текущее состояние Метод записывает текущее состояние приложения в консоль.

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

Например, обновите свои приложения подача метод к блоку кода ниже:

публичныйподача(): пустота{
этот.состояние = этот.состояние.шаг();
этот.showCurrentState();
}

В приведенном выше блоке кода подача метод изменяет состояние с текущего состояния на состояние основного тона.

Точно так же все другие методы изменят состояние с текущего состояния приложения на соответствующие им состояния.

Обновите методы вашего приложения до блоков кода ниже:

черновик метод:

публичныйчерновик(): пустота{
этот.состояние = этот.состояние.драфт();
этот.showCurrentState();
}

редактировать метод:

публичныйредактировать(): пустота{
этот.состояние = этот.состояние.редактировать();
этот.showCurrentState();
}

И публиковать метод:

публичныйпубликовать(): пустота{
этот.состояние = этот.состояние.опубликовать();
этот.showCurrentState();
}

Использование готового приложения

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

// Приложение
сортСтатьяреализуетСтатьяИнтерфейс{
частный состояние: Состояние статьи = новый Состояние Ожидания Проекта();

конструктор() {
этот.showCurrentState();
}

частныйпоказать текущее состояние(): пустота{
консоль.бревно(этот.состояние);
}

публичныйподача(): пустота{
этот.состояние = этот.состояние.шаг();
этот.showCurrentState();
}

публичныйчерновик(): пустота{
этот.состояние = этот.состояние.драфт();
этот.showCurrentState();
}

публичныйредактировать(): пустота{
этот.состояние = этот.состояние.редактировать();
этот.showCurrentState();
}

публичныйпубликовать(): пустота{
этот.состояние = этот.состояние.опубликовать();
этот.showCurrentState();
}
}

Вы можете протестировать переходы между состояниями, вызвав методы в правильной последовательности. Например:

константа документы = новый Статья(); // PendingDraftState: {}

docs.pitch(); // DraftState: {}
docs.draft(); // Состояние редактирования: {}
документы.редактировать(); // Состояние публикации: {}
документы.опубликовать(); // PendingDraftState: {}

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

Если вы попытаетесь изменить состояние недопустимым способом, например, из состояния подачи в состояние редактирования, приложение выдаст ошибку:

константа документы = новый Статья(); // PendingDraftState: {}
docs.pitch() // DraftState: {}
docs.edit () // Неверная операция: невозможно выполнить задачу в текущем состоянии

Вы должны использовать этот шаблон только в том случае, если:

  • Вы создаете объект, который ведет себя по-разному в зависимости от его текущего состояния.
  • Объект имеет множество состояний.
  • Поведение, зависящее от состояния, часто меняется.

Преимущества и недостатки шаблона состояний

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