Рефакторинг: группировка кода в отдельную функцию. Часть 2

Приведем примеры этого приема, описанного в предыдущей статье.

Пример без локальных переменных

Рассмотрим простейший пример - когда выделяемый код не использует какие-либо локальные переменные:

function showOwing() {
    $e = $this->_orders->elements();
    $outstanding = 0.0;
    // show banner
    echo "**************************";
    echo "***** Customer Owes ******";
    echo "**************************";
    // calculate outstanding
    while ($e->hasMoreElements()) {
        $each = $e->nextElement();
        $outstanding += $each->getAmount();
    }
    // show details
    echo "name: ".$this->_name;
    echo "amount: ".$outstanding;
}

Тут очень просто выделить код, который показывает баннер. Просто Ctrl+X, Ctrl+V и поставить вместо выделяемого кода вызов нового метода:

function showOwing() {
    $e = $this->_orders->elements();
    $outstanding = 0.0;
    showBanner();
    // calculate outstanding
    while ($e->hasMoreElements()) {
        $each = $e->nextElement();
        $outstanding += $each->getAmount();
    }
    // show details
    echo "name: ".$this->_name;
    echo "amount: ".$outstanding;
}
function showBanner() {
    // show banner
    echo "**************************";
    echo "***** Customer Owes ******";
    echo "**************************";
}

Пример с использованием локальных переменных

Теперь рассмотрим пример посложнее - когда выделяемый код использует локальную переменную, но не модифицирует ее. Возьмем для этого примера функцию showOwing, описанную выше. В ней можно выделить последний участок кода, где выводятся детали (show details):

function showOwing() {
    $e = $this->_orders->elements();
    $outstanding = 0.0;
    showBanner();
    // calculate outstanding
    while ($e->hasMoreElements()) {
        $each = $e->nextElement();
        $outstanding += $each->getAmount();
    }
    showDetails($outstanding);
}
function showDetails($outstanding) {
    echo "name: ".$this->_name;
    echo "amount: ".$outstanding;
}

Заметьте: я не использовал комментарии, которые были в коде до проведения этого приема рефакторинга. Причина в том, что итак понятно, что функция делает, по ее имени.

Я продемонстрировал пример для одной лоальной переменной, но не составляет никакого труда аналогично поступить для любого числа локальных переменных.

Пример с изменением локальной переменной

Рассмотрим пример еще сложнее - когда значение локальной переменной изменяется.

Но в этом примере рассмотрим два случая. Первый - когда локальная переменная не изменяется, а просто используется кодом, который идет за выделяемым. Второй - когда локальная переменная изменяется в выделяемом коде.

Первый случай:

function showOwing() {
    showBanner();
    $outstanding = getOutstanding();
    showDetails($outstanding);
}
function getOutstanding() {
    $e = $this->_orders->elements();
    $outstanding = 0.0;
    while ($e->hasMoreElements()) {
        $each = $e->nextElement();
        $outstanding += $each->getAmount();
    }
    return $outstanding;
}

Переменная $e используется только в выделяемом коде, поэтому мы ее инициализацию можем переместить в выделяемый метод. А переменная $outstanding используется в вызове функции showDetails, поэтому ее мы вынуждены возвращать из метода getOutstanding. Метод getOutstanding является так называемым функцией-запросом.

Для чистоты переименуем переменную $outstanding в $result в теле метода getOutstanding:

function getOutstanding() {
    $e = $this->_orders->elements();
    $result = 0.0;
    while ($e->hasMoreElements()) {
        $each = $e->nextElement();
        $result += $each->getAmount();
    }
    return $result;
}

Теперь рассмотрим второй случай - значение локальной переменной изменяется в выделяемом коде:

function showOwing($prevAmount) {
    $e = $this->_orders->elements();
    $outstanding = $prevAmount * 1.5;
    showBanner();
    // calculate outstanding
    while ($e->hasMoreElements()) {
        $each = $e->nextElement();
        $outstanding += $each->getAmount();
    }
    showDetails($outstanding);
}

В этом случае выделение кода в метод будет выглядеть так:

function showOwing($prevAmount) {
    $outstanding = $prevAmount * 1.5;
    showBanner();
    $outstanding = getOutstanding($outstanding);
    showDetails($outstanding);
}
function getOutstanding($initValue) {
    $result = $initValue;
    $e = $this->_orders->elements();
    while ($e->hasMoreElements()) {
        $each = $e->nextElement();
        $result += $each->getAmount();
    }
    return $result;
}

После тестирования кода, его можно еще немного очистить:

function showOwing($prevAmount) {
    showBanner();
    $outstanding = getOutstanding($prevAmount * 1.5);
    showDetails($outstanding);
}

Здесь Вы можете задать вопрос: а как быть, если несколько переменных изменяются в пределах выделяемого кода? Как их тогда вернуть?

Тут есть различные варианты. Можно использовать метод, который принимает аргументы по ссылке. Но это не красиво. Будет красивее, если создать несколько методов (и выделить отдельные участки кода, которые изменяют только одну переменную), каждый из которых будет возвращать только одну переменную.

Зачастую, временные переменные настолько часто встречаются в коде, что делают выделение кода в отдельный метод невозможным. В этом случае можно постараться уменьшить количество временных переменных с помощью приема выделения временной переменной в метод. Если же после этого все равно трудно выделить код в отдельный метод, придется прийти за помощью к такому приему, как «Замена метода объектом» (при применении этого приема нас не будет волновать количество временных переменных в коде), о котором мы поговорим в одной из ближайших статей по теме рефакторинга.





Читайте также:



Один ответ на “Рефакторинг: группировка кода в отдельную функцию. Часть 2”

  1. Игорь

    Довольно много полезных приемов на одном блоге. Я бы еще сделал справочник по приемам реффакторинга, что бы не перечитывать статьи, а глянул на прием и сразу видно для каких ситуаций и как его использовать.


© Copyright. . I-Novice. All Rights Reserved. Terms | Site Map