Классы-монстры: создаем сами, а потом думаем куда девать
От: okman Беларусь https://searchinform.ru/
Дата: 27.06.11 14:07
Оценка: 3 (1)
Приветствую, коллеги !

Предлагаю пофилософствовать на следующую тему.

Положим, понадобилось мне в программе работать с реестром, причем только на чтение.
Я по-быстренькому накатываю небольшой классик, хэндл ключа реестра отдаю в руки RAII,
добавляю пару аккессоров и метод read. Все, готово.
Затем, уже в другом проекте, мне снова нужен доступ к реестру, но на этот раз не
только на чтение, но и на запись. Ок, достаем из архивов написанный класс, сдуваем с
него пыль и добавляем несколько вариаций write для разных типов данных.
Потом где-то позарез понадобится рекурсивное перечисление и удаление подключей.
Не проблема, немного расширяем интерфейс и добавляем несколько простых методов.
Список требований со временем пополняется — поддержка backup/restore, умение
определять и менять права доступа к определенным ключам, потокобезопасность, поиск
значений, продуманная система возврата ошибок/выброса исключений исключений и так далее.

А теперь — внимание. В какой-то момент снова возникает необходимость работать с
реестром, только в очень примитивном формате — получить REG_DWORD, изменить,
записать обратно. Как поступить, чтобы не тащить в проект неповоротливого
мамонта и при этом избежать написания кода заново ?

Как эта проблема решается или должна решаться архитектурно ?
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: 0x7be СССР  
Дата: 27.06.11 14:12
Оценка: 1 (1) +1
Здравствуйте, okman, Вы писали:

O>Как эта проблема решается или должна решаться архитектурно ?

Декомпозицией класса на более сфокусированные.
В мегаклассе, который был описан, смешаны несколько ролей: управление хэндлом и чтение/запись разных типов.
Возможно имеет смысл разбить его на несколько классов по принципу
1. Одна класс на управление хэндлом.
2. Один класс на чтение/запить конкретного типа.
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: Sinix  
Дата: 27.06.11 14:18
Оценка: +1
Здравствуйте, okman, Вы писали:

O>Положим, понадобилось мне в программе работать с реестром, причем только на чтение.

O>Я по-быстренькому накатываю небольшой классик, хэндл ключа реестра отдаю в руки RAII
1. Смотрим готовые решения — например, Microsoft.Win32.RegistryKey из дотнета.
2. Разделяем сами структуры данных (грубо говоря, обёртку вокруг хэндла ключа реестра) и логику их обработки. Последнюю (частично) выносим в методы-хелперы, при необходимости выделяем примитивы в один слой и строим следующий слой абстракции поверх них.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
От: okman Беларусь https://searchinform.ru/
Дата: 27.06.11 14:24
Оценка:
Здравствуйте, 0x7be, Вы писали:

0>Здравствуйте, okman, Вы писали:


O>>Как эта проблема решается или должна решаться архитектурно ?

0>Декомпозицией класса на более сфокусированные.
0>В мегаклассе, который был описан, смешаны несколько ролей: управление хэндлом и чтение/запись разных типов.
0>Возможно имеет смысл разбить его на несколько классов по принципу
0>1. Одна класс на управление хэндлом.
0>2. Один класс на чтение/запить конкретного типа.

Ответ понятен, но как предусмотреть потенциально возможную в будущем
декомпозицию еще на стадии зарождения класса, дабы потом не пришлось
переписывать его ? Ведь тогда, проектируя простой смарт-поинтер или
обертку над файловым API, нужно сразу закладывать фасады для будущих
интерфейсов, для многопоточности и прочего, причем не факт, что это
вообще понадобится ?..
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: AnonThisTime  
Дата: 27.06.11 14:25
Оценка:
O>Как эта проблема решается или должна решаться архитектурно ?

class RegistryReader {}, class RegistryWriter {}? или юзаем проприетарные, что всегда лучше.
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
От: 0x7be СССР  
Дата: 27.06.11 14:26
Оценка:
Здравствуйте, okman, Вы писали:

O>Ответ понятен, но как предусмотреть потенциально возможную в будущем

O>декомпозицию еще на стадии зарождения класса, дабы потом не пришлось
O>переписывать его ? Ведь тогда, проектируя простой смарт-поинтер или
O>обертку над файловым API, нужно сразу закладывать фасады для будущих
O>интерфейсов, для многопоточности и прочего, причем не факт, что это
O>вообще понадобится ?..
Верно. Ответ на это: рефакторинг.
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: Temoto  
Дата: 27.06.11 14:34
Оценка: 6 (1) +1
O>А теперь — внимание. В какой-то момент снова возникает необходимость работать с
O>реестром, только в очень примитивном формате — получить REG_DWORD, изменить,
O>записать обратно. Как поступить, чтобы не тащить в проект неповоротливого
O>мамонта и при этом избежать написания кода заново ?

O>Как эта проблема решается или должна решаться архитектурно ?


Написать 5 строк (open, read, modify, write, close) без класса.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
От: okman Беларусь https://searchinform.ru/
Дата: 27.06.11 14:46
Оценка:
Здравствуйте, AnonThisTime, Вы писали:

O>>Как эта проблема решается или должна решаться архитектурно ?


ATT>class RegistryReader {}, class RegistryWriter {}?


Я понимаю это как принцип разделения ответственности в самом резком его проявлении.

Выходит, что класс, который я описывал в первом сообщении, архитектурно порочен
изначально, и понятие reg_key слишком толсто для одного компонента, чтобы уместить в
нем все многочисленные детали и способы работы с реестром ?

Интересно, насколько распостранена практика программирования компонентов, которые до
такой степени мелкогранулярны (в том же Microsoft.Win32.RegistryKey более 40 методов,
кстати говоря)...
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
От: Sinix  
Дата: 27.06.11 15:33
Оценка:
Здравствуйте, 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.


Если ваш фреймворк выглядит сложнее, чем
RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\Microsoft\Windows\CurrentVersion\Run");
key.AddValue("notepad.exe", "%windir%\notepad.exe");

— может, стоит переписать?
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
От: 0x7be СССР  
Дата: 27.06.11 16:11
Оценка: 1 (1) +1
Здравствуйте, okman, Вы писали:

O>Выходит, что класс, который я описывал в первом сообщении, архитектурно порочен

O>изначально, и понятие reg_key слишком толсто для одного компонента, чтобы уместить в
O>нем все многочисленные детали и способы работы с реестром ?
Архитектура не бывает порочной или непорочной сама по себе.
Она обретает это свойство только в контексте конкретной задачи, конкретных требований.

O>Интересно, насколько распостранена практика программирования компонентов, которые до

O>такой степени мелкогранулярны (в том же Microsoft.Win32.RegistryKey более 40 методов,
O>кстати говоря)...
Мы, например, предпочитаем писать мелкогранулярные классы.
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: Undying Россия  
Дата: 27.06.11 16:46
Оценка: +1
Здравствуйте, okman, Вы писали:

O> Как поступить, чтобы не тащить в проект неповоротливого

O>мамонта и при этом избежать написания кода заново ?

O>Как эта проблема решается или должна решаться архитектурно ?


Использованием чистых функций, т.е. статических функций работающих только со входом и выходом и не имеющих хранимого состояния. С этими функциями удобно работать и напрямую, но при необходимости можно и обернуть их в класс или набор классов за пять копеек. Соответственно гибкость по сравнению с реализациями прибитыми гвоздями к классам получается невероятная.
Re[4]: Классы-монстры: создаем сами, а потом думаем куда дев
От: -VaS- Россия vaskir.blogspot.com
Дата: 27.06.11 16:52
Оценка: 1 (1)
S>В официальном руководстве по проектированию классов под .NET — Framework Design Guidelines [skipped]

Это руководство по созданию фреймворков, а не классов вообще. Например, эти guidelines совершенно не подходят для проектирования бизнес-логики.
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: ononim  
Дата: 27.06.11 20:00
Оценка:
O>Как эта проблема решается или должна решаться архитектурно ?
По моему банальное наследоваие
class Key
{
public:
Key(HKEY root, LPCTSTR path, DWORD desired_access = MAXIMUM_ALLOWED);
~Key();
operator HKEY();
};

class KeyReader : protected Key
{
public:
KeyReader(HKEY root, LPCTSTR path, DWORD desired_access = GENERIC_READ);
bool ReadValue(LPTSTR name, DWORD &value);
};

class KeyReadWriter : protected KeyReader
{
public:
KeyReadWriter(HKEY root, LPCTSTR path, DWORD desired_access = GENERIC_READ|GENERIC_WRITE);
bool WriteValue(LPTSTR name, DWORD value);
bool DeleteValue(LPTSTR name);
};
Как много веселых ребят, и все делают велосипед...
Re[5]: Классы-монстры: создаем сами, а потом думаем куда дев
От: Sinix  
Дата: 28.06.11 00:00
Оценка:
Здравствуйте, -VaS-, Вы писали:

VS>Это руководство по созданию фреймворков, а не классов вообще.

А фреймворки не состоят из классов?

VS>Например, эти guidelines совершенно не подходят для проектирования бизнес-логики.

Ну так тема — не о бизнес-логике Впрочем, guidelines вполне подходят, если использовать их для наведения порядка в публичном/внутреннем API. Чтоб не спорить: я не теоретизирую
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: butcha Россия  
Дата: 28.06.11 01:40
Оценка: 1 (1) +2 -2
Здравствуйте, okman, Вы писали:

O>Приветствую, коллеги !


O>А теперь — внимание. В какой-то момент снова возникает необходимость работать с

O>реестром, только в очень примитивном формате — получить REG_DWORD, изменить,
O>записать обратно. Как поступить, чтобы не тащить в проект неповоротливого
O>мамонта и при этом избежать написания кода заново ?

O>Как эта проблема решается или должна решаться архитектурно ?


Решается вызовом трех функций "RegOpenKeyEx", "RegGetValue", "RegCloseKey"
Поверьте, пишется в 15000 раз быстрее (да и нагляднее) чем любые классы.обертки.монстры и т.д.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
От: okman Беларусь https://searchinform.ru/
Дата: 28.06.11 07:30
Оценка:
Здравствуйте, butcha, Вы писали:

O>>Как эта проблема решается или должна решаться архитектурно ?


B>Решается вызовом трех функций "RegOpenKeyEx", "RegGetValue", "RegCloseKey"

B>Поверьте, пишется в 15000 раз быстрее (да и нагляднее) чем любые классы.обертки.монстры и т.д.

Не поверю.

DWORD Version;

try
{
    reg_key Key(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MyProgram");
    Version = Key.read(L"Version");
}

catch (std::exception &Exc)
{
    ...
}

смотрится гораздо лучше, чем

BOOL
GetVersion(
    __inout DWORD      * pVersion
    )
HKEY hKey;

if (ERROR_SUCCESS != RegOpenKeyExW(HKEY_LOCAL_MACHINE,
    L"SOFTWARE\\MyProgram",
    NULL,
    KEY_READ,
    &hKey))
{
    return FALSE;
}

DWORD cbVersion(sizeof (DWORD));
DWORD fType;

if (ERROR_SUCCESS != RegQueryValueExW(hKey,
    L"Version",
    NULL,
    &fType,
    pVersion,
    &cbVersion))
{
    RegCloseKey(hKey);
    return FALSE;
}

RegCloseKey(hKey);

if (REG_DWORD != fType)
{
    return FALSE;
}

return TRUE
}
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
От: butcha Россия  
Дата: 28.06.11 11:16
Оценка:
Здравствуйте, okman, Вы писали:

O>Не поверю.


O>
O>DWORD Version;

O>try
O>{
O>    reg_key Key(HKEY_LOCAL_MACHINE, L"SOFTWARE\\MyProgram");
O>    Version = Key.read(L"Version");
O>}

O>


Но класс "Key" ведь не свалился с неба? До этого, как я понял, кто то "по-быстренькому накатал небольшой классик" Да и пример написать можно было короче, как то так что ли:

BOOL GetVersion(__inout DWORD      * pVersion)
{
    HKEY hKey; HRESULT hr; 
    DWORD fType, cbVersion = sizeof (DWORD);

    if (S_OK == (hr = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
                                L"SOFTWARE\\MyProgram",
                                NULL,
                                KEY_READ,
                                &hKey)))
    {
        hr = RegQueryValueExW(hKey,
                                        L"Version",
                                        NULL,
                                        &fType,
                                        (LPBYTE)pVersion,
                                        &cbVersion);
        if (REG_DWORD != fType) hr = E_FAIL;
        RegCloseKey(hKey);
    }
    return S_OK == hr?TRUE:FALSE;
}
Re[6]: Классы-монстры: создаем сами, а потом думаем куда дев
От: -VaS- Россия vaskir.blogspot.com
Дата: 29.06.11 04:05
Оценка:
S>Ну так тема — не о бизнес-логике Впрочем, guidelines вполне подходят, если использовать их для наведения порядка в публичном/внутреннем API. Чтоб не спорить: я не теоретизирую

Только для публичного API (реально публичного — для взаимодействия с другими командами, например). Что касается бизнес-логики: народ смотрит на сам фрейворк, читает эти гайдлайнс и по их подобию строит бизнес-логику. Получается реальный процедурный кошмар.
Re[7]: Классы-монстры: создаем сами, а потом думаем куда дев
От: AndrewJD США  
Дата: 29.06.11 07:16
Оценка:
Здравствуйте, -VaS-, Вы писали:

VS>Только для публичного API (реально публичного — для взаимодействия с другими командами, например). Что касается бизнес-логики: народ смотрит на сам фрейворк, читает эти гайдлайнс и по их подобию строит бизнес-логику. Получается реальный процедурный кошмар.

А можно небольшой пример?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: 5er Россия  
Дата: 01.07.11 08:38
Оценка: :)
Здравствуйте, 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: Классы-монстры: создаем сами, а потом думаем куда девать
От: Mr.Delphist  
Дата: 15.07.11 14:54
Оценка: +2
Здравствуйте, okman, Вы писали:

O>Как эта проблема решается или должна решаться архитектурно ?


Кажется, еще никто не упоминал про anemic-like подход, когда класс-обертка остается примитивным DTO, а всякая обработка реализуется в виде внешних классов-"алгоритмов". Тогда досыпаем себе алгоритмов, исходя из поребностей проекта, а класс-обертка остается неизменных во всех проектах.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
От: uzhas Ниоткуда  
Дата: 15.07.11 17:30
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Кажется, еще никто не упоминал про anemic-like подход, когда класс-обертка остается примитивным DTO, а всякая обработка реализуется в виде внешних классов-"алгоритмов".

это называется C-style ;=)
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: uzhas Ниоткуда  
Дата: 15.07.11 17:48
Оценка:
Здравствуйте, okman, Вы писали:

O>Приветствую, коллеги !


O>Предлагаю пофилософствовать на следующую тему.


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


первый тип обычно является тонкой прослойкой между клиентом и вызовами к операционной системе
если требуется функциональность, которая с системой не работает, то ее следует оторвать от класса (либо отдельной функцией делать, либо делать класс обертку с поддержкой толстой логики). при этом надо обеспечить доступ ко всем необходимым данным реестра снаружи.
то есть функция SendRegistryKeyOverEmail надо оторвать от класса, в котором есть метод ReadKey, ибо в WINAPI нет такой функции. если же в WINAPI есть такая функция, то ее можно и в обертке просверлить. зависимости не увеличатся
Registry
{
  Key ReadKey(..)
}

RegistryInfoNotifier
{
  SendRegistryKeyOverEmail(Key, email)
}


второй тип оберток обычно гораздо жирнее в плане логики. например, мы на линуксе делали реестр через xml файлы
чтобы интерфейс не пух, достаточно вытащить наружу основные данные. тогда много функций можно сделать снаружи. если в WINAPI есть функция для бекапа реестра, то чтобы ею воспользоваться из обертки первого типа можно вытащить наружу все хендлы, которые передаются в эту WINAPI функцию и сделать бекап снаружи. для второго типа оберток нет такой возможности, поэтому либо сверлить новый метод в этом интерфейсе, либо создавать другую сущность, которая отвечает за бекап реестре (она под собой может держать обертку первого типа). сумбурно, но общий посыл стандартен — не укрупнять классы слишком неординарными функциями. перекоса не должно быть, методы должны составлять целостную систему, преследующую единую цель
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
От: LaptevVV Россия  
Дата: 15.07.11 18:08
Оценка:
Здравствуйте, ononim, Вы писали:

O>>Как эта проблема решается или должна решаться архитектурно ?

O>По моему банальное наследоваие
Причем лучше — от абстрактного класса...
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
От: ononim  
Дата: 15.07.11 19:23
Оценка:
O>>>Как эта проблема решается или должна решаться архитектурно ?
O>>По моему банальное наследоваие
LVV>Причем лучше — от абстрактного класса...
в данном случае необходимости нету, а я слишком ленив чтобы тайпать код без необходимости
Как много веселых ребят, и все делают велосипед...
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
От: vmpire Россия  
Дата: 22.07.11 13:08
Оценка:
Здравствуйте, okman, Вы писали:

O>как предусмотреть потенциально возможную в будущем

O>декомпозицию еще на стадии зарождения класса, дабы потом не пришлось
O>переписывать его ? Ведь тогда, проектируя простой смарт-поинтер или
O>обертку над файловым API, нужно сразу закладывать фасады для будущих
O>интерфейсов, для многопоточности и прочего, причем не факт, что это
O>вообще понадобится ?..
Так думать надо. Иногда — много думать. Никакие формальные оценки не способны заменить мозг полностью.
Если думать нет времени (или желания), то можно потом отрефакторить (как уже написали). Но это не всегда безболезненно.
Re[2]: Классы-монстры: создаем сами, а потом думаем куда дев
От: vmpire Россия  
Дата: 22.07.11 13:12
Оценка:
Здравствуйте, 5er, Вы писали:

5er>Я за компонентную архитектуру.

5er>В приведенном примере я бы создал компонент для работы с реестром.
5er>Нужен backup/restore — кверишь соответствующий интерфейс.
5er>Нужна работа с параметрами — кверишь соответствующий интерфейс.
5er>А что там внутри сидит, супер-мега-продуманный класс или просто вызовы API функций в принципе все равно.
Поздравляю, вы только что изобрели COM
Re[3]: Классы-монстры: создаем сами, а потом думаем куда дев
От: vmpire Россия  
Дата: 22.07.11 13:13
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, Mr.Delphist, Вы писали:


MD>>Кажется, еще никто не упоминал про anemic-like подход, когда класс-обертка остается примитивным DTO, а всякая обработка реализуется в виде внешних классов-"алгоритмов".

U>это называется C-style ;=)
Не, это называется процедурное программирование. Только каждая процедура завёрнута в свой класс, чтобы это можно было называть модным словом
Re: Классы-монстры: создаем сами, а потом думаем куда девать
От: Mystic Украина http://mystic2000.newmail.ru
Дата: 22.07.11 13:38
Оценка:
Здравствуйте, okman, Вы писали:

O>Как эта проблема решается или должна решаться архитектурно ?


Любая либа хороша, если она облегчает решение типичных задач . В данном случае, это прочитать значение, записать значение, удалить значение. А когда начинается тонкие операции с реестром, где возможностей либы не хватает, то не надо плодить монстра, который бы умел все. Ибо в конечном счете мы получим весь набор функций API, только в профиль. С той проблемой, что функции API лучше документированы и принципы их работы знает не один человек. Уместнее посчитать, что данный код специфичен для данного приложения и написать его на голом API. И назвать его в терминах самого проекта (не просто там RegOpenKeyWithAccessMask, а например CreateUserSecurityKey). Конечно, если потом увидишь, что фрагмент кода дублируется, то ничто не помешает выделить еще одну функцию.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.