Добавить новое поведение в существующий метод легко, но часто это бывает не совсем верным решением. Когда изначально создается какой-то метод, то он обычно делает строго одно действие. Любое добавление нового кода выглядит немного подозрительно. Скорее всего, вы добавляете новый код, потому что хотите, чтобы он выполнялся в одно время с уже существующим кодом.

Раньше, такой способ называли “временным связыванием”, код становится довольно некрасивым, если делать это слишком часто. Если группировать процедуры вместе только потому что они должны выполняться в одно и тоже время, то связь между ними весьма слаба. Позже может выясниться, что один метод не используется без другого, и с этой точки зрения они должны логически быть одним методом. Но не торопитесь склеивать их в один метод, потому что вернуть назад может быть нелегко.

Когда нужно добавить какое-то поведение, вы можете это сделать не таким запутанным путем. Один подход - это использовать “Ветвлящийся метод”, но существует и другой подход, который иногда очень полезен. Называется он - “метод обертка”. Пример:

{% highlight java %} public class Employee { … public void pay() { Money amount = new Money(); for (Iterator it = timecards.iterator(); it.hasNext(); ) { Timecard card = (Timecard)it.next(); if (payPeriod.contains(date)) { amount.add(card.getHours() * payRate); } } payDispatcher.pay(this, date, amount); } } {% endhighlight %}

В этом методе складывается время работы за день для одного работника и затем отправляется в PayDispatcher. Давайте предположим, что требования изменились. Каждый раз, когда мы платим работнику, нам нужно обновлять файл с информацией о работнике для отправки в какую-либо систему отчетов. Самый простой вариант - это добавить код в метод pay(). В конце концов наш новый код должен выполняться вместе с методом pay(), правильно же? Давайте так и сделаем:

{% highlight java %} public class Employee { private void dispatchPayment() { Money amount = new Money(); for (Iterator it = timecards.iterator(); it.hasNext(); ) { Timecard card = (Timecard)it.next(); if (payPeriod.contains(date)) { amount.add(card.getHours() * payRate); } } payDispatcher.pay(this, date, amount); }

public void pay() {
 	logPayment();
	dispatchPayment();
}

private void logPayment() {
	 ...
}

} {% endhighlight %}

Мы переименовали метод pay() в dispatchPayment() и сделали его private. Затем создали новый метод pay(), который бужет вызывать наш dispatchPayment(). Новый метод pay()сначала логирует момент оплаты, а потом отправляет ее на обработку. Те клиенты, которые используют методpay()не узнают об изменении, да им и не нужно. Они как обычно будут вызывать метод 'pay()', и все будет по-старому. Это один из возможных видов "метода обертки". Мы создаем метод с названием нашего старого метода, и в теле вызываем старый метод. Делается это для того, чтобы добавить новое поведение к существующему методу. Каждый раз, когда клиент вызывает методpay()` происходит логирование - и такой вид “метода обертки” оказался, как раз к месту.

Существуют также второй вариант этого подхода, который мы можем использовать, когда хотим только добавить новый метод, метод, который никто кроме нас не вызывает. В предыдущем примере, если мы хотим, чтобы логирование было более явным, мы можем добавить метод makeLoggedPayment() в классе Employee:

{% highlight java %} public class Employee { public void makeLoggedPayment() { logPayment(); pay(); }

public void pay() {
...
}

private void logPayment() {
 ...
}

} {% endhighlight %}

Метод обертка - это хороший способ добавить новый функционал, сохраняя при этом своеобразное разделение со старым кодом. Существует только несколько минусов. Первый - это то, что новый функционал не смешан с логикой старого. Подождите, разве я сказал это плохо? Вообще-то, нет, в общем - зависит от ситуации. Второй минус - это то, что вы должны придумать новое имя для старого метода.

В нашем случае, я переименовал pay() в dispatchPayment(). Это имя конечно, притянуто за уши. Мне не очень нравится, как заканчивается код в нашем примере. Метод dispatchPayment() делает гораздо больше, чем просто “обрабатывает платеж”: он так же вычисляет сумму платежа. Если бы я хотел протестировать этот метод, то скорее всего я выделелил бы еще одну функцию - calculatePay() и тогда бы метод pay() выглядел как-то так:

{% highlight java %} public void pay() { logPayment(); Money amount = calculatePay(); dispatchPayment(amount); } {% endhighlight %}

Теперь, кажется, каждый метод делает только то - что должен.

Вот шаги, которые мы сделали в первой версии нашего метода - обертки: . Находим метод, который будем менять . Если изменение может быть сформулировано, как последовательность утверждений, то переименовываем этот метод, а рядом создаем новый метод с именем и сигнатурами предыдущего. . Из старого делаем вызов нового . Добавляем необходимый код в старый метод и покрываем тестами - если это возможно

Во второй версии метода обертки: . Находим метод, который будем менять . Если изменение может быть сформулированно, как последовательность утверждений, то создаем новый метод для этого . Создаем третий метод, который будет вызывать наш новый метод, а затем старый

Минусы

  • Иногда приводит к менее понятным именам методов. Например метод pay() мы переименовали в dispatchPay(), который менее интуитивно понятен, только потому что нам нужно какое-то новое имя метода. Конечно ,если у вас есть какие-то инструменты для рефакторинга, которые позволяют быстро переименовывать методы - это этот минус не такой болезненный.

Плюсы

  • Не увеличивает размеры изначальных методов
  • Добавляемый функционал не зависит от старого функционала