У меня потенциально есть набор операторов if, которые выглядят следующим образом:
if (a and b and c and d) { // do stuff } else (!a and b and c and d) { // do something else } else (!a and !b and c and D) { // do yet something else } ...
и т. д. для всех возможных перестановок.
я думал об этом:
switch ((a ? 'Y' : 'N') . (b ? 'Y' : 'N') . (c ? 'Y' : 'N') . (d ? 'Y' : 'N')) { case 'YNYN': // do stuff break; case 'NNNN': // etc. break; }
Есть ли способ лучше?
То, что я, скорее всего, сделаю (не зная специфики), построит серию классов для каждого состояния. Затем нажмите doStuff на этот класс:
class DoStuff { //The Client protected $strategies = array(); public function addStrategy(iDoStuffStrategy $strategy) { $this->strategies[] = $strategy; } public function doStuff ($a, $b, $c, $d) { foreach ($this->strategies as $strategy) { if ($strategy->test($a, $b, $c, $d)) { return $strategy->doStuff(); } } throw new RuntimeException('Unhandleable Situation!'); } } interface iDoStuffStrategy { // Return a bool if you can handle this situation public function test($a, $b, $c, $d); // Execute the implementation public function doStuff(); }
Затем каждый класс будет выглядеть так:
public function StrategyFoo implements iDoStuffStrategy { public function test($a, $b, $c, $d) { return $a && $b && $c && $d; } public function doStuff() { //DoStuff! } } public function StrategyBar implements iDoStuffStrategy { public function test($a, $b, $c, $d) { return !$a && $b && $c && $d; } public function doStuff() { //DoStuff! } }
Это в основном реализация шаблона стратегии . Выполнение этого способа позволяет выделить дерево решений.
Я думаю, вам следует рассмотреть возможность решения этой проблемы с деревьями решений, где различные узлы являются возможными конечными состояниями. то вы можете составить свою проблему в дереве и избавиться от всех этих ifs ….
Я применил аналогичный подход к вашему описанию случая один раз, когда мне нужно было агрегировать данные на основе набора условий, из которых было пять переключателей, которые могут быть включены или выключены.
Для работы с агрегированием информации о возможных ситуациях это работало нормально, но вне этого варианта использования, если есть действительно n ^ 2 разных действия, тогда я бы придерживался нескольких операторов if. Если на самом деле не так много перестановок, я бы группировал похожие результаты, чтобы уменьшить количество ifs.
Да, есть лучший способ.
О, тебе нужно больше деталей? Ну, у вас, похоже, есть таблица истинности с четырьмя переменными. Есть ли 16 возможных результатов (2 ^ 4), или вас интересует только подмножество? Если есть одна переменная, которая имеет примерно равное количество результатов в любом случае, возможно, используйте это как свои самые верхние операторы if и используйте вложенные ifs.
if (b) { // cases where b is true if (...) ... } else { // cases where b is false if (...) ... }
Вы также можете использовать оператор switch, но вместо строки из Y и N используйте битовые поля.
Я бы обработал ваши четыре логические строки как четыре бита, так как целое число от 0 до 15. Я бы создал массив с 16 элементами и сохранил указатель на функцию в каждом элементе массива. Каждый раз, когда вам нужно это делать, я бы оценил булевы в битовый шаблон, конвертировал в int и вызывал метод, хранящийся в этом индексе массива.
Я знаю, что вы спрашиваете о PHP, чего, боюсь, я не знаю. В C # вы можете сделать что-то вроде этого:
static class Multiplexer { public static string Multiplex(bool a, bool b, bool c, bool d) { var i = 0; i |= (a ? 1 : 0) << 3; i |= (b ? 1 : 0) << 2; i |= (c ? 1 : 0) << 1; i |= (d ? 1 : 0); return _functions[i](); } private static Func<string>[] _functions = new Func<string>[] { () => { return "pie";}, () => { return "index 1"; }, () => { return DateTime.Now.ToString(); }, () => { return "pie";}, () => { return "index 1"; }, () => { return DateTime.Now.ToString(); }, () => { return Assembly.GetExecutingAssembly().FullName; }, () => { return ""; }, () => { return "pie";}, () => { return "index 1"; }, () => { return DateTime.Now.ToString(); }, () => { return "pie";}, () => { return "index 1"; }, () => { return DateTime.Now.ToString(); }, () => { return Assembly.GetExecutingAssembly().FullName; }, () => { return ""; }}; }