• Две ветви условного оператора выглядят похоже. Сделав их идентичными, вы сможете избавиться от условного оператора.
• Два метода выглядят похоже. Сделав их идентичными, вы сможете избавиться от одного из них.
• Два класса выглядят похоже. Сделав их идентичными, вы сможете избавиться от одного из них.
Иногда задачу согласования различий удобнее решать в обратном порядке. Иными словами, вы представляете себе самый тривиальный последний этап этой процедуры, а затем двигаетесь в обратном направлении. Например, если вы хотите избавиться от нескольких подклассов, наиболее тривиальный последний шаг можно будет выполнить в случае, если подкласс ничего не содержит. Тогда везде, где используется подкласс, можно будет использовать суперкласс, при этом поведение системы не изменится. Что надо сделать, чтобы очистить подкласс от методов и данных? Для начала метод можно сделать полностью идентичным одному из методов суперкласса. Постепенно переместив все методы и все данные в суперкласс, вы сможете заменить ссылки на подкласс ссылками на суперкласс. После этого подкласс можно уничтожить.
Изоляция изменений (Isolate Change)
Как можно модифицировать одну часть метода или объекта, состоящего из нескольких частей? Сначала изолируйте изменяемую часть.
Мне приходит в голову аналогия с хирургической операцией: фактически все тело оперируемого пациента покрыто специальной простыней за исключением места, на котором, собственно, осуществляется операция. Благодаря такому покрытию хирург имеет дело с фиксированным набором переменных. Перед выполнением операции врачи сколь угодно долго могут обсуждать, какое влияние на здоровье пациента оказывает тот или иной орган, однако во время операции внимание хирурга должно быть сфокусировано.
Вы можете обнаружить, что после того, как вы изолировали изменение, а затем внесли это изменение в код, результат получился настолько тривиальным, что вы можете отменить изоляцию. Например, если мы обнаружили, что внутри метода findRate() должно присутствовать всего одно действие – возврат значения поля, мы можем вместо обращений к методу findRate() напрямую обратиться к полю. В результате метод findRate() можно будет удалить. Однако подобные изменения не следует выполнять автоматически. Постарайтесь найти баланс между затратами, связанными с использованием дополнительного метода, и пользой, которую приносит дополнительная концепция, добавленная в код.
Для изоляции изменений можно использовать несколько разных способов. Наиболее часто используется шаблон «Выделение метода» (Extract Method), помимо него также используются «Выделение объекта» (Extract Object) и «Метод в объект» (Method Object).
Миграция данных (Migrate Data)
Как можно перейти от одного представления к другому? Временно дублируйте данные.
Как
Вначале рассмотрим версию «от внутреннего к внешнему». В рамках этого подхода вы изменяете вначале внутреннее представление, а затем внешний интерфейс.
1. Создайте переменную экземпляра в новом формате.
2. Инициализируйте переменную нового формата везде, где инициализируется переменная старого формата.
3. Используйте переменную нового формата везде, где используется переменная старого формата.
4. Удалите старый формат.
5. Измените внешний интерфейс так, чтобы использовать новый формат.
Однако в некоторых ситуациях удобнее сначала изменить API. В этом случае рефакторинг выполняется следующим образом.
1. Добавьте параметр в новом формате.
2. Обеспечьте преобразование параметра в новом формате во внутреннее представление, обладающее старым форматом.
3. Удалите параметр в старом формате.
4. Замените использование старого формата на использование нового формата.
5. Удалите старый формат.
Зачем
Проблема миграции данных возникает каждый раз, когда используется шаблон «От одного ко многим» (One to Many). Предположим, что мы хотим реализовать объект TestSuite, используя шаблон «От одного ко многим» (One to Many). Мы можем начать так:
def testSuite(self):
suite = TestSuite()
suite.add(WasRun("testMethod"))
suite.run(self.result)
assert("1 run, 0 failed" == self.result.summary())
Чтобы реализовать этот тест, начнем с одного элемента test:
class TestSuite:
def add(self, test):
self.test = test
def run(self, result):
self.test.run(result)
Теперь мы приступаем к дублированию данных. Вначале инициализируем коллекцию тестов:
TestSuite
def __init__(self):
self.tests = []
В каждом месте, где инициализируется поле test, добавляем новый тест в коллекцию:
Читать дальше
Конец ознакомительного отрывка
Купить книгу