Версии класса для поддержки обратной совместимости

В проекте, над которым я работаю, мы обрабатываем Medical Billing.

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

Это привело к раздутым классам в течение всего срока действия проекта (почти 5 лет с изменениями, санкционированными государством) и просто не поддерживает старые форматы документов, это не вариант.

Я хотел бы попробовать создать новый класс для каждой версии документа, но даже тогда у нас будет несколько копий очень похожего (хотя и слегка измененного) кода. И имена классов, такие как ProgressNoteV16, ProgressNoteV17 выглядят ужасно.

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

Каковы решения и рекомендации, используемые для решения этой проблемы?

Ответ 1

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

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

Есть несколько шаблонов, которые должны быть знакомы (если вы уже не готовы):

  • Factory, о котором уже упоминалось
  • Состав, который, надеюсь, выведет вас из некоторых проблем с уродливым наследованием
  • Template для разделения логики того, что форма делает из версий

Есть две книги, которые очень хороши для объяснения этих (и нескольких других полезных):

  • Нарисуйте первые шаблоны дизайна (не помните, как я с головы до головы)
  • Шаблоны архитектуры корпоративных приложений (Fowler)

Теперь к процессу, который я использовал в прошлом:

  • Начните с принятия всех ваших текущих версий форм, отделите их на разные классы и посмотрите на общее совпадение. Вы можете сделать это в черновом режиме или как ваш текущий план иметь разные классы для каждой версии
  • Вывести из этой структуры классов ряд "базовых" объектов, которые вы можете использовать как корень наследования для каждого значительного набора версий для формы. У вас может быть несколько таких, но они должны быть меньше, чем у вас есть общие объекты формы.
  • Теперь в каждом из этих базовых объектов объединяйте общие функции (т.е. структуры имен и адресов и т.д.) и отвлеките это на свою собственную иерархию классов, добавьте эту иерархию обратно в свои базовые классы через инъекцию конструктора.
  • Примените шаблон состава к иерархии базового класса и работайте над тем, чтобы как можно более сложная структура классов. В идеале ваши формы в этот момент были бы минимальной функциональностью, которая была изменена в каждой форме и имела конструктор, который принимает все обычные (и, возможно, слегка меняющиеся) функциональные возможности.
  • Примените шаблон шаблона теперь, чтобы абстрагироваться от всей общей "функциональности" из ваших классов, такой как общая проверка и логика сохранения, снова используйте конструктор, чтобы вернуть эту функциональность обратно в базовые классы.
  • Извлеките определения адаптера интерфейса из базовых классов и используйте их в качестве основы для работы вашего приложения с вашими формами.
  • Создайте классы factory, чтобы обрабатывать все необходимые действия по настройке.

Надеюсь, что вы получите некоторые идеи о том, что делать.

Ответ 2

Если наследование невозможно использовать "потому что поля в документе могут быть удалены", как вы справляетесь с этим в настоящее время?

В зависимости от деталей вашего дизайна вы должны иметь возможность переопределять поля, не используемые текущей версией документа (т.е. переопределять в подклассе для этого документа), и бросать исключение NotSupportedException или подобное в реализации этого метода, если форма пытается присвоить значение. По аналогичной логике вы можете (и, вероятно, должны) использовать интерфейсы... просто зная, что конкретная реализация может генерировать исключение для свойства, которое оно реализует, но которое не определено для этой версии пользовательского интерфейса.

Вы можете использовать Factory Pattern, чтобы вернуть соответствующий экземпляр объекта для данной версии формы.

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

Как и для именования классов, если вы используете шаблон Factory, вы можете обратиться к классу в большинстве своих кодов с помощью интерфейса. Только самому Factory придется иметь дело с "странными" типами имен классов.

Ответ 3

Я бы реализовал это гораздо более слабо связанно - класс, содержащий два поля: версию и словарь. Когда данные изменяются, это очень утомительно, чтобы попытаться отслеживать утраченные и добавленные свойства. Со словарем вы можете просто проверить, существует ли ключ.

Ответ 4

Без каких-либо подробностей вашего кода, я думаю, вы могли бы попробовать идеи Designator design... Взгляните на эту статью: Избегайте чрезмерного подкласса с шаблоном проектирования Decorator

Проверка Шаблоны для вещей, которые изменяются со временем Мартина Фаулера, также заслуживает внимания, чтобы получить полезные идеи для такого сценария проектирования.