Стиль Delphi: Как структурировать модули данных для тестируемого кода? - PullRequest
11 голосов
/ 11 февраля 2011

Я ищу несколько советов по структурированию Delphi-программ для удобства обслуживания.Я пришел к программированию на Delphi после пары десятилетий, в основном на C / C ++, хотя я впервые научился программировать с Turbo Pascal, поэтому я не чувствую себя неловко с базовым языком.В моем предыдущем опыте работы с C ++ и C # я стал преобразовывать TDD, используя cxxtest и NUnit.

Я унаследовал эту программу, за которую теперь отвечаю.Он состоит в основном из форм и нескольких модулей данных.Бизнес-логика приложения и доступ к данным в основном разбросаны по формам, а модули данных - это всего лишь места, где могут жить глобальные объекты ADO.Доступ к базе данных обычно осуществляется путем обращения к глобальному экземпляру TADOQuery или TADOCommand, форматирования текста SQL прямо в соответствующем свойстве объекта и вызова его метода Open или Execute.

Я пытаюсь получить бизнеслогика в степени инкапсуляции, где она может быть проверена модулем.Я видел этот ответ , и он имеет смысл, если абстрагировать логику от форм.Мне интересно, каковы лучшие практики для доступа к данным.Я думаю, что модули данных должны предоставлять своего рода мини-API для приложений (возможно, со всеми виртуальными методами), чтобы их можно было заменить на фиктивные объекты для тестирования.Ссылка на этот другой ответ показывает некоторые примеры, которые наводят меня на мысль, что я на правильном пути, но я все еще заинтересован в том, чтобы увидеть какой-то документ с рекомендациями по модулям данных.Большинство страниц, которые я могу найти в Google, представляют собой примеры того же рода обо всех интересных вещах, которые вы можете делать во время разработки, подключая привязанные к данным элементы управления к запросам и тому подобное, что меня не очень интересует.на данный момент.

Ответы [ 4 ]

8 голосов
/ 15 февраля 2011

Лично я не фанат TDataModule.Это очень мало для поощрения хороших принципов проектирования ОО.Если бы все это использовалось для создания удобного контейнера для компонентов БД, это было бы одно, но слишком часто это превращалось в дамп для бизнес-логики, которая была бы лучше на уровне домена.Когда это происходит, он становится классом бога и магнитом зависимости.

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

Мое предложение

  • Добавьте слой домена между вашим пользовательским интерфейсом и базой данных
  • Вставьте как можно большую часть своей бизнес-логики в доменные объекты.
  • Сделайте свой пользовательский интерфейс и уровни персистентности данных как можно более мелкими, используя дизайн и архитектурные шаблоны для делегирования принятия решений на уровне домена.

Если вы не знакомы с ним, этот метод называется доменный дизайн .Это, конечно, не единственное решение, но хорошее.Основная предпосылка заключается в том, что пользовательский интерфейс, бизнес-логика и база данных меняются с разной скоростью и по разным причинам.Поэтому сделайте бизнес-логику моделью проблемной области и отделите ее от пользовательского интерфейса и базы данных.

Как это делает мой код более тестируемым?

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

Это стиль Delphi ?

Это зависит от вашей точки зрения.Традиционно большинство приложений Delphi создавались путем разработки пользовательского интерфейса и базы данных в тандеме.Отбросьте несколько элементов управления в базе данных на конструкторе форм.Добавить / обновить таблицу с полями для хранения данных элемента управления.Посыпать либеральным количеством бизнес-логики, используя обработчики событий.Виола!Вы только что испекли заявку.Для очень маленьких приложений это отличная экономия времени.Но давайте не будем обманывать себя, маленькие приложения имеют тенденцию превращаться в большие, и этот дизайн становится непреодолимым кошмаром обслуживания.

Это действительно не ошибка языка.Вы найдете такие же быстрые / грязные / близорукие дизайны из сотен магазинов VB, C # и Java.Подобные приложения являются результатом работы начинающих разработчиков, которые не знают ничего лучше (и опытных разработчиков, которые должны знать лучше), IDE, которая делает ее такой простой и требует быстрого выполнения работы.

В сообществе Delphi (как и в других сообществах) есть люди, которые давно отстаивают лучшие методы проектирования.

7 голосов
/ 11 февраля 2011

Я думаю, что вам нужен (и на самом деле большинству разработчиков баз данных Delphi понадобится) компонент Mock Dataset (запрос, таблица и т. Д.), Который вы можете использовать, и заменить его во время инициализации модуля для вашей текущейADO набор объектов данных для этого фиктивного набора данных, в целях тестирования.Вместо того, чтобы встраивать интерфейсы в свой дизайн, которые являются одним из способов обеспечения возможности замещения, следует учитывать тот факт, что по принципу подстановки Лискова вы сможете (во время настройки тестового устройства) внедрить в свой модуль данных набор макетов-наборы данных, которые вы хотите использовать, и просто замените наборы данных ADO, которые вы используете, во время выполнения теста, на какую-то другую функционально эквивалентную сущность (фиктивный набор данных или набор данных таблицы с файловой поддержкой).

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

Поскольку вы не писали набор данных ADO, вам не нужно его тестировать.Однако создание такого набора данных может быть затруднено.

Я бы посоветовал вам рассмотреть возможность использования JvCsvDataSet или ClientDataSet в качестве основы для ваших наборов данных фиксации (макет).Затем вы сможете использовать их, чтобы удостовериться, что все зависимости вашей платформы базы данных (вещи, которые пишут удаленные процедуры или SQL базы данных) абстрагированы в другие классы, которые вам снова придется макетировать.Такие усилия могут потребоваться не только для того, чтобы сделать ваш модуль бизнес-логики тестируемым, но также могут стать шагом к тому, чтобы стать дружественным к нескольким базам данных в вашей бизнес-логике.

представьте, что у вас есть ADOQuery под названием CustomerQuery, переименуйтеобъект, который вы перетащили в свой модуль данных, в CustomerQueryImpl и добавьте его в объявление класса вашего модуля данных:

  private
        FCustomerQuery:TADOQuery;

  published
        property CustomerQuery:TADOQuery read FCustomerQuery write FCustomerQuery;

, затем в своем модуле данных при создании события подключите свойство к объектам:

   FCustomerQuery := CustomerQueryImpl

Теперь вы можете написать модульные тесты, которые будут «зацеплять» и заменять CustomerQuery своим собственным тестовым приспособлением (фиктивным объектом) во время выполнения.

4 голосов
/ 12 февраля 2011

Во-первых, перед тем, как что-то изменить, вам нужно провести модульные тесты, чтобы вы могли убедиться, что ничего не сломали. Я бы попытался написать модульные тесты для текущего GUI, ничего не меняя. DUnit поддерживает GUI-тестирование (наряду с традиционным модульным тестированием), и хотя это немного неуклюже и не может обрабатывать модальные диалоги, оно функционально.

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

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

Вы можете использовать обычные объекты TObject, а не TDataModules для уровня обслуживания, если вам нравится, однако использование модулей данных дает вам возможность размещать на них невизуальные компоненты позже, например TClientDataSet и TDataSource, если вы отключились. маршрут с учетом данных контролирует позже.

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

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

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

0 голосов
/ 11 февраля 2011

Пожалуйста, прочитайте эту статью , о ней Unit-тестирование и фиктивные объекты , включая теорию фиктивных объектов, локализацию UT и обнаружение интерфейсов.

надеюсь, вам понравится.

...