Найти в Дзене

Написание логики без if/else на примере Java.

if и else уходят в закат Иногда на уме возникают какие-то безумные идеи - как писать логику без операций ветвления? Да, такая тема существует - ниже я объясню зачем, как это работает, какие техники используют, и в каких случаях это реально даёт пользу, а где - это просто красивая теория, которая на практике вредна. Зачем вообще избавляться от if/else. Коротко: Когда мотив имеет смысл 1. Слишком много if/else или switch - код трудно читать и невозможно расширять без правок. 2. Появляется состояние, зависящее от типа/статуса/режима, и каждый новый тип добавляет новые ветки. 3. Хотим избавиться от "god method" - огромной функции с кучей условий. 4. Нужно делать код расширяемым без правок, добавлять поведение через новые классы, а не новые if. Когда это не нужно 1. Когда условие одно-два - if читается лучше, чем усложнённая архитектура. 2. Когда логики мало и структура классов важнее простоты. 3. Когда ты просто следуешь моде - "чтобы не было if". Основная идея - перенести выбор поведения
Оглавление
if и else уходят в закат
if и else уходят в закат

Иногда на уме возникают какие-то безумные идеи - как писать логику без операций ветвления? Да, такая тема существует - ниже я объясню зачем, как это работает, какие техники используют, и в каких случаях это реально даёт пользу, а где - это просто красивая теория, которая на практике вредна.

Зачем вообще избавляться от if/else.

Коротко:

Когда мотив имеет смысл

1. Слишком много if/else или switch - код трудно читать и невозможно расширять без правок.

2. Появляется состояние, зависящее от типа/статуса/режима, и каждый новый тип добавляет новые ветки.

3. Хотим избавиться от "god method" - огромной функции с кучей условий.

4. Нужно делать код расширяемым без правок, добавлять поведение через новые классы, а не новые if.

Когда это не нужно

1. Когда условие одно-два - if читается лучше, чем усложнённая архитектура.

2. Когда логики мало и структура классов важнее простоты.

3. Когда ты просто следуешь моде - "чтобы не было if".

Основная идея - перенести выбор поведения из кода в структуру

И вместо:

if (status == PENDING) { ... }
else if (status == APPROVED) { ... }
else if (status == REJECTED) { ... }

использовать:

  • полиморфизм
  • стратегии
  • таблицы диспетчеризации
  • HashMap<Status, Handler>

Подмена if/else через полиморфизм (State / Strategy)

Есть enum статусов:

enum Status { PENDING, APPROVED, REJECTED }

Проблема - код разрастается:

switch (status) {
case PENDING -> doPending();
case APPROVED -> doApproved();
case REJECTED -> doRejected();
}

Решение — перенести поведение в классы:

interface StatusHandler {
void handle();
}

class PendingHandler implements StatusHandler {
public void handle() { System.out.println("pending"); }
}
class ApprovedHandler implements StatusHandler {
public void handle() { System.out.println("approved"); }
}
class RejectedHandler implements StatusHandler {
public void handle() { System.out.println("rejected"); }
}

Теперь таблица соответствий:

Map<Status, StatusHandler> handlers = Map.of(
Status.PENDING, new PendingHandler(),
Status.APPROVED, new ApprovedHandler(),
Status.REJECTED, new RejectedHandler()
);

handlers.get(status).handle();

- Без if.
- Удобно расширять: новый статус = новый класс, а не редактирование switch.

Таблично-управляемый дизайн (table-driven design)

Подходит, когда:

  • много параметров
  • логика меняется как "матрица решений"
  • if/else → нечитаемый ад

Например:

Map<Pair<UserType, Action>, Runnable> table = Map.of(
Pair.of(UserType.ADMIN, Action.DELETE), () -> delete(),
Pair.of(UserType.USER, Action.DELETE), () -> error(),
Pair.of(UserType.USER, Action.READ), () -> read(),
// ...
);

table.get(Pair.of(type, action)).run();

Это особенно красиво, когда логика - чистая математика/мэппинг.

Command Pattern

Вместо:

if (command.equals("start")) start()
else if (command.equals("stop")) stop()

Мы делаем:

Map<String, Command> commands = Map.of(
"start", new StartCommand(),
"stop", new StopCommand()
);

commands.get(cmd).execute();

Enum с поведением (встроенный полиморфизм)

Java может иметь методы внутри enum:

enum Status {
PENDING {
@Override void handle() { System.out.println("pending"); }
},
APPROVED {
@Override void handle() { System.out.println("approved"); }
};

abstract void handle();
}

Использование:

status.handle();

Тут вообще нет if - всё решает сам enum.

Но тут нужно быть осторожнее и не перегрузить enum сложной логикой.

Pattern Matching (Java 21+)

Это не устраняет if, но делает условные выражения структурированными:

switch(obj) {
case String s -> ...
case Integer i -> ...
case null -> ...
}

Практическое применение - когда это реально нужно

1. Много статусных систем

Например:

  • workflow (moderation → approved → scheduled → published)
  • заказ → оплачен → доставлен → отменён
  • product lifecycle в твоём project-пайплайне

Если у тебя есть:

"Каждый статус имеет свой набор разрешённых переходов и действий"

- то паттерн State полезен.

2. Parsers, interpreters

Когда каждый токен, тип узла AST или команда - представляет собственный класс.

3. Rule engines

Если правила меняются, а разработчики не хотят лезть в код - таблицы решений просто идеальны.

4. Большие if/else по нескольким параметрам

Когда логика запутывается вида:

if (a && !b && type == 3 && age > 18) ...

- это лучше выразить через Decision Table.

Когда это НЕ нужно

  • Если ваш if - на 2 строки
  • Если логика простая
  • Если количество условий не увеличивается со временем
  • Если усложнение архитектуры сделает хуже

Правило:

Код должен быть проще, а не "правильнее".
Убрать if - не самоцель.