Image

Imagecd_riper wrote in Imageru_cpp

Categories:

Антиглобализмъ

В сети (включая и это сообщество) довольно часто натыкаюсь на те или иные обсуждения, связанные с паттерном синглтон, что, по всей видимости, свидетельствует о нездоровом интересе неокрепших умов к этому чрезвычайно вредному решению. Именно поэтому, в данном материале постараюсь на вполне очевидных примерах рассказать о том зле, которые приносят в код глобальные переменные и подобные им решения.

Похоже , что программирование было известно еще до нашей эры. Именно такое складывается у меня впечатление, когда я читаю изречения, дошедшие до нас из эпохи Древнего Рима. «Человеку свойственно ошибаться» – разве это не о том, что написание всякой программы приводит к ошибкам в ней? Или девиз Цезаря «Разделяй и властвуй» – вот он ключ и к дизайну сложной программы, и к возможности ее последующего анализа. Человеческий мозг имеет более чем конечные возможности для охвата системы, состоящей из большого числа компонентов. Для обхода этого ограничения используется иерархический подход – легко понимать суть предмета, которые можно разложить на уровни, каждый из которых имеет небольшое число взаимодействующих сущностей.
Если посмотреть на программу и глобальные переменные в ней, видно, что эти переменные имеют нехорошую особенность – они потенциально доступны на любом уровне приложения, в любой строчке кода, что может серьезно усложнять анализ любой, даже банальной однострочной функции, и делает нетривиальным занятием попытки понять связи и взаимное влияние частей кода друг на друга.
Энтропия, вносимая глобальной переменной в код, пропорциональна его размеру. Десять глобальных переменных в проекте на миллион строчек кода это самый настоящий источник хаоса.
Меры противодействия очевидны – связи нужно всеми силами ограничивать. Точки взаимного влияния частей подсистемы – минимизировать и делать очевидными. В C++ более чем достаточно средств для этого, например слова public/protected/private.

С проблемой глобальных переменных на этапе анализа исходного кода разобрались, теперь о дизайне.

Глобальные переменные ухудшают масштабирование проекта – они не дают распространить понятие «локальный контекст» на любой элемент в системе, закладывая тем самым серьезные ограничения.

Объясню на примере из личного опыта. Была у нас embedded разработка – некий цифровой пульт связи, который подключался к телефонной станции, и обеспечивал функционал голосовых вызовов, режим конференции, записную книжку и т.д. и т.п. Устройство как устройство – некий коммуникационный интерфейс, текстовый дисплей, клавиатура со светодиодной индикацией, flash память – все просто и понятно. Код для этого пульта писали на C++, безбожно увлекаясь синглтонами – ежу ж понятно, что у девайса ОДИН дисплей, ОДНА клавиатура, ОДИН сетевой интерфейс... Все было хорошо, пока на очередной этапе не возникла мысль сделать пульт «болванчиком», тонким клиентом, который лишен всякой сложной логики, и работает в режиме терминала, т.е. отсылает станции коды нажимаемых клавиш, а станция полностью управляет дисплеем и светодиодами, реализуя всю ту логику, которая раньше крутилась на стороне пульта.
Получилась интересная картина. Грубо говоря, практически весь код, который раньше реализовал концепцию «пульт» на стороне железа, теперь должен был выполнятся на станции, параллельно, в неограниченном количестве экземпляров. То, что раньше было глобальным, должно было стать множественно локальным. Дизайн на синглтонах и прочих глобальностях пошел в мусор, но главное, что я навсегда запомнил этот урок.

Приведу еще один пример. Представим себе любое распределенное приложение, допустим некую клиент-сервер систему. Отладка такой системы, в силу ее работы в нескольких процессах (или даже на нескольких машинах в сети), занятие довольно таки нетривиальное. А ведь если дизайн такой системы делался с умом, то отладку такого приложения можно без проблем свести к режиму отладки обычного приложения: просто инстанцируем в одном приложении (в разных потоках) код сервера и код нужного числа клиентов. И получаем все преимущества отладки в одном процессе.
(Разумеется, я отлично понимаю, что это не совсем эквивалентная подмена, и таким образом мы можем не увидеть некоторых важных нюансов настоящей распределенной работы).

Резюмируя. В моих приложениях глобальных переменных нет. Просто нет и все. Даже логирование на всех уровнях я всегда делают зависящим от переданного сверху контекста. Да, мне не лень лишний раз передать этот самый контекст, и тянуть его по всем объектам в программе. Я знаю для чего это делаю, и знаю какие преимущества мне это дает. Вам советую поступать аналогично.

Ибо так говорил Заратустра.