Многоглаголание ООП
Dec. 21st, 2008 01:26 pmЧем больше пишу с использованием ООП, тем больше бесит одна проблема - нарастание количества классов и интерфейсов и уменьшение их размера. В экстремальных случаях интерфейсы и классы вообще служат исключительно маркерами или атрибутами или исключениями и ничего интеллектуального в себе не делают.
Вот пример: есть система распознавания речи, я к ней прикручиваю звукозапись и поле ввода текста с целью прямой диктовки документов в это поле.
Если не извращаться с мегапаттернами - делается один класс, создающий контрол для звукозаписи, контрол для ввода текста, грузит класс для управления с HID-микрофона с кнопками, реализует связь с системой распознавания, реализует всю дополнительную логику и все.
Но мы по ходу люди умудренные опытом, поэтому сразу приходится делать так: во-первых, система распознавания речи сумасшедшая и есть вероятность, что придется ее когда-нибудь отломать и прикрутить другую, поэтому весь ее API заворачивается во wrapper. Все обращения к нему ведутся через интерфейс типа ISpeechRecognizerWrapper. Для упрощения разработки(система распознавания тяжеловесная и каждый раз ее задолбаться запускать) делается mock-wrapper, реализующий тот же интерфейс. Итого +1 интефейс и два класса, реализующих его.
Поле для ввода текста тоже не так просто. Есть стандартный TextBox, работающий корректно с вводимым текстом. Есть наш собственный контрол - полнофункциональный RTF текстовый редактор, который не совсем адекватным образом взаимодействует с текстом переданным с распознавалки речи из-за разного понимания того, что такое знак абзаца. Так же есть стандартный RichTextBox, который может понадобится использовать. Поэтому вместо прямого обращения к методам и событиям текстового контрола - делается интерфейс ITextControl и классы-врапперы реализующие его для каждого из видов текстового контрола. +1 интерфейс +3 класса.
Контрол звукозаписи и сам по себе уже представляет собой базовое ядро с интерфейсом ISoundEngine и две вариации GUI для него. +1 интерфейс, +3 класса.
Управление с HID-микрофона - микрофонов много и разных, каждый представляется своим классом, тоже реализующим стандартный интерфейс. Кроме того, управление с микрофона должно в разных режимах работы направляться в разные части программы, поэтому все события с микрофонов в итоге попадают в менеджер микрофонов, и вызывают метод специального интерфейса IDeviceCommandSink, который этому менеджеру указывается извне теми, кто хочет получать события от микрофонов. Кроме всего этого, на микрофоне есть светодиоды сигнализации режима работы, для управления которыми нужен обратный поток данных от системы, через менеджер микрофонов, в класс-драйвер микрофона. +хрен знает сколько классов и минимум два интерфейса.
Если при этом хотя бы частично следовать идее "один класс-один файл", проекты превращаются в кучу мелких файлов, что совершенно не улучшает время компиляции. Еще хуже, если выделить общие для нескольких проектов части в отдельные проекты и на них ссылаться - компиляция начинает еще больше тупить.
Пониманию логики работы такая структура - множество мелких классов тоже не очень способствует, по моему, потому что реальный рабочий код плохо виден за лесом из служебного кода инициализации-деинициализации всех этих врапперов, интерфейсов и прочего.
Т.е. планомерное следование заветам ООП и паттернам приводит к тому, что проект разрастается, приобретая "гибкость" и возможность независимого тестирования частей, но при этом ухудшается удобство работы с ним (это еще Visual Studio мешает, там без мыши между кучей файлов переключаться практически нереально) и становится сложно удерживать в голове структуру всего этого).
Вот пример: есть система распознавания речи, я к ней прикручиваю звукозапись и поле ввода текста с целью прямой диктовки документов в это поле.
Если не извращаться с мегапаттернами - делается один класс, создающий контрол для звукозаписи, контрол для ввода текста, грузит класс для управления с HID-микрофона с кнопками, реализует связь с системой распознавания, реализует всю дополнительную логику и все.
Но мы по ходу люди умудренные опытом, поэтому сразу приходится делать так: во-первых, система распознавания речи сумасшедшая и есть вероятность, что придется ее когда-нибудь отломать и прикрутить другую, поэтому весь ее API заворачивается во wrapper. Все обращения к нему ведутся через интерфейс типа ISpeechRecognizerWrapper. Для упрощения разработки(система распознавания тяжеловесная и каждый раз ее задолбаться запускать) делается mock-wrapper, реализующий тот же интерфейс. Итого +1 интефейс и два класса, реализующих его.
Поле для ввода текста тоже не так просто. Есть стандартный TextBox, работающий корректно с вводимым текстом. Есть наш собственный контрол - полнофункциональный RTF текстовый редактор, который не совсем адекватным образом взаимодействует с текстом переданным с распознавалки речи из-за разного понимания того, что такое знак абзаца. Так же есть стандартный RichTextBox, который может понадобится использовать. Поэтому вместо прямого обращения к методам и событиям текстового контрола - делается интерфейс ITextControl и классы-врапперы реализующие его для каждого из видов текстового контрола. +1 интерфейс +3 класса.
Контрол звукозаписи и сам по себе уже представляет собой базовое ядро с интерфейсом ISoundEngine и две вариации GUI для него. +1 интерфейс, +3 класса.
Управление с HID-микрофона - микрофонов много и разных, каждый представляется своим классом, тоже реализующим стандартный интерфейс. Кроме того, управление с микрофона должно в разных режимах работы направляться в разные части программы, поэтому все события с микрофонов в итоге попадают в менеджер микрофонов, и вызывают метод специального интерфейса IDeviceCommandSink, который этому менеджеру указывается извне теми, кто хочет получать события от микрофонов. Кроме всего этого, на микрофоне есть светодиоды сигнализации режима работы, для управления которыми нужен обратный поток данных от системы, через менеджер микрофонов, в класс-драйвер микрофона. +хрен знает сколько классов и минимум два интерфейса.
Если при этом хотя бы частично следовать идее "один класс-один файл", проекты превращаются в кучу мелких файлов, что совершенно не улучшает время компиляции. Еще хуже, если выделить общие для нескольких проектов части в отдельные проекты и на них ссылаться - компиляция начинает еще больше тупить.
Пониманию логики работы такая структура - множество мелких классов тоже не очень способствует, по моему, потому что реальный рабочий код плохо виден за лесом из служебного кода инициализации-деинициализации всех этих врапперов, интерфейсов и прочего.
Т.е. планомерное следование заветам ООП и паттернам приводит к тому, что проект разрастается, приобретая "гибкость" и возможность независимого тестирования частей, но при этом ухудшается удобство работы с ним (это еще Visual Studio мешает, там без мыши между кучей файлов переключаться практически нереально) и становится сложно удерживать в голове структуру всего этого).