---
title: "Замена кодирования типа подклассами"
type: "refactoring-technique"
slug: "replace-type-code-with-subclasses"
url: "http://localhost:3000/ru/replace-type-code-with-subclasses.md"
category: "Организация данных"
description: "Проблема: У вас есть закодированный тип, который непосредственно влияет на поведение программы (основываясь на значениях этого поля, в условных операторах выполняется различный код). Решение: Для каждого значения закодированного типа, создайте подклассы. А затем, вынесите соответствующие поведения из исходного класса в эти подклассы. Управляющий код замените полиморфизмом."
---
# Замена кодирования типа подклассами

> Проблема: У вас есть закодированный тип, который непосредственно влияет на поведение программы (основываясь на значениях этого поля, в условных операторах выполняется различный код). Решение: Для каждого значения закодированного типа, создайте подклассы. А затем, вынесите соответствующие поведения из исходного класса в эти подклассы. Управляющий код замените полиморфизмом.

## Problem

У вас есть закодированный тип, который непосредственно влияет на поведение программы (основываясь на значениях этого поля, в условных операторах выполняется различный код).

## Solution

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

## Why Refactor

Данный рефакторинг является более сложным случаем [замены кодирования типа классом](/ru/replace-type-code-with-class).

Как и в первом рефакторинге, у вас есть какой-то набор простых значений, которые составляют все доступные значения для какого-то поля. Хотя эти значения зачастую заданы как константы и имеют понятные имена, их использование чревато появлением ошибок проверки типов, так как, в сущности, они всё-равно являются значениями примитивных типов. Например, у вас есть метод, принимающий в параметрах одно из таких значений. В определённый момент, вместо константы `USER_TYPE_ADMIN` со значением `"ADMIN"`, в метод придёт та же строка, но в нижнем регистре `"admin"`, что приведёт к выполнению чего-то другого, нежели планируемое автором поведение.

В данном рефакторинге мы имеем дело с управляющим кодом, таким как условные операторы `if`, `switch` или `?:`. Другими словами, внутри условий этих операторов используются поля с закодированным значением (напр. `$user->type === self::USER_TYPE_ADMIN`). Если бы мы применяли здесь [замену кодирования типа классом](/ru/replace-type-code-with-class), то все эти управляющие конструкции следовало бы тоже перенести в класс, отвечающих за тип данных. Это, в конечном итоге, сделало бы класс типа очень похожим на исходный класс, и содержащим те же проблемы.

## Benefits

* Удаление управляющего кода. Вместо большого `switch` в исходном классе, вы переносите код в соответствующие подклассы. Это улучшает _разделение ответственности между классами_ и упрощает читабельность программы в целом.
* Если вам потребуется добавить новое значение закодированного типа, все что нужно будет сделать — это добавить новый подкласс, не трогая существующий код (_принцип открытости/закрытости_).
* Заменив кодирование типа классами, мы обеспечим возможность контроля и проверки типов значений (type hinting), передаваемых в методы и поля на уровне языка программирования. Чего не сделаешь при помощи простых численных или строковых значений, содержащихся в закодированном типе.

## How to Refactor

1. Используйте [самоинкапсуляцию поля](/ru/self-encapsulate-field) для создания геттера для поля, которое содержит кодирование типа.
2. Сделайте конструктор суперкласса приватным. Создайте статический фабричный метод с теми же параметрами, что и конструктор суперкласса. Он обязательно должен содержать параметр, который будет принимать стартовые значения закодированного типа. В зависимости от этого параметра, фабричный метод будет создавать объекты различных подклассов. Для этого в его коде придётся создать большой условный оператор, но, по крайней мере, он будет единственным, который действительно необходим — обо всем остальном, в дальнейшем, смогут позаботиться подклассы и полиморфизм.
3. Для каждого значения кодированного типа, создайте свой подкласс. В нем переопределите геттер закодированного поля так, чтобы он возвращал соответствующее значение закодированного типа.
4. Удалите поле с закодированным типом из суперкласса, его геттер сделайте абстрактным.
5. Теперь, когда у вас появились подклассы, можете начинать перемещать поля и методы из суперкласса в соответствующие подклассы (при помощи [спуска поля](/ru/push-down-field) и [спуска метода](/ru/push-down-method)).
6. Когда все что можно перемещено, используйте [замену условных операторов полиморфизмом](/ru/replace-conditional-with-polymorphism), чтобы окончательно избавиться от условных операторов, использующий закодированный тип.
## Relations

**Similar techniques**

- [Замена кодирования типа классом](/ru/replace-type-code-with-class.md)
- [Замена кодирования типа состоянием/стратегией](/ru/replace-type-code-with-state-strategy.md)

**Opposite refactorings**

- [Замена подкласса полями](/ru/replace-subclass-with-fields.md)

**Eliminates smells**

- [Одержимость элементарными типами](/ru/smells/primitive-obsession.md)

