Рефакторинг блоков сравнения / операторов для СУХОГО повышения и снижения уровня 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);
}
}