альтернатива оператору if в java
так что, как вы видите, все операторы if являются отдельными, а не другими условиями. Обратите внимание, что XYZ — это совершенно разные условия, поэтому переключатель не подходит.
Хотя любопытство может быть забавным . зачем мне альтернатива if, особенно когда условие состоит в том, что переключатель не подходит? Как будто Java еще недостаточно раздулась. — ty812
либо то, что вы пытаетесь сделать, глупо (если это здорово!), либо вам нужно предоставить больше информации. Я могу представить себе ситуацию, когда подобный код можно было бы обработать с помощью шаблона обмена сообщениями или чего-то еще интересного (при условии, что ваша ситуация не так проста, как вы ее объяснили). — twolfe18
Что вы ожидаете от этой альтернативы, чего не может предоставить представленный код? (меньше символов, больше читаемости?) — Zed
@Hellnar: Есть дополнительные знания, которые имеют ценность, и есть неясный код для гольфа. — S.Lott
11 ответы
Одним из «истинно объектно-ориентированных» ответов было бы определить интерфейс для «Правила» (с методами condition() и action()), создать 3 реализации, поместить их в коллекцию, а затем просто выполнить итерацию по ним в общем, как в:
Список правила = . ; // ваши 3 правила каким-то образом инициализированы здесь for(Rule r : rules) < if(r.condition()) < r.action(); >>
Это имеет гораздо больше смысла, если у вас есть 300 правил/условий, а не только 3. В Java8 вы можете сделать это вместо этого, если правила интенсивно используют ЦП:
rules.parallelStream().filter(Rule::condition).forEach(Rule::action);
Карта здесь не сработает, потому что условие уникально для каждой реализации правила. Map требует фактического ключа, а не какого-то кода, скрытого в вызываемом методе. — Alex R
- Полиморфизм, когда поведение зависит от начальных значений
- Ссылочное присвоение, когда вы знаете возможные начальные значения, и они имеют корреляцию 1 к 1 с возвращаемыми значениями. Списки для этого лучше, чем массивы, но.
// Example: if (a==1) < b=2; >if (a==2) < b=17; >// Becomes int fx(2); // our array of answers fx[0] = 2; fx[1] = 17; b = fx[ a - 1 ];
// Example: if (a==1) < doSomething1(); >if (a==2) < doSomething2(); >// Becomes function * fx(2); // our array or better still, list of functions fx[0] = &doSomething1; fx[1] = &doSomething2; `fx[ a - 1 ](); `
if (thisCondition == true) < b = true; >else
Альтернативы if-else в Java — оператор switch и оператор условно троичный (?:), ни один из которых не делает именно то, что вы просите (обрабатывает только if без else ). Код, который вы разместили, — лучший способ сделать это, на мой взгляд.
interface SomethingDoer < public void doSomething(); >class ADoer implements SomethingDoer < . >class BDoer implements SomethingDoer < . >class CDoer implements SomethingDoer < . >public class Main < public static void main (String[] args) < SomethingDoer doer = new SomethingDoerFactory(args).getDoer(); doer.doSomething(); >>
Оператор if не устранен полностью, но перемещен в SomethingDoerFactory. Это решение применимо не во всех случаях, но в некоторых из них является очень хорошей альтернативой множественным «если».
Любые комментарии о том, почему вы минусуете? Разве это не альтернатива? Или это недостаточно хорошо, чтобы быть упомянутым? — Артемб
Я бы рассматривал полиморфное решение только в том случае, если бы мое условное выражение уже было основано на напишите. Я бы не стал создавать целую иерархию классов только для того, чтобы заменить if(x) do a; — Билл Ящерица
во многих случаях это имеет смысл. видеть youtube.com/watch?v=4F72VULWFvc — говорит технический специалист Google. (Разговоры о чистом коде — наследование, полиморфизм и тестирование) я считаю, что отрицательный голос неправильный. — Андреас Петерссон
Прямо со слайдов Мисько: «иногда if — это просто if «. Если вы не будете повторно использовать эти операторы if во многих местах, это сделает вещи излишне сложными. Также: ваше решение неполное. Что, если выполняются оба условия x и y? Вы пропустили AAndBDoer, AAndCDoer, BAndCDoer, AAndBAndCDoer; они хорошо покрыты исходным кодом. — Зак Томпсон
Очевидно, это зависит от того, что именно означает «делать». Полиморфизм вполне может быть отличным решением, но пример слишком общий, чтобы иметь единственное решение для всех случаев. AAndBDoer и т. д. обычно решаются с помощью шаблонов Decorator/Composition для решения типа полиморфизма. — Eek
это самое простое, читабельно, но эффективно решение. Я был бы удивлен, увидев здесь эффективные альтернативы.
можно попробовать применить метод извлечения несколько раз:
Я делаю это в прошивке для устройства, в котором может быть включено или отключено множество различных опций. Я бы предпочел, чтобы мой main() вызывал такие вещи, как doFooIfEnabled(), чем загромождал его множеством ifs и #ifdefs. — Жанна Пиндар
Как и любой шаблон, есть моменты, когда его следует использовать, и моменты, когда его не следует использовать. — бдонлан
Сделайте что-то вроде этого: внедрите Enum на основе условия и вызовите метод переопределения.
public enum Rule < a()< @Override public void do()< // do something. >>, b() < @Override public void do()< // do something. >>, c() < @Override public void do()< // do something. >public abstract void do(); > > public class Test < public static void main(String[] args)< Rule r = Rule.valueOf("a"); r.do(); >>
Как заменить многие операторы if в Java
Конструкции принятия решений являются жизненно важной частью любого языка программирования. Но мы попадаем в кодирование огромного количества вложенных операторов if, которые делают наш код более сложным и трудным в обслуживании.
В этом уроке мы рассмотрим различные способы замены вложенных операторов if .
Давайте рассмотрим различные варианты того, как мы можем упростить код.
2. Тематическое исследование
Часто мы сталкиваемся с бизнес-логикой, которая включает в себя множество условий, и каждое из них требует различной обработки. Для демонстрации давайте возьмем пример класса Calculator . У нас будет метод, который принимает два числа и оператор в качестве входных данных и возвращает результат на основе операции:
public int calculate(int a, int b, String operator) < int result = Integer.MIN_VALUE; if ("add".equals(operator)) < result = a + b; >else if ("multiply".equals(operator)) < result = a * b; >else if ("divide".equals(operator)) < result = a / b; >else if ("subtract".equals(operator)) < result = a - b; >return result; >
Мы также можем реализовать это с помощью switch statements :
public int calculateUsingSwitch(int a, int b, String operator) < switch (operator) < case "add": result = a + b; break; // other cases >return result; >
В типичном развитии утверждения if могут стать намного больше и сложнее по своей природе . Кроме того, операторы switch плохо подходят для сложных условий .
Еще одним побочным эффектом вложенных конструкций решений является то, что они становятся неуправляемыми. Например, если нам нужно добавить новый оператор, мы должны добавить новый оператор if и реализовать операцию.
3. Рефакторинг
Давайте рассмотрим альтернативные варианты замены сложных операторов if выше на гораздо более простой и управляемый код.
3.1. Заводской класс
Много раз мы сталкиваемся с конструкциями решений, которые в конечном итоге выполняют аналогичную операцию в каждой ветви. Это дает возможность извлечь фабричный метод, который возвращает объект заданного типа и выполняет операцию на основе поведения конкретного объекта .
Для нашего примера давайте определим интерфейс Operation , который имеет один apply метод:
public interface Operation
Метод принимает два числа в качестве входных данных и возвращает результат. Давайте определим класс для выполнения дополнений:
public class Addition implements Operation < @Override public int apply(int a, int b) < return a + b; >>
Теперь мы реализуем фабричный класс, который возвращает экземпляры Operation на основе данного оператора:
public class OperatorFactory < static MapoperationMap = new HashMap<>(); static < operationMap.put("add", new Addition()); operationMap.put("divide", new Division()); // more operators >public static Optional getOperation(String operator) < return Optional.ofNullable(operationMap.get(operator)); >>
Теперь в классе Calculator мы можем запросить завод, чтобы получить соответствующую операцию и применить исходные номера:
public int calculateUsingFactory(int a, int b, String operator) < Operation targetOperation = OperatorFactory .getOperation(operator) .orElseThrow(() ->new IllegalArgumentException("Invalid Operator")); return targetOperation.apply(a, b); >
В этом примере мы видели, как ответственность делегируется слабо связанным объектам, обслуживаемым фабричным классом. Но могут быть шансы, что вложенные операторы if просто будут перенесены в класс factory, что противоречит нашей цели.
В качестве альтернативы, мы можем поддерживать репозиторий объектов в Карте , который может быть запрошен для быстрого поиска . Как мы видели, Operator Factory#operation Map служит нашей цели. Мы также можем инициализировать Map во время выполнения и настроить их для поиска.
3.2. Использование перечислений
В дополнение к использованию Map, мы также можем использовать Enum для обозначения конкретной бизнес-логики . После этого мы можем использовать их либо во вложенных операторах if , либо в операторах |//switch case |//. Кроме того, мы также можем использовать их в качестве фабрики объектов и разрабатывать стратегию для выполнения соответствующей бизнес-логики.
Это также уменьшило бы количество вложенных операторов if и делегировало бы ответственность отдельным значениям Enum .
Давайте посмотрим, как мы можем этого достичь. Сначала нам нужно определить наше Перечисление :
Как мы можем наблюдать, значения являются метками различных операторов, которые будут использоваться в дальнейшем для расчета. У нас всегда есть возможность использовать значения в качестве различных условий во вложенных операторах if или случаях переключения, но давайте разработаем альтернативный способ делегирования логики самому перечислению .
Мы определим методы для каждого из значений Enum и выполним расчет. Например:
ADD < @Override public int apply(int a, int b) < return a + b; >>, // other operators public abstract int apply(int a, int b);
А затем в классе Calculator мы можем определить метод для выполнения операции:
public int calculate(int a, int b, Operator operator)
Теперь мы можем вызвать метод путем преобразования String значения в Оператор с помощью Operator#valueOf() метод :
@Test public void whenCalculateUsingEnumOperator_thenReturnCorrectResult()
3.3. Шаблон команд
В предыдущем обсуждении мы видели использование класса factory для возврата экземпляра правильного бизнес-объекта для данного оператора. Позже бизнес-объект используется для выполнения расчета в Калькуляторе .
Мы также можем разработать метод калькулятора#calculate , чтобы принять команду, которая может быть выполнена на входах . Это будет еще один способ замены вложенных операторов if .
Сначала мы определим наш Командный интерфейс:
Далее, давайте реализуем команду Add:
public class AddCommand implements Command < // Instance variables public AddCommand(int a, int b) < this.a = a; this.b = b; >@Override public Integer execute() < return a + b; >>
Наконец, давайте представим новый метод в Калькуляторе , который принимает и выполняет команду :
public int calculate(Command command)
Затем мы можем вызвать вычисление, создав экземпляр команды Add и отправив ее в метод Calculator#calculate :
@Test public void whenCalculateUsingCommand_thenReturnCorrectResult()
3.4. Механизм правил
Когда мы в конечном итоге пишем большое количество вложенных операторов if, каждое из условий отображает бизнес-правило, которое должно быть оценено для правильной обработки логики. Механизм правил выводит такую сложность из основного кода. Механизм Правил оценивает Правила и возвращает результат на основе входных данных.
Давайте рассмотрим пример, разработав простой механизм Правил , который обрабатывает Выражение через набор Правил и возвращает результат из выбранного Правила . Во-первых, мы определим Правило интерфейс:
Во-вторых, давайте реализуем Механизм правил :
public class RuleEngine < private static Listrules = new ArrayList<>(); static < rules.add(new AddRule()); >public Result process(Expression expression) < Rule rule = rules .stream() .filter(r ->r.evaluate(expression)) .findFirst() .orElseThrow(() -> new IllegalArgumentException("Expression does not matches any Rule")); return rule.getResult(); > >
Механизм Правил принимает Выражение объекта и возвращает Результат . Теперь , давайте спроектируем класс Expression как группу из двух Целых объектов с оператором , который будет применен:
И, наконец, давайте определим пользовательский класс addRule , который вычисляется только тогда, когда указана операция ADD :
public class AddRule implements Rule < @Override public boolean evaluate(Expression expression) < boolean evalResult = false; if (expression.getOperator() == Operator.ADD) < this.result = expression.getX() + expression.getY(); evalResult = true; >return evalResult; > >
Теперь мы вызовем механизм правил с выражением |:
@Test public void whenNumbersGivenToRuleEngine_thenReturnCorrectResult()
4. Заключение
В этом уроке мы рассмотрели ряд различных вариантов упрощения сложного кода. Мы также узнали, как заменить вложенные операторы if с помощью эффективных шаблонов проектирования.
Как всегда, мы можем найти полный исходный код в репозитории GitHub .