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

В проекте, над которым я работаю, мы занимаемся выставлением счетов за медицинские услуги.

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

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

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

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

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


person Brett Allen    schedule 29.12.2009    source источник


Ответы (4)


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

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

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

  1. Factory, которая уже упоминалась ранее
  2. Композиция, которая, мы надеемся, избавит вас от некоторых неприятных проблем с наследованием.
  3. Шаблон для отделения логики действия формы от версий

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

  1. Head First Design Patterns (не могу вспомнить сразу)
  2. Шаблоны архитектуры корпоративных приложений (Фаулер)

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

  1. Начните с того, что возьмите все ваши текущие версии форм, разделите их на разные классы и найдите общие совпадения. Вы можете сделать это в черновом режиме или в соответствии с вашим текущим планом, чтобы иметь разные классы для каждой версии.
  2. Выведите из этой структуры класса ряд «базовых» объектов, которые вы можете использовать в качестве корня наследования для каждого значительного набора версий формы. Их может быть несколько, но их должно быть меньше, чем всего объектов формы.
  3. Теперь в каждом из этих базовых объектов сгруппируйте общие функции (например, структуры имен и адресов и т. д.) и абстрагируйте их в свою собственную иерархию классов, внедрите эту иерархию обратно в ваши базовые классы посредством внедрения конструктора.
  4. Примените шаблон композиции к вашей иерархии базовых классов и работайте над тем, чтобы получить как можно больше общей структуры классов. В идеале ваши формы на этом этапе должны иметь минимальную функциональность, которая изменилась в каждой форме, и иметь конструктор, который принимает все общие (и, возможно, слегка отличающиеся) функции.
  5. Примените шаблон шаблона сейчас, чтобы абстрагировать все общие «функциональные возможности» из ваших классов, такие как общая проверка и логика сохраняемости, снова используйте внедрение конструктора, чтобы вернуть эту функциональность в ваши базовые классы.
  6. Извлеките определения адаптера интерфейса из ваших базовых классов и используйте их в качестве основы для вашего приложения для работы с вашими формами.
  7. Создайте свои фабричные классы, чтобы справиться со всей кропотливой работой по настройке.

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

person GrayWizardx    schedule 29.12.2009
comment
Спасибо, у нас есть возможность переосмыслить наш дизайн из-за возможного пересмотра того, как действует федеральное правительство. Этот план очень поможет изучить возможности. - person Brett Allen; 30.12.2009
comment
НП. И удачи. Если у вас есть возможность связаться с некоторыми другими агентствами, такими как DHS (социальные службы) или DIS (иммиграционные службы), у них есть эта проблема в массовом масштабе, и они могут предоставить вам полезную информацию. - person GrayWizardx; 30.12.2009
comment
Я столкнулся с этим, потому что начал видеть «даты» в именах наших классов, то есть ClassA, ClassA20100901, ClassA20101201. Это было объяснено мне так: ClassA действителен/используется до 1 сентября, затем будет использоваться Class20100901, затем 1 декабря станет активным последний класс. Это ОГРОМНЫЙ запах, что что-то не так. Я переварю это и предложу команде. Я дам ответ, если мы найдем какие-либо советы. Благодарю вас! - person Tony R; 05.08.2010

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

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

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

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

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

person Eric J.    schedule 29.12.2009
comment
Если наследование нельзя использовать, потому что поля в документе могут быть удалены, как вы справляетесь с этим в настоящее время? Это не решило бы проблему классов, имеющих слишком много свойств (из предыдущих версий, оставленных для обратной совместимости). - person Brett Allen; 29.12.2009
comment
Фабричный шаблон кажется правдоподобным решением, и, поскольку строки принятия данных привязки не будут иметь сильных ошибок компилятора при наборе текста... - person Brett Allen; 29.12.2009
comment
@Aequitarum: конкретная версия пользовательского интерфейса, по-видимому, не будет привязана к полям, которые не подходят для этой версии. Наличие подкласса для конкретной версии выдает NotImplementedException, если одно такое свойство привязано к пользовательскому интерфейсу, в конце концов, помогает обнаруживать такие проблемы на ранней стадии. - person Eric J.; 30.12.2009

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

person rein    schedule 29.12.2009

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

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

person JuanZe    schedule 29.12.2009