ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 06.11.2023
Просмотров: 885
Скачиваний: 6
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
387 7. Введение внешнего метода (Introduce Foreign Method). Ситуация достаточно распространенная. Есть прекрасный класс с отличными сер- висами. Затем оказывается, что нужен еще один сервис, но класс его не предоставляет. Если есть возможность модифицировать исходный код, вводится новый метод. Если такой возможности нет, приходится обход- ными путями программировать отсутствующий метод в клиенте. Если клиентский класс использует этот метод единственный раз, то дополни- тельное кодирование не представляет больших проблем и, возможно, даже не требовалось для исходного класса.
Однако если метод используется многократно, приходится повто- рять кодирование снова и снова. Повторение кода - корень всех зол в программах, поэтому повторяющийся код необходимо выделить в от- дельный метод. При проведении этого рефакторинга можно явно извес- тить о том, что этот метод в действительности должен находиться в ис- ходном классе, сделав его внешним методом. Если обнаруживается, что для класса сервера создается много внешних методов или что многим классам требуется один и тот же внешний метод, то следует применить другой рефакторинг – Введение локального расширения (Introduce Local
Extension).
8. Введение локального расширения (Introduce Local Extension). К со- жалению, создатели классов не могут предоставить все необходимые методы. Если есть возможность модифицировать исходный код, то час- то лучше всего добавить в него новые методы. Однако иногда исходный код нельзя модифицировать. Если нужны лишь один-два новых метода, можно применить введение внешнего метода. Однако если их больше, они выходят из-под контроля, поэтому необходимо объединить их вы- брав для этого подходящее место. Очевидным способом является стан- дартная объектно-ориентированная технология создания подклассов и оболочек. Локальное расширение представляет собой отдельный класс, но выделенный в подтип класса, расширением которого он является.
Это означает, что он умеет делать то же самое, что и исходный класс, но при этом имеет дополнительные функции. Вместо работы с исходным классом следует создать экземпляр локального расширения и пользо- ваться им. Применяя локальное расширение, вы поддерживаете прин- цип упаковки методов и данных в виде правильно сформированных блоков. Если же продолжить размещение в других классах кода, кото- рый должен располагаться в расширении, это приведет к усложнению других классов и затруднению повторного использования этих методов.
8.5.5. Методы рефакторинга, облегчающие работу с данными
В программистской литературе давно ведутся обсуждения вопроса о том, должен ли объект осуществлять доступ к собственным данным не- посредственно или через методы доступа. Иногда методы доступа нуж- ны, и тогда можно получить их с помощью Самоинкапсуляции поля (Self
Encapsulate Field). Одно из удобств объектных языков состоит в том, что
388 они разрешают определять новые типы, которые позволяют делать больше, чем простые типы данных обычных языков.
Начинающие программисты сначала используют простое значение данных и лишь позже начинают понимать, что объект был бы удобнее.
Замена значения данных объектом (Replace Data Value with Object) по- зволяет превратить бессловесные данные в раздельно говорящие объек- ты. Поняв, что эти объекты представляют собой экземпляры, которые понадобятся во многих местах программы, их можно превратить в объ- екты ссылки посредством Замены значения ссылкой (Change Value to
Reference). Если видно, что объект выступает как структура данных, можно сделать эту структуру данных более понятной с помощью Заме-
ны массива объектом (Replace Array with Object). Во всех этих случаях объект представляет собой лишь первый шаг. Реальные преимущества достигаются при Перемещении метода (MoveMethod), позволяющем добавить поведение в новые объекты.
Магические числа (числа с особым значением
4
) давно представляют собой проблему. М. Фаулер предлагает ими не пользоваться и приме- нять Замену магического числа символической константой (Replace
Magic Number with Symbolic Constant), чтобы избавляться от них, как только становится понятно, что они делают.
Ссылки между объектами могут быть односторонними или двусто- ронними. Односторонние ссылки проще, но иногда для поддержки но- вой функции нужна Замена однонаправленной связи двунаправленной
(Change Unidirectional Association to Bidirectional). Замена двунаправ-
ленной связи однонаправленной (Change Bidirectional Associa tion to
Unidirectional) устраняет излишнюю сложность, когда обнаруживается, что двунаправленная ссылка больше не нужна.
Часто классы GUI выполняют бизнес-логику, которая не должна на них возлагаться. Для перемещения поведения в классы надлежащей предметной области необходимо хранить данные в классе предметной области и поддерживать GUI с помощью Дублирования видимых данных
(Duplicate Observed Data). Обычно дублирование данных не приветству- ется, но этот случай составляет исключение.
Одним из главных принципов объектно-ориентированного програм- мирования является инкапсуляция. Если какие-либо открытые данные повсюду демонстрируют себя, то с помощью Инкапсуляции поля
(Encapsulate Field) можно обеспечить им достойное прикрытие. Если данные представляют собой коллекцию, следует воспользоваться Ин-
капсуляцией коллекции (Encapsulate Collection), поскольку у нее особый протокол. Если обнажена целая запись, выберите Замену записи классом
данных (Replace Record with Data Class).
Особого обращения требует такой вид данных, как код типа – специ- альное значение, обозначающее какую-либо особенность типа экземп-
4
Магическое число
(программирование). http://ru.wikipedia.org/wiki/%D0%9C%D0%....
389 ляра. Часто эти данные представляются перечислением, иногда реали- зуемым как статические неизменяемые целые числа. Если код предна- значен для информации и не меняет поведения класса, можно прибег- нуть к Замене кода типа классом (Replace Type Code with Class), что ведет к лучшей проверке типа и создает основу для перемещения пове- дения в дальнейшем. Если код типа оказывает влияние на поведение класса, можно попытаться применить Замену кода типа подклассами
(Replace Type Code with Subclasses). Если это не удается, возможно, по- дойдет более сложная (но более гибкая) Замена кода типа состояни-
ем/стратегией (Replace Type Code with State / Strategy).
Что касается обращения к полям, то есть мнения двух школ. Первая учит, что в том классе, где определена переменная, обращаться к ней следует свободно (непосредственный доступ к переменной). Другая школа утверждает, что даже внутри этого класса следует всегда приме- нять методы доступа (косвенный доступ к переменной). В сущности, преимущество косвенного доступа к переменной состоит в том, что он позволяет переопределить в подклассе метод получения информации и обеспечивает большую гибкость в управлении данными, например от- ложенную инициализацию, при которой переменная инициализируется лишь при необходимости ее использования. Преимущество прямого доступа к переменной заключается в легкости чтения кода.
Важнейший повод применить Самоинкапсуляцию поля (Self
Encapsulate Field) возникает, когда при доступе к полю родительского класса необходимо заменить обращение к переменной вычислением значения в подклассе. Первым шагом для этого является самоинкапсу- ляция поля. После этого можно заместить методы получения и установ- ки значения теми, которые необходимы.
Рассмотрим основные методы облегчающие работу с данными.
1. Замена значения данных объектом (Replace Data Value with
Object). Часто на ранних стадиях разработки принимается решение о представлении простых фактов в виде простых элементов данных. В процессе разработки обнаруживается, что эти простые элементы в дей- ствительности сложнее. Например, телефонный номер может быть вна- чале представлен в виде строки, но позднее выясняется, что у него должно быть особое поведение в виде форматирования, извлечения ко- да зоны и т. п. Если таких элементов один-два, можно поместить соот- ветствующие методы в объект, который ими владеет. Однако у кода может появиться зависть к функциям (см. выше). Если такой факт поя- вился, следует преобразовать данные в объект.
2. Замена значения ссылкой (Change Value to Reference). Во многих системах можно провести полезную классификацию объектов как объ- ектов ссылки и объектов значения. Объекты ссылки (reference objects) – это, например, такие объекты, как клиент или счет. Каждый объект обо- значает один объект реального мира, и равными считаются объекты, которые совпадают. Объекты значений (value objects) – это, например, дата или денежная сумма. Они полностью определены своими значе-
390 ниями данных. Равенство объектов должно проверяться, для чего пере- гружается метод equals (а также метод hashCode).
Выбор между ссылкой и значением не всегда очевиден. Иногда вна- чале есть простое значение с небольшим объемом неизменяемых дан- ных. Затем возникает необходимость добавить изменяемые данные и обеспечить передачу этих изменений при всех обращениях к объекту.
Тогда необходимо превратить его в объект ссылки.
3. Замена ссылки значением (Change Reference to Value). Как и в пре- дыдущем случае, выбор между объектом ссылки и объектом значения не всегда очевиден. Побудить к переходу от ссылки к значению могут возникшие при работе с объектом ссылки неудобства. Объектами ссыл- ки необходимо некоторым образом управлять. Всегда приходится за- прашивать у контроллера нужный объект. Ссылки в памяти тоже могут оказаться неудобными в работе. Объекты значений особенно полезны в распределенных и параллельных системах. Важное свойство объектов значений заключается в том, что они должны быть неизменяемыми. При каждом запросе, возвращающем значение одного из них, должен полу- чаться одинаковый результат.
Если это так, то не возникает проблем при наличии многих объек- тов, представляющих одну и ту же вещь. Если значение изменяемое, необходимо обеспечить, чтобы при изменении любого из объектов об- новлялись все остальные объекты, которые представляют ту же самую сущность. Это настолько обременительно, что проще всего для этого создать объект ссылки.
4. Замена массива объектом (Replace Array with Object). Массивы следует применять лишь для хранения коллекций аналогичных объек- тов в определенном порядке. Впрочем, иногда можно столкнуться с хранением в массивах ряда различных объектов. Соглашения типа
«первый элемент массива содержит имя лица» трудно запоминаются.
Используя объекты, можно передавать такую информацию с помощью названий полей или методов, чтобы не запоминать ее и не полагаться на точность комментариев. Можно также инкапсулировать такую инфор- мацию и добавить к ней поведение с помощью «Перемещения метода»
(Move Method).
5. Дублирование видимых данных (Duplicate Observed Data). В хоро- шо организованной многоуровневой системе код, обрабатывающий ин- терфейс пользователя, отделен от кода, обрабатывающего бизнес- логику. Поведение можно легко разделить на части, чего часто нельзя сделать с данными. Данные должны встраиваться в графический эле- мент GUI и иметь тот же смысл, что и данные в модели предметной об- ласти. Однако данные нельзя просто переместить, их надо скопировать и обеспечить механизм синхронизации. Необходимо скопировать дан- ные в объект предметной области приложения и создать объект- наблюдатель, который будет обеспечивать синхронность данных.
391 8.5.6. Рефакторинг третьего уровня
Такие рефакторинги М. Фаулер называет крупными. При их выпол- нении, как говорят авторы 12-й главы монографии [32], сложно точно сказать, что нужно сделать, здесь многое зависит от конкретной ситуа- ции. Например, когда вводится в метод новый параметр, то механизм ясен, потому что ясна область действия. Когда же приходится разби- раться с запутанным наследованием, то каждый случай особенный.
Кроме того, надо отметить, что такие рефакторинги отнимают много времени.
Любой рефакторинг, рассмотренный выше, можно выполнить за не- сколько минут, в крайнем случае, за час. Над крупными рефакторинга- ми придется работать в течение месяцев (или даже лет), причем часто в действующих системах. Из-за того что они отнимают так много време- ни, крупные рефакторинги не приносят мгновенного вознаграждения.
Крупные рефакторинги требуют определенного согласия во всей ко- манде программистов, чего не надо для маленьких рефакторингов.
Крупные рефакторинги определяют направление для многих и многих модификаций. Вся команда должна осознать, что «в игре» находится один из крупных рефакторингов, и действовать соответственно.
Почему важны крупные рефакторинги, если в них нет многих из тех качеств, которые составляют ценность маленьких (предсказуемости, заметного прогресса, немедленного вознаграждения)? Потому что без них велик риск потратить время и силы на обучение проведению рефак- торинга, а затем и на практический рефакторинг, и не получить никаких выгод. Крупным рефакторингом занимаются потому, что разработчик рассчитывает сделать со своими программами после проведения рефак- торинга то, чего без него просто нельзя было сделать. Накопление ма- лопонятных проектных решений в итоге просто “удушает” программ- ную систему. Благодаря рефакторингу разработчик гарантирует, что полное понимание того, как должна быть спроектирована программа, всегда находит в ней отражение.
В монографии [32] идет речь о четырех рефакторингах третьего уровня. Разделение наследования (Tease Apart Inheritance) имеет дело с запутанной иерархией наследования, в которой различные варианты представляются объединенными вместе так, что это сбивает с толку.
Преобразование процедурного проекта в объекты (Convert Procedural
Design to Object) содействует решению классической проблемы преоб- разования процедурного кода. Множество программистов пользуется объектно-ориентированными языками, не имея действительного пред- ставления об объектах, поэтому данный вид рефакторинга приходится часто выполнять.
Если в системе есть код, написанный с классическим двухзвенным подходом к интерфейсам пользователя и базам данных, то понадобится
Отделение предметной области от представления (Separate Domain from Presentation ), чтобы разделить бизнес-логику и код интерфейса пользователя. Опытные объектно-ориентированные разработчики поня-