Шаблон проектирования адаптера

Это специальный объект, который преобразует интерфейс одного объекта так, чтобы его мог понять другой объект.

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

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

Как работает адаптер

  1. Адаптер получает интерфейс, совместимый с одним из существующих объектов.
  2. Используя этот интерфейс, существующий объект может безопасно вызывать методы адаптера.
  3. При получении вызова адаптер передает запрос второму объекту, но в формате и порядке, ожидаемом вторым объектом.

Адаптер объекта

  1. Клиент — это класс, содержащий существующую бизнес-логику программы.
  2. Клиентский интерфейс описывает протокол, которому должны следовать другие классы, чтобы иметь возможность взаимодействовать с клиентским кодом.
  3. Служба – это полезный класс (обычно сторонний или устаревший). Клиент не может использовать этот класс напрямую, потому что у него несовместимый интерфейс.
  4. Адаптер — это класс, способный работать как с клиентом, так и со службой: он реализует клиентский интерфейс, оборачивая при этом объект службы. Адаптер получает вызовы от клиента через интерфейс адаптера и преобразовывает их в вызовы обернутого объекта службы в понятном ему формате.

Адаптер класса

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

Осуществлять

  1. Убедитесь, что у вас есть как минимум два класса с несовместимыми интерфейсами.
  2. Объявите клиентский интерфейс и опишите, как клиенты взаимодействуют со службой.
  3. Создайте класс адаптера и заставьте его следовать клиентскому интерфейсу. Оставьте пока все методы пустыми.
  4. Добавьте в класс адаптера поле для хранения ссылки на объект службы.
  5. Один за другим реализуйте все методы клиентского интерфейса в классе адаптера.
  6. Клиенты должны использовать адаптер через клиентский интерфейс. Это позволит вам изменять или расширять адаптеры, не затрагивая клиентский код.

Проблема: рабочее время сотрудника

У нас есть объект для сотрудника, который рассчитывает отработанное время в час, также у каждого сотрудника должен быть контракт, который рассчитывает отработанное время в день. каждый сотрудник работает 8 часов в день, поэтому, если сотрудник отработал 2 дня, он вычислил => 2 * 8 = 16 часов, отработанных за 2 дня.

interface IEmployee {
     work_in_hour(): number;
}
class Employee implements IEmployee {
      public hour_worked: number;
      constructor(hour_worked: number) {
          this.hour_worked = hour_worked;
      }
      work_in_hour(): number {
         return this.hour_worked;
      }
}
interface IContractor {
     work_in_day(): number;
}

class Contractor implements IContractor {
     public day_worked: number;
     constructor(day_worked: number) {
        this.day_worked = day_worked;
     }
     work_in_day(): number {
       return this.day_worked;
     }
}
class ContractorAdapter implements IEmployee {
      private contractor: IContractor;
      constructor(contractor: IContractor) {
          this.contractor = contractor;
      }
      work_in_hour() {
         return this.contractor.work_in_day() * 8;
      }
}
class EmployeeAdapter implements IContractor {
      private employee: IEmployee;
      constructor(employee: IEmployee) {
         this.employee = employee;
      }
      work_in_day() {
       return this.employee.work_in_hour() / 8;
     }
}
let employee_1 = new Employee(200);
let contractor_1 = new Contractor(19);
// 19 day = 152 hours
const to_hour = new ContractorAdapter(contractor_1).work_in_hour();
// 200 hours = 25 days
const to_day = new EmployeeAdapter(employee_1).work_in_day();

Плюсы

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

Минусы

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