Допустим, у нас есть система отмены/возврата, и у нас есть суперкласс, хранящий объект, который позже должен сформировать дерево атрибутов, которые можно отменить/повторить.
Это потребует, чтобы каждый класс, который должен участвовать в отмене/возврате, расширялся от этого одного суперкласса, в зависимости от того, насколько большой будет система, это может привести к 40, 50 или кто знает, сколько прямых подклассов.
Будет ли это плохой практикой? Есть ли что-то вроде «слишком много прямых подклассов»?
Обновлено: каждый атрибут, который можно отменить, является экземпляром, например. ValueBoolean, ValueInt и т. д.. Все эти значения имеют имя идентификатора и регистрируются в корне для формирования дерева, например. «Редактор» имеет дочерние элементы, «включенные», «ввод» и т. д. Этот корень и некоторый код сериализации — это то, что находится в суперклассе, о котором я упоминал. Теперь, когда что-то отменяется, система ищет идентификатор в дереве (с путем для правильного поиска дочерних элементов).
Я не писал эту систему, я пытаюсь понять, хорошо ли она спроектирована. Мне показалось немного странным, что все должно расширять один класс.
Я не думаю, что количество потомков (само по себе) обязательно является проблемой.
Но я сомневаюсь, что наследование в любом случае является идеальным способом реализации такого поведения.
Помещая отмену/повтор в основу дерева, вы утверждаете, что все, что от нее происходит, также должно поддерживать отмену/повтор. Однако это может не сработать с существующей иерархией. В существующей иерархии у вас вполне может быть несколько довольно специфических узлов, которые должны поддерживать отмену/возврат, но эта поддержка (по крайней мере, в большей или меньшей степени) отличается от существующей иерархии.
В довольно многих случаях именно контекст определяет, должно ли что-то поддерживать отмену/возврат, а не сам объект. Таким образом, у вас могут быть некоторые объекты класса, которые должны поддерживать отмену, а другие — нет.
Есть несколько довольно распространенных способов реализации отмены/повтора. Шаблон команды один. И то, о чем вы говорите, может быть в значительной степени реализацией Command (или нет - вы не дали достаточно подробностей, чтобы быть уверенным). Чтобы реализовать паттерн Command, ваш базовый класс должен определить что-то вроде методов execute
и undo
, так что все, что унаследовано от него, сможет делать или отменять это «вещь» по команде. Это работает, но имеет тенденцию быть довольно навязчивым для объектов, которые должны поддерживать отмену/возврат. Он также, как правило, имеет довольно тесную связь между механизмом сохранения/получения/восстановления состояния и политикой для шаблона(ов), который вы поддерживаете/не поддерживаете при этом (так, например, это часто не поддерживает такие вещи, как «повторить A, C и D, но не B»).
Еще один распространенный метод — это шаблон на память. Это в основном своего рода общий способ хранения внутреннего состояния объекта. Обычно у вас есть Создатель (вещь, которую вы хотите отменить/восстановить), сувенир (само внутреннее состояние) и Смотритель, который получает и сохраняет сувенир, а также занимается фактическим сохранением и восстановлением сувениров для реализации отмены/восстановления. переделывать по мере необходимости. Преимущество здесь в том, что он отделяет реализацию отмены/повтора от объектов, которые вы хотите иметь возможность отменить/повторить. Весь объект должен иметь возможность производить или потреблять сувенир, а решение о том, как и когда сказать ему об этом, зависит от (совершенно отдельного) класса смотрителя. Это, как правило, облегчает добавление поддержки нелинейных шаблонов отмены/повтора, таких как описанный выше, когда повторяются A, C и D, но пропускается B.