В один прекрасный обычный четверг в одной команде разработчиков возникли разногласия относительно некоторых архитектурных решений, реализация которых была одобрена приказом сверху, а не родилась в ходе аргументированного спора.
Через некоторое время аналогичный спор возник снова, уже по новому проекту.
Дискуссия становилась все более жаркой, и для прояснения ситуации и достижения просветления даже привлекались внешние высшие силы.
Последнее не было достигнуто, и все же тем же волевым решением был принят один из вариантов развития существования.
Но мудрый Каа, один из участников дискуссии, решил не оставлять нерешенных споров в коллективе - поводов для конфликтов в будущем, раскола коллектива и прочих драк, и предложил решить все общим пьянством путем составления протокола.
этот документ, который поможет членам команды достичь просветления и снова стать мягкими и пушистыми.
В этом документе меня попросили описать преимущества и недостатки двух подходов, достичь консенсуса и принести мир и справедливость во Вселенную.
Ниже я постараюсь сделать это в меру своих интеллектуальных возможностей (поэтому буду использовать очень простые слова и выражения) и довести их до сведения кровавых палачей добропорядочной публики.
Волевой путь, ведущий в бездну достижения нирваны У нас есть набор сервисов, которые обрабатывают данные и используют репозитории для хранения данных.
Поскольку репозитории часто являются медленными источниками, некоторые сервисы используют временное хранилище, такое как сеанс и кеш.
А т.к.
сервисы используются не только из веб-приложений, то эти временные хранилища нельзя использовать напрямую (например, у консольного приложения вообще нет сессии), поэтому сервисы реализуют и работу с ними.
(То есть в нашей архитектуре нет четкого понятия, что такое сервис и не указано его высшее предназначение.
Фактически при таком подходе сервис — это класс, который что-то делает)
Рис.
1. Иерархия классов первого подхода Пример реализации всего этого счастья можно скачать по ссылке Git-хаб Что мне здесь не нравится: Во-первых, мне не нравится отсутствие четких договоренностей по делегированию полномочий: это оба сервиса, но они делают совершенно разные вещи.
Один занимается обработкой данных, другой — временным хранением.
Во-вторых, мне не нравится класс SessionDataServiceBase, а именно метод T Get(string key, Func getData), который не только работает с сессией, но и обрабатывает данные.
По названию ISessionDataService (и, по сути, по тому, как оно было изначально задумано) предполагается, что это всего лишь оболочка над сеансом, и максимум, что он может сделать, — это вернуть значение по умолчанию.
Если посмотреть историю развития этого объекта, то изначально в интерфейсе не был описан метод T Get(string key, Func getData) (или его аналог).
Он был реализован в каждом классе, который использовал реализацию этого интерфейса (и с одной стороны, на мой взгляд, это правильно).
У этого подхода был минус — это повторяемость кода.
Я это заметил, когда решил добавить использование сессии в один из сервисов.
После непродолжительного расследования было обнаружено большое количество дублирующего кода, точнее это дублирование было во ВСЕХ сервисах.
Это нарушило мою целостность восприятия Вселенной и не позволило мне жить в гармонии с ней.
Мне показалось неправильным делегировать сессионному сервису какую-то дополнительную работу с данными, и было решено перенести это в какой-нибудь базовый класс, чтобы убрать этот диссонанс из души (подробности в описании второго пути к истине) .
Третья проблема, которую открыл мне Господь Бог, Его Святейшество в двоичном коде – это ключи.
Им были присвоены значения (не боюсь признаться, у меня был в свое время грех) в стиле «кто-то в лес идет, кто-то за дровами».
А метод void Clear(string[] sessionKeyPrefix) иногда давал совершенно неожиданные результаты.
Были и другие проблемы, но мы не будем их касаться; решение этих проблем обеспечивало и грамотное наследование.
Четвертая проблема — тестирование.
После перехода волевым решением в эту структуру все юнит-тесты методов, использующих T Get(string key, Func getData) под капотом провалились.
Быстро их промокнуть мне не удалось, даже с помощью нашего гуру модульного тестирования, и меня попросили их вообще игнорировать, что, на мой взгляд, нехорошо.
А теперь о преимуществах - о них мы не можем ничего сказать, то есть в моем смысле, потому что я за второй путь, полный благочестия и совершенства, ведущий к самой совершенной нирване, в которую код преобразуется непосредственно в двоичные коды, минуя унизительный и скучный процесс конвертации в CIL. Путь, осмеянный современниками, как и все поистине великие дела.
Этот подход основан прежде всего на условностях и ограничениях.
Предполагается, что сервис представляет собой класс, который обрабатывает полученные данные и сохраняет результаты этой обработки в постоянном хранилище, с которым работает через репозиторий, причем репозиторий у него только один, и сервис ничего не знает о том, как он хранит данные.
данные.
Ему все равно, база данных это, файл, сторонний сервис в Интернете и т. д. По-хорошему было бы правильно сделать какой-то базовый класс или интерфейс, который бы показывал, что этот класс отвечает за обработку и отправку данных между хранилищем и конечным потребителем.
Но, к сожалению, этого не было сделано, и, как мы видим в первом подходе, у нас есть класс (HttpContextBasedSessionDataService), называемый сервисом, но без репозитория и не обрабатывающий данные.
Поэтому будем считать, что базовая сущность для сервисов у нас всё-таки есть, это будет интерфейс IService. Теперь о HttpContextBasedSessionDataService и подобных ему классах.
Оно появилось потому, что репозитории — это медленные источники данных, во-первых, и их следует использовать как можно реже, так как это всегда узкое место, во-вторых.
Поэтому было бы неплохо держать некоторые данные под рукой — и тут появляется новый тип классов, они не обрабатывают данные, не имеют репозитория, они просто предоставляют доступ к временному хранилищу.
В принципе это ближе к репозиториям, чем к сервисам, только репозитории временного хранения, такие как Application, Cache, Session. Назовем базовую сущность IShortermStore и примем, что имена таких классов не будут содержать слова Service. Теперь попробуем на основе этих расчетов построить иерархию классов, которая устранит недостатки предыдущей реализации, и перенесем часть функционала по логике работы с временным хранилищем в базовый сервисный класс, а именно работу с ключи и логика ленивой инициализации.
А теперь давайте взглянем на диаграмму классов, построенную по этой концепции:
Рис.
2 Иерархия классов второго подхода Пример реализации всего этого счастья можно скачать по ссылке Git-хаб Это устраняет недостатки, которыми, на мой взгляд, страдает первый подход. Модульные тесты работают без каких-либо дополнительных макетов.
Теперь о мнимых недостатках.
Если вспомнить доводы спорщиков, то тут есть недостаток в том, что если класс помимо Session хочет использовать еще и Cache, то возникают проблемы.
Но на самом деле с этим проблем нет: если возникнет такая необходимость, то, возможно, стоит подумать о SOLID, в частности о букве I в этой аббревиатуре (Принцип разделения интерфейсов) а не делать монстров, которые умеют «вышивать и пользоваться пишущей машинкой».
О недостатках пока больше ничего сказать не могу, так как память избирательна и запоминает только светлое и хорошее, а не критику вообще.
Пожалуйста, начните разбивать меня вдребезги, а то все очень хорошо получается.
Алексей Михайличенко, разработчик программного обеспечения .
Net, технический руководитель Теги: #архитектура приложения #принципы проектирования #дизайн интерфейсов #дизайн взаимодействия #интерфейсы #разработка веб-сайтов #программирование #C++
-
Отключение Функции «Последние Документы Xp»
19 Oct, 24 -
Проект Авокадо
19 Oct, 24 -
Тесный Контакт: Что Может Ваш Смартфон С Nfc
19 Oct, 24 -
Интерполяция Данных: Красиво Соединяем Точки
19 Oct, 24 -
Asterisk 12 Выйдет В 2013 Году.
19 Oct, 24