Разработать универсальный механизм отмены и возврата действий

Храните команды с парой redo/undo и два стека истории. Новое действие очищает redo-историю, а отмена и повтор перемещают команду между стеками.

Короткий ответ

Что ответить на собеседовании

Храните команды с парой redo/undo и два стека истории. Новое действие очищает redo-историю, а отмена и повтор перемещают команду между стеками.

Подробный разбор

Ответ с пояснениями

Условие

Во многих приложениях, таких как текстовые и графические редакторы, реализуется возможность откатывать последнее действие и повторно его выполнять (Undo/Redo). Требуется написать компонент, который будет легко интегрироваться в разные типы редакторов, предоставляя единый интерфейс для управления историей изменений.

Ответ

Храните команды с парой redo/undo и два стека истории. Новое действие очищает redo-историю, а отмена и повтор перемещают команду между стеками.

Минимальный компонент для синхронных невыбрасывающих действий:

@MainActor
final class History {
    struct Command {
        let redo: () -> Void
        let undo: () -> Void
    }
    private var past: [Command] = []
    private var future: [Command] = []
    func perform(_ command: Command) {
        command.redo()
        past.append(command)
        future.removeAll()
    }
    func undo() {
        guard let command = past.popLast() else { return }
        command.undo()
        future.append(command)
    }
    func redo() {
        guard let command = future.popLast() else { return }
        command.redo()
        past.append(command)
    }
}

Команда сохраняет прежние данные или обратимую операцию; редактор отвечает за правильность этих closures. Не вызывайте perform из самой команды. Для production добавьте группировку, лимит памяти и стратегию ошибок; внешние необратимые действия не превращаются в Undo автоматически.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее