При работе с микроконтроллерами есть вещи, которые по своей природе глобальны — я имею в виду периферийные устройства, такие как последовательные порты или другие интерфейсы. Есть также периферийные устройства, которые не только глобальны, но есть только одна (и никогда не будет больше) — например, периферийные устройства, контролирующие тактовые частоты ядра или контроллер прерываний. Эти периферийные устройства имеют какое-то глобальное состояние (например, тактовая частота ядра установлена на какое-либо значение), и обратный расчет этих значений неэффективен.
Если бы я хотел, чтобы моя программа была хорошо ориентирована на объект, мне трудно решить, как обращаться с такими объектами … Глобальные переменные не хороши, и это очевидно, но я просто не знаю (не достаточно опыт) следует ли мне пытаться «скрыть» тот факт, что эти вещи глобальны … Например, «cin» или «stdout» также являются глобальными (давайте не будем учитывать тот факт, что в многопоточных приложениях они обычно ориентированы на потоки) и нет скрывает, что … Давайте придерживаться периферийного генератора тактовой частоты — есть только один, поэтому я мог бы использовать одноэлементный анти-шаблон (или сделать класс статическим или просто иметь глобальный единственный объект (это то, что я обычно делал) , поскольку в этом объекте хранятся текущие настройки часов, и это значение необходимо для LOTS других вещей — необходимо установить системный таймер, используемый RTOS, необходимо установить часы для других периферийных устройств (скорость передачи UART, битрейт SPI, …) необходимо установить правильные часы для внешней памяти или настроить состояния ожидания памяти, поэтому я думаю, что создание одного объекта в main () и передача его везде будет немного громоздким …
Я мог бы написать методы так, чтобы вся «глобальная» информация поступала из периферийных регистров (например, частота ядра могла быть обратно рассчитана из текущих настроек ФАПЧ), но это также кажется неправильной идеей, не говоря уже о создании объекта для генератора периферийных часов везде было бы смешно смотреться …
Текущая настройка часов может храниться в статическом члене класса, но отсюда есть только один маленький шаг к полностью статическому классу (так как указатель «this» будет бесполезен для класса, у которого нет состояния) …
Решение, обычно встречающееся в не объектно-ориентированных программах, наиболее близко к полностью статическому классу — есть только функции, которые работают с глобальными переменными.
У кого-нибудь есть хорошая идея, как правильно справиться с таким сценарием или стоит ли эта проблема времени? Может быть, я должен просто использовать один глобальный объект и покончить с этим? (;
Если бы я хотел, чтобы моя программа была хорошо ориентирована на объект, мне трудно решить, как обращаться с такими объектами … Глобальные переменные не хороши, и это очевидно, но я просто не знаю (не достаточно опыт) должен ли я попытаться «скрыть» тот факт, что эти вещи являются глобальными …
Когда я читаю это, мне интересно, знаете ли вы, почему вы используете ООП и почему вы не используете глобальные переменные.
Во-первых, ООП — это инструмент, а не цель. В вашем случае контроллеру прерываний не нужны такие вещи, как деривация и виртуальные функции. Все, что вам нужно, это интерфейс для программирования, заключенный в один класс. Вы даже можете использовать набор простых функций, которые делают это (C = стиль модульного программирования), не отказываясь от удобства обслуживания. В вашем случае сделать глобальный экземпляр еще яснее. Представьте себе альтернативу, в которой разные части программы могут создавать классы, которые используются для доступа к одному и тому же UART. Если вы используете глобальные переменные, код (или, скорее, автор) знает об этом и будет думать о том, как координировать доступ.
Теперь, что касается глобалов, вот пример, почему бы не использовать их:
int a, b, c;
void f1()
{
c = a;
f3();
}
void f2()
{
c = b;
f3();
}
void f3()
{
// use c
}
int main()
{
a = 4;
f1();
b = 5;
f2();
}
Дело в том, что параметры хранятся в глобальных переменных, а не передаются в качестве фактических параметров, что затрудняет понимание того, где и когда они используются. Кроме того, использование выше полностью исключает любые рекурсивные вызовы. Что касается вашей среды, то есть вещи, которые по своей природе глобальны, потому что они являются уникальными частями среды, такими как контроллер прерываний (аналогично cin / cout / cerr / clog). Не беспокойся об этом. Должно быть, многие из них используются повсеместно, пока вы не подумаете об ограничении доступа.
Есть два руководства, чтобы сделать это проще:
Вы в значительной степени изложили свои варианты:
В конечном счете, вам решать, что выбрать из двух, и выбрать то, что вам больше нравится из эстетической или другой перспективы.
В конце дня, как вы сказали, у вас все еще будет один генератор тактовых импульсов, один UART и т. Д.
Нужно иметь в виду, что слишком большое количество абстракций исключительно для целей абстракции не принесет вам много пользы. Однако он может сделать так, чтобы кому-то незнакомому с вашим кодом было сложнее понять, как на самом деле работают эти слои классов. Итак, примите во внимание вашу команду, если таковая имеется.
Шаблон Singleton, несомненно, является источником споров. Некоторые люди просто говорят, что это «плохо», но я бы не согласился; это просто широко используется неправильно и неправильно. Это определенно полезно в определенных ситуациях. На самом деле, это относится именно к ситуации, которую вы описали.
Если у вас есть класс, который должен быть доступен глобально, и по самой своей природе он не может иметь несколько экземпляров, тогда Лучший вариант.
Одиночки, глобальные объекты, статические классы — все они одинаковы. Одень злое мировое государство в любой соус, какой захочешь, это все еще глобальное государство. Проблема в салате, а не в заправке, так сказать.
Малоизученный путь — это монадический код в стиле Haskell (да, в C ++). Я никогда не пробовал сам, но, судя по всему, этот вариант должен быть веселым. Смотрите, например Вот для примера реализации интерфейса Monad в C ++.