В моем опыте я часто вижу некоторые шаблоны проектирования, такие как шаблоны посетителей, шаблон стратегии,... в объектно-ориентированных языках, таких как Java... Но я не видел много шаблонов в процедурных языках, таких как C... я интересно, существуют ли эти шаблоны в процедурных языках?
Имеет ли процедурный язык шаблоны проектирования?
Ответ 1
Процессуальные языки действительно имеют шаблоны проектирования. Но поскольку процедурный подход обычно игнорируется в пользу ООП класса, они не получили широкого признания.
Я разрабатываю высокопроизводительное программное обеспечение на C, и есть несколько повторяющихся шаблонов. Поэтому я расскажу о том, какие шаблоны я часто вижу.
Рукоятки
Таким образом, инкапсуляция выполняется в процедурном программировании. Функция построения не возвращает структуру или объект. Но дескриптор: обычно это непрозрачный указатель или просто целое число. Вы не можете делать абсолютно ничего интересного, потому что это просто номер. Детали полностью скрыты. Но вы можете передать этот дескриптор функциям, которые его обрабатывают:
Примеры:
- В Windows функция CreateWindow возвращает HWND. Это дескриптор окна, который может быть передан другим функциям, таким как ShowWindow, DestroyWindow и т.д.
- В Linux используется системный вызов open. Который возвращает только и int. Это дескриптор файла.
контексты
Объекты обычно называются контекстами на языке процедур. Контекст - это структура, которая содержит состояние некоторой системы, так же как и элементы объекта. В ООП вы пишете object.method(parameter)
. В процедурном программировании вы пишете function(addressOfContext, parameter)
. Внутренние функции напрямую используют контекстную структуру, в то время как публичные функции принимают только дескриптор, а реализация решает его в реальной структуре контекста.
Callbacks
Или указатели на функции. Пользователь функции передает адрес своей функции для добавления пользовательского поведения в систему. Это как полиморфизм делается в процедурных программирования. Это позволяет писать общие функции.
Примечательным примером этого является функция qsort C. Это берет адрес массива элементов. Принимает как большой один элемент, так и количество элементов в массиве и функцию компаратора, которая выполняет сравнение. Это полностью общая реализация и позволяет сортировать все виды данных.
Настройка структур
Когда функция может быть параметризована множеством способов. Обычно используется структура установки. Спецификации часто требуют, чтобы эти структуры были заполнены нулями по умолчанию, и заполняются только соответствующие члены. Если некоторые члены являются взаимоисключающими, они помещаются в союз. Типичным примером такой структуры установки является WNDCLASS из WinAPI.
Данные с переменным размером
Ну, это скорее шаблон C, чем общий шаблон дизайна. Иногда объекты могут содержать произвольную бинарную полезную нагрузку. Этот шаблон обычно возникает при чтении данных из двоичных файлов, чем может содержать несколько типов блоков данных. Это делается с помощью такой структуры.
typedef struct
{
int someData;
int otherData;
int nPayloadLength;
unsigned char payload[1];
} VariableSized;
И в коде выполняется следующее:
VariableSized *vs = malloc(sizeof(VariableSized) + extraLength);
Это выделяет память, которая больше, чем структура, позволяющая пространство для полезной нагрузки переменной длины. В чей 5-й байт можно получить доступ, например. vs->payload[4]
.
Преимущество этого в том, что весь объект может быть освобожден одним вызовом free
. И это гарантировало, что у него есть непрерывный блок в памяти. Таким образом, он использует кеш лучше, чем выделение соответствующего буфера где-то еще в куче.
Процедурные копии шаблонов проектирования ООП
Шаблоны ООП никогда не называются в их именах в процедурных языках. Поэтому я могу только догадываться.
Шаблоны создания
- Аннотация factory: абстрактный factory, как правило, является одноэлементным. В этом случае этот шаблон вообще не используется, и вместо этого используется условная компиляция. В противном случае настройте структуры, предоставляющие функции создания.
- Builder: используются настройки.
- Factory метод: для создания используются обратные вызовы.
- Ленивая инициализация. В С++ для этой цели используются статические локальные переменные. В C вы можете использовать шаблон
if (!initialized) { initialize(); initialized = 1; }
в местах, которые не критичны по производительности. Для критического кода производительности ленивая загрузка вообще не используется. Пользователь должен найти место для инициализации контекста. - Прототип. В процедурном мире мы просто возвращаем дескрипторы для объектов запаса. Примером этого является функция GetStockObject в WinAPI. Для изменяемых объектов механизм "копирование-запись" часто используется по соображениям производительности.
- Singleton: просто напишите функции верхнего уровня (и используйте глобальные переменные, когда вам абсолютно необходимо глобальное состояние).
Структурные шаблоны
- Адаптер и Фасад. Шаблон для создания другого интерфейса на существующем. Просто новые функции вызовут старые и другие.
- Мост. Обратные вызовы для конкретных реализаций предоставляются в виде структуры установки.
- Композитный: используются функции верхнего уровня, определяющие дескриптор родительского node, над которым он должен работать.
- Decorator: поведение декорирования предоставляется в виде обратных вызовов. Или один обратный вызов обработчика событий предоставляется для всех возможных украшений, которые принимают различные сообщения и решает их обрабатывать или нет (пример - оконная процедура в WinAPI).
- Flyweight: бинарные данные только для чтения, используемые в структурах и массивах.
- Прокси. Практически то же, что и в ООП, но без классов.
Поведенческие шаблоны
- Цепь ответственности: массив или связанный список обратных вызовов, пройденных циклом. Спецификация описывает, как обратные вызовы указывают, что они обрабатывали запрос, вызывающий разрыв цикла.
- Команда: Команды - это структуры, которые содержат обратный вызов
do
иundo
. Эти обратные вызовы обычно используют какой-то контекст для работы. И массив команд поддерживается для выполнения отмены. - Интерпретатор. Компилятор/парсер/интерпретатор записывается или генерируется с использованием lex и yacc.
- Итератор. Используются ручки, в противном случае они одинаковы. По соображениям производительности в C мы часто придерживаемся массивов.
- Посредник: обычно реализуется с помощью механизма отправки сообщений и циклов сообщений и обработчиков событий.
- Memento: то же, что и в ООП, но без классов.
- Наблюдатель. То же, что и цепочка ответственности, но цикл не сломается. Примером может служить atexit.
- Состояние: реализовано с помощью двухмерных таблиц отправки, которые отображают текущее состояние и запрошенную операцию в функцию. (В редких случаях используется просто ifs.)
- Стратегия. Это основной вариант обратных вызовов.
- Метод шаблона. Обычно структуры позволяют пользователю предоставлять свои собственные обратные вызовы для некоторых функций. Библиотеки часто предоставляют способ использования пользовательской функции выделения памяти, предоставляющей пользовательские
malloc
иfree
. - Посетитель. Реализован с использованием многомерных массивов обратных вызовов, который обычно заполняется NULL в начале (для поведения по умолчанию) и заполняется в основном коде инициализации для каждой пары типов.
Ответ 2
Книга "Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения" была знаковой книгой, которая привлекла внимание к шаблонам проектирования для практики компьютерного программирования, дизайна и архитектуры. Доминирующей парадигмой программирования в то время была разработка объектно-ориентированного программного обеспечения, и книга явно была нацелена на эту парадигму, а не на других. Хотя вы можете утверждать, что некоторые шаблоны проектирования в книге применимы к другим парадигмам, это не было в центре внимания книги. Итак, что стало популярным у дизайнеров и программистов, был набор шаблонов дизайна, описанных в этой книге. С тех пор другие были зарегистрированы другими авторами, блоггерами и другими веб-сайтами. Несомненно, что существуют шаблоны проектирования, которые применяются к процедурным языкам, которые были описаны на разных веб-сайтах, однако, как я уже сказал, когда сообщество разработчиков говорит о шаблонах проектирования, они в основном относятся к шаблонам, изложенным в этой книге. Я знаю, что это не настоящий ответ, потому что я не знаю, где какие-либо задокументированные шаблоны для процедурных языков, конечно же, некоторые из них, я уверен. Я подумал, может быть, я бы сказал о важности этой книги и о той парадигме, к которой она была изначально предназначена.