1 Замена алгоритма Замена алгоритма на этапе рефакторинга встречается довольно часто. Порой на этапе создания продукта алгоритмы программируются «в лоб», чтобы сократить время разработки. В результате получается громоздкий и неудобочитаемый код. На этапе рефакторинга может возникнуть ситуация, при которой необходимо удалить алгоритм целиком и использовать вместо него более эффективный вариант. Также часто встречающейся является ситуация, при которой алгоритм реализован в новой библиотеке, которая используется в проекте с определенной версии; тогда код, реализующий алгоритм, может быть удален, и вместо него будет использована соответствующая функция из библиотеки. String foundPerson (String[] people){ for (int i=0; i<people.lenght;i++;){ if (people[i].equals (“Don”)){ return “Don”; } if (people[i].equals (“John”)){ return “John”; } if (people[i].equals (“Kent”)){ return “Kent”; } } return “”; } String foundPerson (String[] people){ List candidates = Arrays.asList(new String[] {“Don”,”John”,”Kent”}); for (int i=0; i<people.lenght;i++;) if (candidates.contains(people[i])) return people[i]; return “”; } Процедура Разработайте альтернативную версию алгоритма Замените исходный алгоритм на новый, протестируйте результат Если тестирование выдало ошибки, используйте код исходного алгоритма для сравнения при тестировании и отладке. Перемещение ответственности между объектами Это целая группа шаблонов рефакторинга, к которой относятся такие шаблоны как перемещение метода и атрибута, извлечение класса и ликвидация класса, сокрытие делегата, устранение посредника, и ряд других. Перемещение метода Шаблон применяется тогда, когда метод использует либо используется больше не в классе, в котором он определен, а в некотором другом классе. Идея состоит в определении аналогичного метода в другом классе. Исходный метод заменяется делегатом либо удаляется. Процедура Исследуйте все свойства (методы и атрибуты), используемые исходным методом в своем классе. Рассмотрите целесообразность переместить их вместе с исходным методом в другой класс. Если свойство используется лишь перемещаемым методом, оно может быть безболезенно перемещено вместе с ним. Если же оно используется и другими методами, возможно, их тоже следует переместить. Порой 2 бывает удобнее переместить целиком группу методов, нежели каждый из них по отдельности. Проверьте, нет ли в подклассах и суперклассах исходного класса других объявлений данного метода. Если они есть, вы не можете перемещать метод. Объявите метод в целевом классе. При этом возможно заменить имя метода с тем, чтобы новое имя было осмысленным с точки зрения нового класса Скопируйте код метода в новый класс. Добейтесь того, чтобы он заработал в новом классе. Например, если метод использует исходный класс, определите природу этого использования, например, исходный класс может передаваться в метод в качестве параметра. Также необходимо определить, какой из классов должен обрабатывать исключения, с точки зрения логики обоих классов. Скомпилируйте целевой класс Определите, каким образом корректно сослаться на целевой класс из исходного. Возможно уже существует атрибут или метод, который позволяет получить экземпляр целевого класса; если нет, то возможно, его легко можно создать. Включите исходный метод в метод-делегат Откомпилируйте и протестируйте код Определите, возможно ли удалить исходный метод, либо же его необходимо оставить в виде делегата. Последний вариант может быть проще, если у вас есть много вызово исходного метода. Если исходный метод удаляется, необходимо заменить все его вызовы на вызовы целевого метода После этого необходимо снова откомпилировать и протестировать код. Пример Исходный класс (фрагмент): class Account … double overdraftCharge() { if (_type.isPremium()) { double result=10; if (_daysOverdrawn >7) result += (_daysOverdrawn-7)*0.85; return result; } else return _daysOverdrawn*1.75; } double bankCharge() { double result=4.5; if (_daysOverdrawn > 0) result += overdraftChange(); return result; } private AccountType _type; private int _daysOverdrawn; Предположим, у нас могут появиться несколько новых типов счетов, для каждого из которых по своему рассчитывается сумма овердрафта. Т.е. возникает необходимость вынести метод для расчета овердрафта за пределы класса счета, в класс типа счета (Account Type). 3 Вначале смотрим, какие свойства использует метод overdraftCharge и решить вопрос, не стоит ли перенести целую группу методов. Очевидно, что _daysOverdrawn зависит от конкретного счета, и его необходимо оставить в этом классе. Далее, копируем метод в новый класс: class Account Type… double overdraftCharge(int daysOverdrawn) { if (isPremium()) { double result=10; if (daysOverdrawn >7) result += (daysOverdrawn-7)*0.85; return result; } else return daysOverdrawn*1.75; } Модификации метода коснулись удаления из него упоминания о переменной _type (сейчас этот метод вызывается в классе AccountType). Также нам нужно решить, как будет получена переменная daysOverdrawn – атрибут класса Account. В общем случае существуют следующие возможности: Перенести свойство в новый класс Использовать ссылку на исходный класс в целевом классе Передать исходный объект в целевой в качестве параметра Передать переменную в целевой объект в качестве параметра. В данном случае используется последний вариант. После того, как целевой метод модифицирован, скомпилирован и протестирован в целевом классе, исходный метод может быть заменен на делегат: class Account … double overdraftCharge () { return _type. overdraftCharge(_daysOverdrawn); } Можно остановиться здесь, а можно удалить метод из исходного класса. В этом случае необходимо найти все вызовы этого метода и заменить их на вызов метода из целевого класса. class Account … double bankCharge() { double result=4.5; if (_daysOverdrawn > 0) result += _type.overdraftChange(_daysOverdrawn); return result; } После того, как все вызовы заменены, можно удалить объявление метода из исходного класса. Если метод не был объявлен как private, необходимо для каждого такого метода проверить, не используется ли он другими классами. Если же переносимый метод вызывает другие методы исходного класса, то в метод целевого класса необходимо передать объект: 4 class Account Type… double overdraftCharge(Account account) { if (isPremium()) { double result=10; if (account.getDaysOverdrawn() >7) result += (account.getDaysOverdrawn()-7)*0.85; return result; } else return account.getDaysOverdrawn()*1.75; }