Абстракция данных. Основная идея этой парадигмы — сначала создать набор типов для предметной области, а затем писать программы для их использования. Классическим примером являются матрицы (разделы 24.3–24.6). Интенсивно используется явное сокрытие данных (например, использование закрытых членов класса). Распространенными примерами абстракции данных являются стандартные классы string и vector, демонстрирующие сильную зависимость между абстракциями данных и параметризацией, используемой в обобщенном программировании. Слово “абстракция” используется в названии этой парадигмы потому, что взаимодействие с типом осуществляется посредством интерфейса, а не прямого доступа к его реализации.

Объектно-ориентированное программирование. Основная идея этой парадигмы программирования — организовать типы в иерархии, чтобы выразить их отношения непосредственно в коде. Классический пример — иерархия Shape, описанная в главе 14. Этот подход имеет очевидную ценность, когда типы действительно имеют иерархические взаимоотношения. Однако существует сильная тенденция к его избыточному применению; иначе говоря, люди создают иерархии типов, не имея на это фундаментальных причин. Если люди создают производные типы, то задайте вопрос: “Зачем?” Что выражает это выведение? Чем различие между базовым и производным классом может мне помочь в данном конкретном случае?

Обобщенное программирование. Основная идея этой парадигмы программирования — взять конкретные алгоритмы и поднять их на более высокий уровень абстракции, добавив параметры, позволяющие варьировать типы без изменения сущности алгоритма. Простым примером такого повышения уровня абстракции является функция high(), описанная в главе 20. Алгоритмы find() и sort() из библиотеки являются классическими алгоритмами поиска и сортировки, выраженными в очень общей форме с помощью обобщенного программирования. См. также примеры в главах 20-21.

  Итак, подведем итоги! Часто люди говорят о стилях программирования (парадигмах) так, будто они представляют собой противоречащие друг другу альтернативы: либо вы используете обобщенное программирование, либо объектно-ориентированное. Если хотите выразить решения задач наилучшим образом, то используйте комбинацию этих стилей. Выражение “наилучшим образом” означает, что вашу программу легко читать, писать, легко эксплуатировать и при этом она достаточно эффективна.

Рассмотрим пример: классический класс Shape, возникший в языке Simula (раздел 22.2.4), который обычно считается воплощением объектно-ориентированного программирования. Первое решение может выглядеть так:

void draw_all(vector& v)

{

  for(int i = 0; idraw();

}

Этот фрагмент кода действительно выглядит “довольно объектно-ориентированным”. Он основан на иерархии классов и вызове виртуальной функции, при котором правильная функция draw() для каждого конкретного объекта класса Shape находится автоматически; иначе говоря, для объекта класса Circle он вызовет функцию Circle::draw(), а для объекта класса Open_polyline — функцию Open_polyline::draw(). Однако класс vector по существу является конструктивным элементом обобщенного программирования: он использует параметр (тип элемента), который выясняется на этапе компиляции. Следует подчеркнуть, что для итерации по всем элементам используется алгоритм из стандартной библиотеки.

void draw_all(vector& v)

{

  for_each(v.begin(),v.end(),mem_fun(&Shape::draw));

}

Третьим аргументом функции for_each() является функция, которая должна вызываться для каждого элемента последовательности, заданной двумя первыми аргументами (раздел Б.5.1). Предполагается, что третья функция представляет собой обычную функцию (или функцию-объект), которая вызывается с помощью синтаксической конструкции f(x), а не функцию-член, вызываемую с помощью синтаксической конструкции p–>f(). Следовательно, для того чтобы указать, что на самом деле мы хотим вызвать функцию-член (виртуальную функцию Shape::draw()), необходимо использовать стандартную библиотечную функцию mem_fun() (раздел Б.6.2). Дело в том, что функции for_each() и mem_fun(), будучи шаблонными, на самом деле не очень хорошо соответствуют объектно-ориентированной парадигме; они полностью относятся к обобщенному программированию. Еще интереснее то, что функция mem_fun() является автономной (шаблонной) функцией, возвращающей объект класса. Другими словами, ее следует отнести к простой абстракции данных (нет наследования) или даже к процедурному программированию (нет сокрытия данных). Итак, мы можем констатировать, что всего лишь одна строка кода использует все четыре фундаментальных стиля программирования, поддерживаемых языком C++.

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже