Рефакторинг блоков сравнения / операторов для СУХОГО повышения и снижения уровня CRAP

Я решил сделать небольшой проект вокруг множества классов, которые возвращают генераторы (php 5.5).

Основной мотивацией для небольшого проекта было расширение моего путешествия по TDD, возиться с генераторами и иметь пакет, который я мог бы скинуть на упаковщика для дальнейшего использования.

Текущее состояние всего "проекта" можно найти на Github

Все тесты зеленые, методы делают то, что я хочу. Теперь я хочу провести рефакторинг, так как у меня много повторов.

    /**
    *   Returns a Generator with a even range.
    *
    *   getEven(10); // 10,12,14,16,18,20,22 ...
    *   getEven(null, 10); // 10,8,6,4,2,0,-2,-4 ...
    *   getEven(10, null, 2); // 10,6,2, -2 ...
    *   getEven(10,20); // 10,12,14,16,18,20
    *   getEven(20,10); // 20,18,16,14,12,10
    *   getEven(10,20,2); // 10,14,18
    *
    *   @param int|null $start
    *   @param int|null $end
    *   @param int $step
    *   @throws InvalidArgumentException|LogicException
    *   @return Generator
    */
    public function getEven( $start = null, $end = null, $step = 1 )
    {
        // Throws LogicException
        $this->throwExceptionIfAllNulls( [$start, $end] );
        $this->throwExceptionIfInvalidStep($step);

        // Throws InvalidArgumentException
        $this->throwExceptionIfNotNullOrInt( [$start, $end] );

        // infinite increase range
        if(is_int($start) && is_null($end))
        {
            // throw LogicException
            $this->throwExceptionIfOdd($start);

            $Generator = function() use ($start, $step)
            {
                for($i = $start; true; $i += $step * 2)
                {
                    yield $i;
                }
            };
        }
        // infinite decrease range
        elseif(is_int($end) && is_null($start))
        {
            // throws LogicException
            $this->throwExceptionIfUneven($end);

            $Generator =  function() use ($end, $step)
            {
                for($i = $end; true; $i -= $step * 2)
                {
                    yield $i;
                }
            };
        }
        // predetermined range
        else 
        {
            // throws LogicException
            $this->throwExceptionIfUneven($start);
            $this->throwExceptionIfUneven($end);

            // decrease
            if($start >= $end)
            {
                $Generator = function() use ($start, $end, $step)
                {
                    for($i = $start; $i >= $end; $i -= $step * 2)
                    {
                        yield $i;
                    }
                };
            }
            // increase
            else
            {
                $Generator = function() use ($start, $end, $step)
                {
                    for($i = $start; $i <= $end; $i += $step * 2)
                    {
                        yield $i;
                    }
                };
            }
        }

        return $Generator();
    }

Класс также имеет метод с именем getOdd (и да, он выглядит очень похоже на это;))

Основное дублирование - закрытие $Generator = function() ... и разница в основном операторы, такие как + - * / и аргументы в цикле for. Это в основном то же самое в остальной части класса.

Я прочитал операторы динамического сравнения в PHP и пришел к выводу, что не существует нативного метода, подобного compare(...)

Должен ли я сделать закрытый / защищенный метод для сравнения. Если так, я должен сделать новый класс / функцию для этого? Я не думаю, что он принадлежит к текущему классу.

Я скучаю по чему-то еще, я не уверен, как правильно высушить это?

Btw. Я знаю, getEven, getOdd довольно глупо, когда я получил getRange с функцией шага, но это более общий вопрос рефакторинга / паттерна.

Обновление@github getEven и getOdd теперь удалены...

1 ответ

Решение

Приведенный ниже код не был протестирован или проверен для работы, но я верю в это, и, по крайней мере, он показывает один из возможных способов удаления нескольких функций генератора.

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

function createGenerator($index, $limit, $step) {
    return function() use($index, $limit, $step) {
        $incrementing = $step > 0;
        for ($i = $index; true; $i += 2 * $step) {
            if (($incrementing && $i <= $limit) || (!$incrementing && $i >= $limit)) {
                yield $i;
            }else {
                break;
            }
        }
    };
}

Чтобы использовать это, вам нужно немного поработать с входными аргументами, и это помогает (по крайней мере, делает это красиво) для определения некоторых констант. PHP уже получил PHP_INT_MAX константа, содержащая максимально возможное значение для целого числа, однако она не получила PHP_INT_MIN, Поэтому я бы определил это как свою собственную константу.

define('PHP_INT_MIN', ~PHP_INT_MAX);

Теперь давайте посмотрим на четыре случая в вашей функции.

1) бесконечный прирост дальности

Infinte является довольно смелым утверждением здесь, если мы изменим его на "максимально возможное значение, учитывая ограничения int", мы получим конечный диапазон от $index в PHP_INT_MAXследовательно, путем установки $limit = PHP_INT_MAX; вышеупомянутая функция генератора останется прежней.

//$limit = PHP_INT_MAX;
createGenerator($index, PHP_INT_MAX, $step);

2) Бесконечный диапазон уменьшения

Здесь можно снова использовать тот же аргумент, что и выше, но с отрицательным $step и обмен $index а также $limit;

//$index = $limit;
//$limit = PHP_INT_MIN;
//$step *= -1;
createGenerator($limit, PHP_INT_MIN, -1 * $step);

3) Предопределенный убывающий диапазон

Обмен и отрицание еще раз.

//$temp = $index;
//$index = $limit;
//$limit = $temp;
//$step *= -1;
createGenerator($limit, $index, -1 * $step);

4) Предопределенный диапазон увеличения

Ну, это просто случай по умолчанию, где приведены все аргументы. И ничего не нужно менять.

createGenerator($index, $limit, $step);

Пересмотренный код

public function getEven($index = null, $limit = null, $step = 1) {
    // Throws LogicException
    $this->throwExceptionIfAllNulls([$index, $limit]);
    $this->throwExceptionIfInvalidStep($step);

    // Throws InvalidArgumentException
    $this->throwExceptionIfNotNullOrInt([$index, $limit]);

    //Generator function
    function createGenerator($index, $limit, $step) {
        return function() use($index, $limit, $step) {
            $incrementing = $step > 0;
            for ($i = $index; true; $i += 2 * $step) {
                if (($incrementing && $i <= $limit) || (!$incrementing && $i >= $limit)) {
                    yield $i;
                }else {
                    break;
                }
            }
        };
    }
    // infinite increase range
    if (is_int($index) && is_null($limit)) {
        // throw LogicException
        $this->throwExceptionIfodd($index);
        return createGenerator($index, PHP_INT_MAX, $step);
    }
    // infinite decrease range
    elseif (is_int($limit) && is_null($index)) {
        // throws LogicException
        $this->throwExceptionIfodd($limit);
        return createGenerator($limit, PHP_INT_MIN, -1*$step);
    }
    // predetermined range
    else {
        // throws LogicException
        $this->throwExceptionIfodd($index);
        $this->throwExceptionIfodd($limit);

        // decrease
        if ($index >= $limit) {
            return createGenerator($limit, $index, -1 * $step);
        }
        return createGenerator($index, $limit, $step);
    }
}
Другие вопросы по тегам