Парадигмы программирования в C++

С++ — язык программирования общего назначения с уклоном в сторону системного программирования, который:

  • лучше, чем С,
  • поддерживает абстракцию данных,
  • поддерживает объектно-ориентированное программирование,
  • поддерживает обобщенное программирование.

Детальное понимание средств языка — даже всех средств языка — не может компенсировать отсутствия общего представления о языке и основных методах его использования.

Парадигмы программирования и их поддержка в разных языках программирования

Парадигма программирования — это стиль написания программ, который подчиняется определенным идеям и правилам.

Объектно-ориентированное программирование является техникой программирования — парадигмой для написания «хороших» программ, решающих различные задачи. Если термин «объектно-ориентированный язык программирования» вообще что-либо означает, он должен означать язык программирования, который предоставляет удобные механизмы поддержки объектно-ориентированного стиля программирования.

Отметим следующий существенный момент. Можно сказать, что язык поддерживает данный стиль, если он предоставляет средства, которые делают использование стиля удобным (достаточно простым, надежным и эффективным). Язык не поддерживает технику программирования, если для написания соответствующей программы требуются чрезмерные усилия либо мастерство. Такой язык просто предоставляет возможности для использования данной техники. Например, можно написать структурную программу на языке Fortran77 или объектно-ориентированную программу на С, но это неоправданно сложно, потому что упомянутые языки не поддерживают соответствующие техники непосредственно.

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

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

  1. Все средства должны быть встроены в язык понятным и элегантным образом.
  2. Должна существовать возможность комбинирования средств для решения задач, которые в противном случае потребовали бы дополнительных, отдельных средств.
  3. Должно быть как можно меньше неестественных средств «специального назначения».
  4. Реализация средства не должна приводить к значительным накладным расходам в не использующих его программах.
  5. Пользователь не обязан знать ничего, кроме того подмножества языка, которое он явно применяет при написании программы.

Первый принцип апеллирует к эстетике и логике, два следующих отражают концепцию минимализма, а два последних могут быть сформулированы так: «то что вам не нужно, не должно мешать».

С++ создавался с целью добавления поддержки абстракции данных, объектно-ориентированного и обобщенного программирования к традиционному языку С с учетом указанных ограничений. Не подразумевалось принуждение всех пользователей к какому-либо конкретному стилю программирования.

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

Парадигмы программирования в C++

В C++ реализована поддержка следующих парадигм:

  • Процедурное программирование;
  • Модульное программирование;
  • Абстракция данных;
  • Объектно-ориентированное программирование;
  • Обобщенное программирование.

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

Процедурное программирование

Исходная парадигма программирования:

Реши, какие требуются процедуры;
используй наилучшие доступные алгоритмы.

Акцент здесь делается на обработке — алгоритме, необходимом для выполнения требуемых вычислений. Языки поддерживают эту парадигму, предоставляя средства для передачи аргументов функциям и возврата значений из функций. Литература, имеющая отношение к такому образу мыслей, пестрит обсуждениями способов передачи аргументов, различий между разными видами аргументов, описаниями разновидностей функций (процедуры, подпрограммы, макросы…) и т. п.

С точки зрения организации программы, функции используются для наведения порядка в хаосе алгоритмов. Алгоритмы сами по себе записываются с использованием вызовов функций и других средств языка.

Модульное программирование

С течением времени акцент при разработке программ сместился от проектирования процедур в сторону организации данных. Помимо прочего, это явилось отражением факта увеличения размеров программ. Набор связанных процедур вместе с данными, которые они обрабатывают, часто называют модулем. Парадигмой программирования становится:

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

Эта парадигма также известна как «принцип сокрытия данных».

С++ предоставляет механизм группировки связанных данных, функций и т. д. в пространства имен (namespace).

Так как данные — это только часть того, что хочется «спрятать», понятие «сокрытие данных» тривиальным образом расширяется до понятия «сокрытие информации». А именно, имена переменных, констант, функций и типов также могут быть сделаны локальными в модуле. Соответственно, С++ позволяет поместить любое объявление в пространство имен.

Абстракция данных

Модульность — фундаментальный аспект всех успешно работающих крупных систем. Она остается в центре внимания при обсуждении вопросов проектирования. Однако, только модулей недостаточно для ясного представления сложных систем.

С++ стремится решить задачу, позволяя пользователю непосредственно определять типы, которые ведут себя (почти) также, как и встроенные. Такой тип часто называют абстрактным типом данных. Я предпочитаю термин тип, определяемый пользователем (пользовательский тип). Достаточно точное определение абстрактного типа данных потребовало бы «абстрактной» математической формулировки. При ее наличии то, что здесь называется типами, служило бы конкретными примерами таких истинно абстрактных сущностей. Парадигма программирования становится такой:

Реши, какие требуются типы;
обеспечь полный набор операций для каждого типа.

Везде, где не нужно более одного объекта определенного типа, достаточно стиля программирования с сокрытием данных при помощи модулей.

Объектно-ориентированное программирование

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

В терминах С++ мы скажем, что класс Circle является производным от Shape, а класс Shape является базовым для класса Circle. Альтернативная терминология называет Circle и Shape подклассом и суперклассом (или надклассом) соответственно. Говорят, что производный класс наследует члены от базового класса, поэтому применение и базового и производного класса в совокупности обычно называют наследованием. Парадигма программирования теперь звучит так:

Реши, какие требуются классы;
обеспечь полный набор операций для каждого класса;
явно вырази общность через наследование.

В тех случаях, когда такой общности нет, достаточно абстракции данных. Степень общности между типами, которую можно выделить, использовав наследование и виртуальные функции, является лакмусовой бумажкой для определения, применим ли объектно-ориентированный подход к данной задаче. В некоторых областях, таких как интерактивная графика, очевидно, что объектно-ориентированное программирование весьма полезно. В других задачах, таких как классические арифметические типы и вычисления, основанные на них, похоже, трудно найти применение чему-то большему, чем абстракция данных, а средства, необходимые для поддержки объектно-ориентированного программирования, выглядят бесполезными.

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

Обобщенное программирование

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

Парадигма программирования для этого стиля звучит так:

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

Итоги

Не существует идеальных языков программирования. К счастью, язык программирования и не обязан быть идеальным, чтобы быть хорошим инструментом для написания даже огромных систем. В действительности, язык общего применения не может быть идеальным для всех задач. То, что является совершенством для одной задачи, очень часто оказывается недостатком для другой, потому что достижение совершенства в конкретной области подразумевает специализацию. С++ создавался в качестве удобного инструмента для решения широкого круга задач и для более непосредственного выражения разнообразных идей.

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

При практическом программирования мало толку от знания самых «тайных» средств языка или от использования максимально возможного количества средств. То или иное средство языка само по себе не представляет большого интереса. Только в контексте общей технологии и других средств оно приобретает смысл и значение. Поэтому пожалуйста помните, что подлинной целью глубокого проникновения в С++ должно быть стремление научиться использовать средства языка в комплексе, опираясь на хороший стиль программирования и выверенные методы проектирования.

Опытный разработчик применяет различные парадигмы по мере необходимости.

Советы

  • Для написания хороших программ не нужно знать о С++ все;
  • Сосредоточьте внимание на технологиях программирования, а не на элементах языка.

По материалам книги «Язык программирования C++» Бьерна Страуструпа (создателя C++).

Указатели и ссылки в C++
Преобразование типов в C++


pc71.ru