Я поддерживаю программное обеспечение CAD / CAM для металлорежущего станка. Так что у меня есть некоторый опыт с этими проблемами.
Когда мы впервые преобразовали наше программное обеспечение (оно было впервые выпущено в 1985 году!) В объектно-ориентированный дизайн, я сделал то, что вам не нравится. Объекты и интерфейсы имели Draw, WriteToFile и т. Д. Обнаружение и чтение шаблонов проектирования в середине процесса преобразования очень помогло, но все еще было много неприятных запахов кода.
В конце концов я понял, что ни один из этих типов операций на самом деле не касался объекта. Скорее, различные подсистемы, необходимые для выполнения различных операций. Я справился с этим с помощью так называемого объекта Passive View Command и четко определенного интерфейса между уровнями программного обеспечения.
Наше программное обеспечение в основном имеет такую структуру
- Формы, реализующие различные Формы
Интерфейс. Эти формы являются оболочкой, передающей события на уровень пользовательского интерфейса.
- Уровень пользовательского интерфейса, который получает события и манипулирует формами через интерфейс Form.
- Уровень пользовательского интерфейса будет выполнять команды, которые все реализуют интерфейс команд
- Объект пользовательского интерфейса имеет свои собственные интерфейсы, с которыми команда может взаимодействовать.
- Команды получают необходимую им информацию, обрабатывают ее, манипулируют моделью и затем отчитываются перед объектами пользовательского интерфейса, которые затем делают все необходимое с формами.
- Наконец, модели, которые содержат различные объекты нашей системы. Как программы форм, траектории резки, раскройный стол и листы металла.
Таким образом, рисование обрабатывается на уровне пользовательского интерфейса. У нас разное программное обеспечение для разных машин. Таким образом, в то время как все наши программы используют одну и ту же модель и используют много одинаковых команд. Они занимаются такими вещами, как рисование совсем по-другому. Например, раскройный стол ничем не отличается от станка с маршрутизатором от станка с плазменной горелкой, несмотря на то, что оба они по сути являются гигантским плоским столом X-Y. Это потому, что, как и в случае с автомобилями, эти две машины построены достаточно по-разному, так что для клиента существует визуальная разница.
Что касается фигур, мы делаем следующее
У нас есть программы форм, которые создают траектории резки по введенным параметрам. Путь резки знает, какая программа обработки формы. Однако траектория резки не является формой. Это просто информация, необходимая для рисования на экране и разрезания фигуры. Одна из причин такого дизайна заключается в том, что траектории резки могут быть созданы без программы фигур, когда они импортируются из внешнего приложения.
Этот дизайн позволяет нам отделить дизайн траектории резания от дизайна формы, которые не всегда одно и то же. В вашем случае, вероятно, все, что вам нужно, - это информация, необходимая для рисования фигуры.
Каждая программа-фигура имеет несколько представлений, реализующих интерфейс IShapeView. Через интерфейс IShapeView программа shape может сообщить общей форме формы, которая у нас есть, как настроить себя, чтобы показать параметры этой формы. Универсальная форма формы реализует интерфейс IShapeForm и регистрируется в объекте ShapeScreen. Объект ShapeScreen регистрируется в нашем объекте приложения. Представления формы используют любой экран формы, который регистрируется в приложении.
Причина множества просмотров, которые есть у наших клиентов, которые любят вводить фигуры по-разному. Наша клиентская база разделена пополам между теми, кто любит вводить параметры формы в форме таблицы, и теми, кто любит вводить с графическим представлением формы перед ними. Нам также необходимо время от времени получать доступ к параметрам через минимальное диалоговое окно, а не через экран ввода полной формы. Отсюда и несколько просмотров.
Команды, управляющие формами, попадают в одну из двух категорий. Либо они управляют траекторией резки, либо они управляют параметрами формы. Чтобы манипулировать параметрами формы в целом, мы либо возвращаем их обратно на экран ввода формы, либо показываем минимальный диалог. Пересчитайте фигуру и отобразите ее в том же месте.
Для пути резки мы объединяли каждую операцию в отдельный объект команды. Например, у нас есть объекты команды
ResizePath
RotatePath
MovePath
SplitPath
и т. д.
Когда нам нужно добавить новую функциональность, мы добавляем еще один объект команды, на правой стороне экрана пользовательского интерфейса находим меню, короткую клавиатуру или слот для кнопки панели инструментов и настраиваем объект интерфейса для выполнения этой команды.
Например
CuttingTableScreen.KeyRoute.Add vbShift+vbKeyF1, New MirrorPath
или
CuttingTableScreen.Toolbar("Edit Path").AddButton Application.Icons("MirrorPath"),"Mirror Path", New MirrorPath
В обоих случаях объект Command MirrorPath связан с требуемым элементом пользовательского интерфейса. В методе выполнения MirrorPath есть весь код, необходимый для зеркального отображения пути на определенной оси. Скорее всего, команда будет иметь собственный диалог или использовать один из элементов пользовательского интерфейса, чтобы спросить пользователя, какую ось отразить. Ничто из этого не делает посетителя или добавляет метод к пути.
Вы обнаружите, что многое можно обработать путем объединения действий в команды. Однако я предупреждаю, что это не черно-белая ситуация. Вы по-прежнему обнаружите, что определенные вещи лучше работают как методы для исходного объекта. По своему опыту я обнаружил, что, возможно, 80% того, что я делал в методах, можно было перенести в команду. Последние 20% просто работают лучше на объекте.
Теперь некоторым это может не понравиться, потому что кажется, что оно нарушает инкапсуляцию. От сохранения нашего программного обеспечения в качестве объектно-ориентированной системы в течение последнего десятилетия я должен сказать, что САМОЕ важное долгосрочное действие, которое вы можете сделать, это четко задокументировать взаимодействия между различными уровнями вашего программного обеспечения и между различными объектами.
Объединение действий в объекты Command помогает достичь этой цели лучше, чем рабская преданность идеалам инкапсуляции. Все, что нужно сделать, чтобы отразить путь, объединено в объекте команды Mirror Path.