Шаблон команд и сложные операции в C # - PullRequest
2 голосов
/ 25 декабря 2011

Я пишу программу на C #, которая должна поддерживать отмену / повтор. Для этого я остановился на шаблоне Command; tldr, каждая операция, которая манипулирует состоянием документа, должна выполняться объектом Command, который знает о предыдущем состоянии документа, а также об изменениях, которые необходимо внести, и способен сам выполнять / отменять действия.

Он отлично работает для простых операций, но теперь у меня есть операция, которая затрагивает несколько частей документа одновременно. Аналогично, объект Command должен быть достаточно умен, чтобы знать все старое состояние, которое он должен сохранить в случае необходимости отмены.

Проблема в том, что раскрытие всего этого состояния с использованием общедоступных интерфейсов может привести к неправильному использованию, если кто-то попытается вызвать интерфейс напрямую, что может привести к повреждению состояния. Мои инстинкты говорят мне, что наиболее внушительный способ сделать это - предоставить специализированные классы Command - вместо того, чтобы позволять вам напрямую манипулировать состоянием документа, все, что вы можете сделать, - попросить документ создать объект Command, который имеет доступ к внутреннее состояние и гарантированно знает достаточно для правильной поддержки отмены / возврата.

К сожалению, C # не поддерживает концепцию друзей, поэтому я не могу создать класс Command, который имеет доступ к внутренним документам. Есть ли способ показать закрытые члены класса документа другому классу, или есть какой-то другой способ сделать то, что мне нужно, без необходимости раскрывать много внутренних документов?

Ответы [ 4 ]

2 голосов
/ 25 декабря 2011

Зависит от того, что если вы развертываете библиотеку, ваш Document может объявлять «внутренние» методы для взаимодействия с ее внутренним состоянием, эти методы будут использоваться вашим классом Command, внутренние методы ограничены сборкой, которую они компилируют.

Или вы можете вложить закрытый класс в ваш документ, и таким образом он сможет получить доступ к внутреннему состоянию документа и открыть для него открытый интерфейс, тогда ваш документ создаст командный класс, скрытый этим интерфейсом.

2 голосов
/ 26 декабря 2011

Во-первых, в C # есть ключевое слово internal, которое объявляет доступность «друг», что обеспечивает открытый доступ изнутри всей сборки.

Во-вторых, доступность «друг» может быть расширена довторая сборка с атрибутом сборки InternalsVisibleTo, чтобы вы могли создать второй проект для ваших команд, и все же внутренние документы останутся внутренними.

В качестве альтернативыЕсли ваши командные объекты вложены в класс документа, то они будут иметь доступ ко всем его закрытым членам.

Наконец, сложные команды могут также просто клонировать документ перед внесением изменений.Это простое решение, хотя и не очень оптимизированное.

0 голосов
/ 26 декабря 2011

Вместо команды, выполняющей изменения в разных местах документа, у вас могут быть две фиктивные команды, которые отмечают начало и конец многошаговых операций.Давайте назовем их BeginCommand и EndCommand.Сначала вы помещаете BeginCommand в стек отмены, а затем выполняете различные шаги в виде отдельных команд, каждая из которых вносит изменения только в одном месте документа.Конечно, вы также помещаете их в стек отмены.Наконец, вы помещаете EndCommand в стек отмены.

При отмене проверяете, является ли команда, извлеченная из стека отмены, EndCommand.Если это так, вы продолжаете отменять, пока не будет достигнут BeginCommand.

Это превращает многошаговую команду в макро-команду, делегирующую работу другим командам.Эта макрокоманда сама по себе не помещается в стек отмены.

0 голосов
/ 25 декабря 2011

Вы всегда можете получить доступ к полям и свойствам, частным или нет, с помощью отражения (Type.GetField(string, BindingFlags.Private) & friends).

Может быть, с пользовательским атрибутом класса (или поля /свойство) автоматизировать процесс захвата достаточного состояния для каждой команды?

...