Спасибо за вопросы :) 1. Я отрицаю проектирование "снизу-вверх", оскольку ересь есть.
И начинаю не с "классов, реализующих унифицированную работу", а с паттерна проектирования. В моем случае - "стратегия", в MVC модели приложения.
Конечно я не видел написанного вами. Примем за аксиому что у вас все пучком, и прям идеально. Мы обсуждаем сугубо академический вопрос, попивая чай у камина :)
Так вот, при проектировании "сверху-вниз" на роль "view" найдется сразу несколько кандидатов.
Это и передача на верхний уровень (целый список протоколов),
и пульт оператора, и диагностическое подключение ноутбуком.
"Controller" - более-менее очевиден.
И тут встает вопрос об инкапсуляции. Если мы договорились что во "view" нужен уровень диагностики, а не только кнопки пуск-стоп, то о какой изоляции данных идет речь?
Мы начинаем что-то от самих себя прятать и тут же героически создавать лазейки, как это назад достать? Дурная работа дважды?
Упомянутое коллегами
general_sensor.do()
вызывает злую усмешку. Какое, нахрен, "do"? А прерывания? А цифровая фильтрация?
Не знаю как у вас, а у меня все равно есть водораздел в виде переменных в памяти, с отфильтрованными и масштабированными значениями АЦП.
И предложенный general_sensor.do() на самом деле принципиально не может никакого иного "do", кроме
return adc_val[i].filtered_value;
Т.е. он является пустой, избыточной абстракцией.
(повторюсь, лично у вас - все иначе и таv внутри много полезного, но угроза, что так может быть - согласитесь, существует)
Тоже самое с дискретными входами.
С дискретными выходами вообще цирк с конями.
При проектировании "сверху-вниз"... нет никаких дискретных выходов (у меня, по крайней мере).
Есть некие сущности реального мира, например вентилятор, включение которых может быть, например, запрещено с верхнего уровня.
Причем для вентилятора такие запреты - разумны, а для двух выходов на управление 3pt-клапаном - ересь.
Там запрет, если и есть, либо идет на контур регулирования целиком, либо на аналоговую переменную "процент открытия".
И когда мы приходим к битам, нам уже никакие абстракции не нужны,
прикладной алгоритм "отрезан" функциями Fan_On(), Fan_Off(), внутри которых и сетевые блокировки, и приоритеты постов управления и даже пароль оператора.
Тут уже нет никакой беды написать прямо: PORTB |= _BV(Fan[i].Bit_number)
(правда у меня немного иначе, но все равно совсем без инкапсуляции, и сразу понятно в какой бит какого физического регистра будет запись)
2. Модульность.
Повторюсь, я пишу на Паскале (просто компилирую в Си). И паскалевская модульность меня вполне устраивает. Ну можно еще и в классы обернуть (но без полиморфизма).
Но можно и не оборачивать, если у подключаемых модулей вменяемая структура. Это предмет личных предпочтений.