Логотип ConstructiCat
CodeBust.
Навигация по разделу ▾

Замена кодирования типа состоянием/стратегией.

Проблема

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

Решение

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

##Пример

До
class Employee {
  static final int ENGINEER = 0;
  static final int SALESMAN = 1;
  private int type;

  int payAmount() {
    switch (type) {
      case ENGINEER: return monthlySalary;
      case SALESMAN: return monthlySalary + commission;
      default: throw new IllegalStateException();
    }
  }
}
После
class Employee {
  private EmployeeType type;

  int payAmount() { return type.payAmount(this); }
  void setType(EmployeeType arg) { type = arg; }
}
abstract class EmployeeType {
  abstract int payAmount(Employee emp);
}
class Engineer extends EmployeeType {
  int payAmount(Employee emp) { return emp.monthlySalary; }
}
class Salesman extends EmployeeType {
  int payAmount(Employee emp) { return emp.monthlySalary + emp.commission; }
}

##Зачем делать рефакторинг

У нас есть кодирование типа, и оно влияет на поведение класса, поэтому мы не можем заменить кодирование типа классом.

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

##Преимущества

  • Данный рефакторинг предоставляет выход из ситуации, когда поле с закодированным типом меняет своё значение в процессе жизни объекта. В этом случае, замена значения производится путём замены объекта-состояния, на который ссылается исходный класс.

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

##Как делать рефакторинг

  1. Используйте самоинкапсуляцию поля для создания геттера для поля, которое содержит кодирование типа.

  2. Создайте новый класс и дайте ему понятное название, соответствующее предназначению закодированного типа. Этот класс будет играть роль состояния (или стратегии). Создайте в нем абстрактный геттер закодированного поля.

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

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

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

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

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