Положим, понадобилось мне в программе работать с реестром, причем только на чтение.
Я по-быстренькому накатываю небольшой классик, хэндл ключа реестра отдаю в руки RAII,
добавляю пару аккессоров и метод read. Все, готово.
Затем, уже в другом проекте, мне снова нужен доступ к реестру, но на этот раз не
только на чтение, но и на запись. Ок, достаем из архивов написанный класс, сдуваем с
него пыль и добавляем несколько вариаций write для разных типов данных.
Потом где-то позарез понадобится рекурсивное перечисление и удаление подключей.
Не проблема, немного расширяем интерфейс и добавляем несколько простых методов.
Список требований со временем пополняется — поддержка backup/restore, умение
определять и менять права доступа к определенным ключам, потокобезопасность, поиск
значений, продуманная система возврата ошибок/выброса исключений исключений и так далее.
А теперь — внимание. В какой-то момент снова возникает необходимость работать с
реестром, только в очень примитивном формате — получить REG_DWORD, изменить,
записать обратно. Как поступить, чтобы не тащить в проект неповоротливого
мамонта и при этом избежать написания кода заново ?
Как эта проблема решается или должна решаться архитектурно ?
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O>Как эта проблема решается или должна решаться архитектурно ?
Декомпозицией класса на более сфокусированные.
В мегаклассе, который был описан, смешаны несколько ролей: управление хэндлом и чтение/запись разных типов.
Возможно имеет смысл разбить его на несколько классов по принципу
1. Одна класс на управление хэндлом.
2. Один класс на чтение/запить конкретного типа.
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O>Положим, понадобилось мне в программе работать с реестром, причем только на чтение. O>Я по-быстренькому накатываю небольшой классик, хэндл ключа реестра отдаю в руки RAII
1. Смотрим готовые решения — например, Microsoft.Win32.RegistryKey из дотнета.
2. Разделяем сами структуры данных (грубо говоря, обёртку вокруг хэндла ключа реестра) и логику их обработки. Последнюю (частично) выносим в методы-хелперы, при необходимости выделяем примитивы в один слой и строим следующий слой абстракции поверх них.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, 0x7be, Вы писали:
0>Здравствуйте, okman, Вы писали:
O>>Как эта проблема решается или должна решаться архитектурно ? 0>Декомпозицией класса на более сфокусированные. 0>В мегаклассе, который был описан, смешаны несколько ролей: управление хэндлом и чтение/запись разных типов. 0>Возможно имеет смысл разбить его на несколько классов по принципу 0>1. Одна класс на управление хэндлом. 0>2. Один класс на чтение/запить конкретного типа.
Ответ понятен, но как предусмотреть потенциально возможную в будущем
декомпозицию еще на стадии зарождения класса, дабы потом не пришлось
переписывать его ? Ведь тогда, проектируя простой смарт-поинтер или
обертку над файловым API, нужно сразу закладывать фасады для будущих
интерфейсов, для многопоточности и прочего, причем не факт, что это
вообще понадобится ?..
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O>Ответ понятен, но как предусмотреть потенциально возможную в будущем O>декомпозицию еще на стадии зарождения класса, дабы потом не пришлось O>переписывать его ? Ведь тогда, проектируя простой смарт-поинтер или O>обертку над файловым API, нужно сразу закладывать фасады для будущих O>интерфейсов, для многопоточности и прочего, причем не факт, что это O>вообще понадобится ?..
Верно. Ответ на это: рефакторинг.
Re: Классы-монстры: создаем сами, а потом думаем куда девать
O>А теперь — внимание. В какой-то момент снова возникает необходимость работать с O>реестром, только в очень примитивном формате — получить REG_DWORD, изменить, O>записать обратно. Как поступить, чтобы не тащить в проект неповоротливого O>мамонта и при этом избежать написания кода заново ?
O>Как эта проблема решается или должна решаться архитектурно ?
Написать 5 строк (open, read, modify, write, close) без класса.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, AnonThisTime, Вы писали:
O>>Как эта проблема решается или должна решаться архитектурно ?
ATT>class RegistryReader {}, class RegistryWriter {}?
Я понимаю это как принцип разделения ответственности в самом резком его проявлении.
Выходит, что класс, который я описывал в первом сообщении, архитектурно порочен
изначально, и понятие reg_key слишком толсто для одного компонента, чтобы уместить в
нем все многочисленные детали и способы работы с реестром ?
Интересно, насколько распостранена практика программирования компонентов, которые до
такой степени мелкогранулярны (в том же Microsoft.Win32.RegistryKey более 40 методов,
кстати говоря)...
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, okman, Вы писали:
O>Выходит, что класс, который я описывал в первом сообщении, архитектурно порочен O>изначально.
Нет. Не надо расценивать стремление к абстрактной чистоте идеи как признак качественного дизайна (AnonThisTime, сорри ).
Дизайн в первую очердь должен быть практичным, т.е:
1. Удовлетворять текущим потребностям.
2. Не становиться причиной проблем при последующем изменении требований к коду.
Если мы проектируем класс исходя из формального разделения на операции чтения и записи — как нам быть с сценариями, использующими и то, и другое одновременно? Как быть с сценариями, что требуют получения значения, и с сценариями, предполагающими правку ACL? Что, если мы не хотим работать с ключом, как таковым — нам надо просто его удалить, или получить значение по такому-то пути?
В официальном руководстве по проектированию классов под .NET — Framework Design Guidelines — в самом начале приводится очень простое и правильное правило:
Framework Design Principle
Frameworks must be designed starting from a set of usage scenarios and code samples implementing these scenarios.
Здравствуйте, okman, Вы писали:
O>Выходит, что класс, который я описывал в первом сообщении, архитектурно порочен O>изначально, и понятие reg_key слишком толсто для одного компонента, чтобы уместить в O>нем все многочисленные детали и способы работы с реестром ?
Архитектура не бывает порочной или непорочной сама по себе.
Она обретает это свойство только в контексте конкретной задачи, конкретных требований.
O>Интересно, насколько распостранена практика программирования компонентов, которые до O>такой степени мелкогранулярны (в том же Microsoft.Win32.RegistryKey более 40 методов, O>кстати говоря)...
Мы, например, предпочитаем писать мелкогранулярные классы.
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O> Как поступить, чтобы не тащить в проект неповоротливого O>мамонта и при этом избежать написания кода заново ?
O>Как эта проблема решается или должна решаться архитектурно ?
Использованием чистых функций, т.е. статических функций работающих только со входом и выходом и не имеющих хранимого состояния. С этими функциями удобно работать и напрямую, но при необходимости можно и обернуть их в класс или набор классов за пять копеек. Соответственно гибкость по сравнению с реализациями прибитыми гвоздями к классам получается невероятная.
Re[4]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, -VaS-, Вы писали:
VS>Это руководство по созданию фреймворков, а не классов вообще.
А фреймворки не состоят из классов?
VS>Например, эти guidelines совершенно не подходят для проектирования бизнес-логики.
Ну так тема — не о бизнес-логике Впрочем, guidelines вполне подходят, если использовать их для наведения порядка в публичном/внутреннем API. Чтоб не спорить: я не теоретизирую
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O>Приветствую, коллеги !
O>А теперь — внимание. В какой-то момент снова возникает необходимость работать с O>реестром, только в очень примитивном формате — получить REG_DWORD, изменить, O>записать обратно. Как поступить, чтобы не тащить в проект неповоротливого O>мамонта и при этом избежать написания кода заново ?
O>Как эта проблема решается или должна решаться архитектурно ?
Решается вызовом трех функций "RegOpenKeyEx", "RegGetValue", "RegCloseKey"
Поверьте, пишется в 15000 раз быстрее (да и нагляднее) чем любые классы.обертки.монстры и т.д.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, butcha, Вы писали:
O>>Как эта проблема решается или должна решаться архитектурно ?
B>Решается вызовом трех функций "RegOpenKeyEx", "RegGetValue", "RegCloseKey" B>Поверьте, пишется в 15000 раз быстрее (да и нагляднее) чем любые классы.обертки.монстры и т.д.
Но класс "Key" ведь не свалился с неба? До этого, как я понял, кто то "по-быстренькому накатал небольшой классик" Да и пример написать можно было короче, как то так что ли:
S>Ну так тема — не о бизнес-логике Впрочем, guidelines вполне подходят, если использовать их для наведения порядка в публичном/внутреннем API. Чтоб не спорить: я не теоретизирую
Только для публичного API (реально публичного — для взаимодействия с другими командами, например). Что касается бизнес-логики: народ смотрит на сам фрейворк, читает эти гайдлайнс и по их подобию строит бизнес-логику. Получается реальный процедурный кошмар.
Re[7]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, -VaS-, Вы писали:
VS>Только для публичного API (реально публичного — для взаимодействия с другими командами, например). Что касается бизнес-логики: народ смотрит на сам фрейворк, читает эти гайдлайнс и по их подобию строит бизнес-логику. Получается реальный процедурный кошмар.
А можно небольшой пример?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O>Приветствую, коллеги !
O>Предлагаю пофилософствовать на следующую тему.
O>Положим, понадобилось мне в программе работать с реестром, причем только на чтение. O>Я по-быстренькому накатываю небольшой классик, хэндл ключа реестра отдаю в руки RAII, O>добавляю пару аккессоров и метод read. Все, готово. O>Затем, уже в другом проекте, мне снова нужен доступ к реестру, но на этот раз не O>только на чтение, но и на запись. Ок, достаем из архивов написанный класс, сдуваем с O>него пыль и добавляем несколько вариаций write для разных типов данных. O>Потом где-то позарез понадобится рекурсивное перечисление и удаление подключей. O>Не проблема, немного расширяем интерфейс и добавляем несколько простых методов. O>Список требований со временем пополняется — поддержка backup/restore, умение O>определять и менять права доступа к определенным ключам, потокобезопасность, поиск O>значений, продуманная система возврата ошибок/выброса исключений исключений и так далее.
O>А теперь — внимание. В какой-то момент снова возникает необходимость работать с O>реестром, только в очень примитивном формате — получить REG_DWORD, изменить, O>записать обратно. Как поступить, чтобы не тащить в проект неповоротливого O>мамонта и при этом избежать написания кода заново ?
O>Как эта проблема решается или должна решаться архитектурно ?
Я за компонентную архитектуру.
В приведенном примере я бы создал компонент для работы с реестром.
Нужен backup/restore — кверишь соответствующий интерфейс.
Нужна работа с параметрами — кверишь соответствующий интерфейс.
А что там внутри сидит, супер-мега-продуманный класс или просто вызовы API функций в принципе все равно.
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O>Как эта проблема решается или должна решаться архитектурно ?
Кажется, еще никто не упоминал про anemic-like подход, когда класс-обертка остается примитивным DTO, а всякая обработка реализуется в виде внешних классов-"алгоритмов". Тогда досыпаем себе алгоритмов, исходя из поребностей проекта, а класс-обертка остается неизменных во всех проектах.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, Mr.Delphist, Вы писали:
MD>Кажется, еще никто не упоминал про anemic-like подход, когда класс-обертка остается примитивным DTO, а всякая обработка реализуется в виде внешних классов-"алгоритмов".
это называется C-style ;=)
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O>Приветствую, коллеги !
O>Предлагаю пофилософствовать на следующую тему.
хорошая тема, попробую
хорошо, что есть конкретика и не надо слишком абстрактно растекаться по древу
среди врапперов реестра хотел бы выделить два типа:
обертка над системными функциями
абстракция от операционной системы (для портабельности или юнит-тестированности других модулей, к примеру)
первый тип обычно является тонкой прослойкой между клиентом и вызовами к операционной системе
если требуется функциональность, которая с системой не работает, то ее следует оторвать от класса (либо отдельной функцией делать, либо делать класс обертку с поддержкой толстой логики). при этом надо обеспечить доступ ко всем необходимым данным реестра снаружи.
то есть функция SendRegistryKeyOverEmail надо оторвать от класса, в котором есть метод ReadKey, ибо в WINAPI нет такой функции. если же в WINAPI есть такая функция, то ее можно и в обертке просверлить. зависимости не увеличатся
второй тип оберток обычно гораздо жирнее в плане логики. например, мы на линуксе делали реестр через xml файлы
чтобы интерфейс не пух, достаточно вытащить наружу основные данные. тогда много функций можно сделать снаружи. если в WINAPI есть функция для бекапа реестра, то чтобы ею воспользоваться из обертки первого типа можно вытащить наружу все хендлы, которые передаются в эту WINAPI функцию и сделать бекап снаружи. для второго типа оберток нет такой возможности, поэтому либо сверлить новый метод в этом интерфейсе, либо создавать другую сущность, которая отвечает за бекап реестре (она под собой может держать обертку первого типа). сумбурно, но общий посыл стандартен — не укрупнять классы слишком неординарными функциями. перекоса не должно быть, методы должны составлять целостную систему, преследующую единую цель
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, ononim, Вы писали:
O>>Как эта проблема решается или должна решаться архитектурно ? O>По моему банальное наследоваие
Причем лучше — от абстрактного класса...
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
O>>>Как эта проблема решается или должна решаться архитектурно ? O>>По моему банальное наследоваие LVV>Причем лучше — от абстрактного класса...
в данном случае необходимости нету, а я слишком ленив чтобы тайпать код без необходимости
Как много веселых ребят, и все делают велосипед...
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, okman, Вы писали:
O>как предусмотреть потенциально возможную в будущем O>декомпозицию еще на стадии зарождения класса, дабы потом не пришлось O>переписывать его ? Ведь тогда, проектируя простой смарт-поинтер или O>обертку над файловым API, нужно сразу закладывать фасады для будущих O>интерфейсов, для многопоточности и прочего, причем не факт, что это O>вообще понадобится ?..
Так думать надо. Иногда — много думать. Никакие формальные оценки не способны заменить мозг полностью.
Если думать нет времени (или желания), то можно потом отрефакторить (как уже написали). Но это не всегда безболезненно.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, 5er, Вы писали:
5er>Я за компонентную архитектуру. 5er>В приведенном примере я бы создал компонент для работы с реестром. 5er>Нужен backup/restore — кверишь соответствующий интерфейс. 5er>Нужна работа с параметрами — кверишь соответствующий интерфейс. 5er>А что там внутри сидит, супер-мега-продуманный класс или просто вызовы API функций в принципе все равно.
Поздравляю, вы только что изобрели COM
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, Mr.Delphist, Вы писали:
MD>>Кажется, еще никто не упоминал про anemic-like подход, когда класс-обертка остается примитивным DTO, а всякая обработка реализуется в виде внешних классов-"алгоритмов". U>это называется C-style ;=)
Не, это называется процедурное программирование. Только каждая процедура завёрнута в свой класс, чтобы это можно было называть модным словом
Re: Классы-монстры: создаем сами, а потом думаем куда девать
Здравствуйте, okman, Вы писали:
O>Как эта проблема решается или должна решаться архитектурно ?
Любая либа хороша, если она облегчает решение типичных задач . В данном случае, это прочитать значение, записать значение, удалить значение. А когда начинается тонкие операции с реестром, где возможностей либы не хватает, то не надо плодить монстра, который бы умел все. Ибо в конечном счете мы получим весь набор функций API, только в профиль. С той проблемой, что функции API лучше документированы и принципы их работы знает не один человек. Уместнее посчитать, что данный код специфичен для данного приложения и написать его на голом API. И назвать его в терминах самого проекта (не просто там RegOpenKeyWithAccessMask, а например CreateUserSecurityKey). Конечно, если потом увидишь, что фрагмент кода дублируется, то ничто не помешает выделить еще одну функцию.