Я просто написал ужасную функцию PHP, мне нужна помощь (elseif chain - switch?)
Я создаю сайт, который определяет значение массива в зависимости от времени. Я написал этот ужасный (функциональный) script, и мне интересно, могу ли я сделать его более кратким. Я начал с оператора case/switch, но не смог получить несколько условных выражений, работающих с ним. Здесь грязное дело:
if ($now < november 18th) {
$array_to_use = $home;
}
elseif (november 18th < $now && $now < november 21st ) {
$array_to_use = $driving;
}
elseif (november 21st < $now && $now < november 22nd) {
$array_to_use = $flying;
}
...
...
...
elseif (february 1st < $now) {
$array_to_use = $arrived;
}
else {
$array_to_use = $default;
}
Расписание на самом деле сложнее и содержит 13 elseif
операторов. Может кто-то, пожалуйста, подтвердите, что у меня только что был блок кодера и что есть лучший способ сделать это?
EDIT: Я изменил временные метки Unix на грубые реальные времена, чтобы было легче понять, что я делаю (надеюсь)
РЕДАКТИРОВАТЬ 2: Пожалуйста, простите, что в настоящее время неработающие часы Javascript, но это сайт, над которым я работаю:
Таблица времени.
Каждый массив основан на моем местоположении, и есть 15 "они в настоящее время" основаны на времени. Это небольшая проблемная область с известными временами начала и окончания, поэтому гибкость не является ключевой, просто получая ее все написанное. Вы можете видеть, как время непрерывное, и только один массив строк должен быть выбран за раз.
Ответы
Ответ 1
Сначала, пожалуйста, пожалуйста, выньте свои жестко закодированные цифры и поместите их в константы.
$FLIGHT_START_TIME = 1258956001;
$FLIGHT_END_TIME = 1260511201;
Во-вторых, я бы сделал мини-функции для каждого из условных выражений:
т.е.
function isFlying($time)
{
return ( $FLIGHT_START_TIME < $time && $time < $FLIGHT_END_TIME );
}
В-третьих, возьмите весь набор условных выражений и поместите его в функцию, чтобы получить текущее состояние и замените в своих вызовах функции:
function getStateArrayForTime($time)
{
if (isDriving($time)
{
return $driving;
}
if ( isFlying($time) )
{
return $flying;
}
...etc
}
Наконец, замените весь встроенный раздел кода на один вызов функции:
$currentState = getStateArrayForTime($now);
Поскольку другие плакаты также прокомментировали, на этом этапе вы можете использовать функцию, управляемую таблицей данных, чтобы вернуть состояние, если вы знаете, что только начальное и конечное время будут параметрами состояния:
поэтому замените реализацию getStateArrayForTime на:
function getStateArrayForTime ($time)
{
//
$states = array (
array("startTime" => 1258956001, "endTime" => 1260511201, "state" => $flying),
array("startTime" => 1260511201, "endTime" => 1260517000, "state" => $driving),
..etc...
);
foreach($states as $checkStateArray)
{
if($checkStateArray['startTime'] < $time && $time < $checkStateArray['endTime'])
{
return $checkStateArray['state'];
}
}
return null;
}
Наконец, некоторые люди могут спросить: "Почему дела в этом порядке?" Я не могу претендовать на кредит вообще, кроме приложения, но у Мартина Фаулера есть замечательная книга под названием "Рефакторинг", которая объясняет, почему вы очищаете код за один шаг за раз и проверяете на каждом этапе пути, а затем, наконец, замените оптовые функции, которые не имеют смысла, все время проверяя, что они функционально эквивалентны.
Ответ 2
Это может быть излишним, но я бы сделал что-то подобное, чтобы я мог использовать все диапазоны времени в одном ясном месте:
@timeWindows = ({ start -> 0, end -> 1258783201, array -> $home },
... ,
{start -> 1260511201, end -> MAXVAL, array -> $arrived});
а затем цикл, подобный
$array_to_use = $default;
foreach (my $window in @timeWindows) {
if (($now > $window->start) && ($now < $window->end)) {
$array_to_use = $window->array;
last;
}
}
Извините, что в Perl я не знаю PHP, но я думаю, что это похоже.
Ответ 3
Вы можете поместить время и массив для использования в массиве и их цикл для выбора.
$Selctions = array(
1258783201 => $Home,
1258956001 => $Driving,
1260511201 => $Flying,
...
1260511201 => $Arriving
);
// MUST SORT so that the checking will not skip
ksort($Selction);
$TimeToUse = -1;
$Now = ...;
foreach ($Selctions as $Time => $Array) {
if ($Now < $Time) {
$TimeToUse = $Time;
break;
}
}
$ArrayToUse = ($TimeToUse != -1) ? $Selctions[$TimeToUse] : $Default;
Этот метод может использоваться только тогда, когда время не имеет пробела (один диапазон справа от другого).
Надеюсь, что это поможет.
Ответ 4
Вы можете использовать оператор switch, делая что-то вроде этого:
switch (true)
{
case $now < 1258783201:
// your stuff
break;
case $now < 1258783201
// more of your stuff
break;
//...
}
Это по крайней мере немного чище...
Ответ 5
Что-то вроде этого:
$array_to_use = null;
$dispatch = array(1258783201, $home, 1258956001, $driving, ..., $arrived);
for ($i=0; i<count($dispatch); $i+=2) {
if ($now<$dispatch[$i]) {
$array_to_use = $dispatch[$i+1];
break;
}
}
if ($array_to_use==null) $array_to_use = $dispatch[count($dispatch)-1];
Вам также нужно подумать, нужно ли вам "<". или "< =".
Ответ 6
Возможно, вам захочется изучить Command Pattern; это также может помочь в этом обстоятельстве.