упростить огромные заявления if – шаблон дизайна?

У меня потенциально есть набор операторов 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; } 

Есть ли способ лучше?

Related of "упростить огромные заявления if – шаблон дизайна?"

То, что я, скорее всего, сделаю (не зная специфики), построит серию классов для каждого состояния. Затем нажмите 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 ""; }}; }