Классы-монстры: создаем сами, а потом думаем куда девать
От: 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 функций в принципе все равно.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.