Честно говоря, на такую тему можно написать работу размеров от небольшого эссе и до размеров средней книги. Тем не менее, я попытаюсь уместить всё в один пост блога и не утомить читателей чтением. Впрочем, это невозможно конечно же, но попытка обещает быть интересной. Поэтому приготовьтесь к чтению и вперед.
Заранее предупреждаю, я не имею полного набора фактов относительно истории развития языков программирования. Я не сведущ относительного того кто, когда и главное каких недалал поделок, называя их "языками программирования" и что уж скрывать, меня это не особо интересует. На данный момент времени понятно, что существует ряд популярных языков программирования в которых миллионы мух (вы уж простите, что я так о программистах, конечно же вы самые умные и интеллектуальные и <вставьте ваш комплимент>, но это звучит символично) что-то эдакое нашли. И опираясь на то, что я вижу сейчас и то, что было когда-то (немножко-то истории я всё же знаю), я сделаю свои выводы. И пожалуйста, не подумайте, что я ярый приверженец какой-то одной идеи будь то ООП или функциональщина. Я скорее пытаюсь не вдаваясь в термины/понятия парадигм поделиться своим представлением относительно явления "программирование".
Дженерейшн намба уан. Или его отсутствие.
Начиналось всё с железа конечно же. Были железяки какие-то и люди придумали, что было бы неплохо заставить их делать разные вещи. Первые железяки наверно и программировались по железному - куча рычажков, переключаем их в разное положение и получаем разные результаты работы. Прекрасно! Но хотелось большего. Вероятно в будущем появлялись ещё всякого рода извращения с перфокартами и какими-нибудь вариантами промежуточной памяти (регистры). Вообщем, это нас не сильно интересует, поэтому я это оставлю для тех, кого интересуют факты истории. Время шло и люди родили быстрые машины, которые умеют выполнять вполне определенный набор команд и назвали их CPU - процессор тобишь. Всё было круто и прекрасно, но однажды настал вероятно такой момент, когда запоминать все команды в циферках было сложновато. Уж не говоря о процессе написания программ.
Поколение некст или намба ту.
И тут появился первый язык программирования, если его можно так назвать - assembly language. Главное, на что стоит обратить внимание - это появление стадии трансляции из символьного представления машинных команд в непосредственно машинные. Ещё раз - главное это сама стадия, а не что она делает. Т.е. человек в то время открыл для себя важную штуку - машинный код он писать нифига не способен и появилась потребность в средствах кодогенерации.
Я немного отойду от главной темы и немного углублюсь в абстрактное понимание процесса программирования. Что это такое по сути? Мы пишем программы на любимых языках и формошлепим иногда, рисуем какие-то GUI элементы, подготавливаем какие-то наборы данных. По сути я называю этот процесс одним словом - кодогенерация. Т.е. если мы пишем в Си статический массив - это кодогенерация, если мы пишем функцию для рекурсивной обработки сложной древовидной структуры - это кодогенерация. Ключевая фишка тут это то, что данные и код это одно и то же. Рисуем картинку для программы - кодогенерация. Да, я понимаю звучит не важно, просто запомните пожалуйста, что я называю это так. Когда нужно будет различить, что я имею ввиду, я буду упоминать кодогенерацию данных или кодогенерацию исполняемого кода :)
Так вот, ассемблер - первое средство кодогенерации. Понятно дело, что оно не особо сильно ушло от машинного одномерного представления (память имеет одномерный вид) и поэтому писать на нём всё-таки сложновато. Да и к тому же ассемблер привязан к определенному процессору, т.к. использует вполне прямое символьное представление команд вполне конкретного процессора. Однако, и тут произошло не мало интересного. Появляются переменные, появляются макросы (препроцессор), появляются другие инструменты позволяющие человеку более внятно и компактно выражать какой же он хочет код сгенерировать.
Третье поколение. Революция.
Следующим шагом в развитии средств кодогенерации (читайте языков программирования) является структуризация кода. Кода в широком смысле этого слова, т.е. данных и исполняемого кода. Функция становится основным строительным блоком исполняемого кода, структура становится тем же основным строительным блоком данных. Становится ясно, что логика процессоров вполне похожа и можно создать некий абстрактный вариант команд процессора, который можно преобразовывать в машинный код для различных архитектур. Как видно, куча средств кодогенерации усложняются. Читатель может догадаться, что главным образом я говорю о Си и это несомненно правда. Си - это язык прагматичных, практикующих программистов, который появился из непосредственных нужд упростить процесс кодогенерации. Это не поделка маркетологов или каких-нибудь профессоров, преподавателей-теоретиков. Так вот, язык становится кроссплатформенным в какой-то мере, благодаря абстракции инструкций процессоров и помощи усложненного препроцессора. Кроссплатформенным конечно же на уровне исходников. Не сложно догадаться, что такой подход к эволюции средств разработки завоевывает популярность и даже сейчас Си является основным языком, на котором написано множество ОС (почти все я думаю). И Си незаменим, где-то слышал фразу, что Си основа интеропа между другими языками и нельзя не сказать, что это так, по крайней мере пока ОС написаны на Си. Но далее в истории что-то случилось и всё пошло не так.
Звездец.
Пожалуй в конце 80ых и в начале 90ых, такое устройство как компьютер начинает получать известность и массовость. В сущность вливаются реклама и деньги. Всё подвергается жуткой коммерциализации и прочим прелестям от лукавого. Одним словом - неприятно. Но это не самое страшное, появляются некоторые чёрные концепции типа ООП, АОП, которые намеренно отступают от главной сути языков программирования и дают какие-то свои ничем не подкрепленные понятия. Теперь язык программирования не является средством кодогенерации, а становится вдруг средством выражения идей человека в первую очередь, хотя я считаю это в корне не верно. Нельзя избавится от того факта, что программирование это процесс ориентированный на железо. Т.е. Hardware-oriented programming. В ООП появляются какие-то непонятно чем навеенные понятия: наследование, класс, инкапсуляция, объект, полиморфизм. И они якобы имеют какие-то отношение к реальности человеческой или машинной. Иными словами настал звездец. А теперь я предлагаю вам немного передохнуть, очистить голову от всяких бранных мыслей и продолжить чтение. Далее я раскрою тему того, что я имею ввиду.
Что не так с нынешним путем. Или его отсутствие ;-)
Стоит заметить, что под ООП я имею ввиду все эти новомодные фичи с наследованием, инкапсуляцией, абстракцией и полиморфизмом. Ими в полной мере или частично страдают такие языки как Java, C#, C++, Python, D, Ruby, PHP, Delphi (вероятней всего ваш основной язык программирования в этом списке). А теперь давайте по порядку посмотрим, что же из себя представляют эти фичи.
Абстракцию можно даже не рассматривать, т.к. программирование это такая история про абстракцию. Нет программирования без абстракции. И почему её приписывают к ООП?
Наследование - все помнят типичный пример Shape, Rectangle, Circle. Идея в том, что какие-то структуры данных строятся на основе уже существующих. Каждая Shape имеет площадь, но скажем Rectangle имеет в дополнение к этому такой параметр как отношение сторон, а Circle имеет радиус. Я считаю, что наследование хоть и имеет какую-то логику, но реальная её польза ощутима при использовании понятия полиморфизм. Иначе же это простая композиция данных и/или алгоритмов, т.е. кода. Вообще я бы сказал, что наследование это и есть способ реализации идеи полиморфизма. Помнится в злой науке невнятные понятия частое явление. Есть о чём задуматься? Так, обратно к теме. Пусть наследование и полиморфизм представляют один набор тесно взаимосвязанных фич, на практике же всё это представлено в виде неявного использования таблицы виртуальных функций/методов (vtbl). Т.е. такой простой прием кодогенерации встраивают в язык по какой-то причине и позволяют работать с ним на уровне синтаксиса. Оно и понятно, никому не охото писать myobject->vtbl[METHOD_ID](args). Но фактический в такой фиче нет ничего нового и по сути своей она ничем не отличается от простого использования указателей на функции. Возможно вид несколько иной, пусть и более удобный. Но однозначно фича не стоит того, чтобы о ней писали трактаты и на площадях выкрикивали: "Everything is an object!".
Инкапсуляция - что-то там про сокрытие данных и выставление на показ только интерфейса для пользователя-дебила. Ну хорошо, пусть это средство самоконтроля себя от своих же ошибок. Также можно отнести к средству какого-никакого дизайна/архетектуры, но сомнительно. #define private public - это реальность, а не сказка. Понятно дело, что никакого кода не генерирует. В С++ если использовать класс на прямую, не по указателю, то компилятору обязательно нужно знать все детали его реализации, для определения размера. Так что смысла в кодогенерации нет никакого. Получаем бесплатную порцию жучков мозгоедов.
Про полиморфизм уже было сказано. Подведу я лучше итог по ООП. Однозначно ООП это инструмент разработки дизайна/архитектуры программ и к языку вообще никакого отношения не имеет. Тут правы те, кто знают, что нельзя называть язык объектно-ориентированным, язык может поддерживать средства для работы с объектно-ориентированным дизайном, но не факт, что они вообще нужны.
Куда идти?
Теперь я бы хотел ещё остановится на других элементах языка С++ в частности, которые имеют несомненно большую пользу, чем ООП средства. А также озвучу некоторые случайные идеи.
Более строгая система типов - помимо более строгого контроля ошибок программистов-идиотов, скорее всего несет смысл в виде возможности генерировать более оптимально код. Непосредственно фича кодогенерации, как и система типов вообще.
Шаблоны - неосознанная попытка заменить препроцессор на типизированный препроцессор с возможностями метапрограммирования. Шаблоны сильно полезны в области generic программирования. Непосредственный инструмент кодогенерации. Более совершенная система подобного рода должна рано или поздно заменить устаревший препроцессор. Можно посмотреть в сторону языка D, где препроцессора уже нет и сам язык выполняет его роль на стадии компиляции. Появились даже AST макросы, позволяющие работать с AST напрямую. Однозначно правильная идея развития кодогенерации как данных так и исполняемого кода.
Перегрузка операторов - средством генерации вряд-ли назовешь, скорее синтаксический сахар, но тем не менее польза есть. Просто удобно. А кто не понимает - пусть пишут на Lisp.
Конструкторы, деструкторы - опять инструмент кодогенерации, причем очень простой. Однозначно имеет определенную пользу, т.к. расширяет возможности структуризации кода путем взаимодействия с понятием scope.
Есть в С++ ещё и плохие штуки. Например сломанный static. В Си изначально keyword static является исключительно инструментом кодогенерации данных. В С++ он приобрел невнятное состояние из-за возможности запускать исполняемый код. Это от части проблема того, что в языке нет нормальных инструментов метапрограммирования, от части вероятно недальнозоркость авторов языка. Вроде бы в С++0x должны появиться какие-то фичи связанные с этим в виде constexpr. Посмотрим.
Теперь о GUI. Не поверите как текущие GUI системы себя инициализируют! Вместо того, чтобы программа напрямую содержала snapshot GUI иерархии виджетов и после загрузки программы память сразу начинала с ним работать, в GUI программах содержится код создания этой самой иерархии, который вызывается ровно один раз и является абсолютно не нужным баластом с точки зрения логики. Всё, что можно делать вне runtime'а, должно делаться именно там. Проблема в том, что в современных языках недостаточно выразительных средств кодогенерации данных. Нельзя описать в разумном виде сложную иерархию объектов и использовать её. Языки имеют средство описания массивов например, но в приличном виде невозможно описать скажем red-black tree. Почему? А потому что мыслим в другом направлении. Я однозначно за развитие таких средств, пусть они и будут в виде техник метапрограммирования. Конечно, ничего не мешает мне написать генератор данных и встроить их в Си код, но такие средства должны уметь взаимодействовать сразу с языком программирования. Подведя итоги. В GUI вообще не должно быть кода инициализации самих GUI виджетов. Конечно всегда есть исключения, которые имеют под собой вполне понятное обоснование. Если программа загружает конфиг и в зависимости от значений полученных оттуда что-то меняет в виджетах, это понятно. Но когда программа при каждом запуске запускает один и тот же код, который генерирует в памяти один и тот же набор данных при любых условиях и более того, этот набор данных меньше или равен по размеру самому кода(!), это лишний код и он требует удаления. Поверьте, такой код не редкость!
Хотелось бы напоследок сказать. Товарищи программисты, не забывайте чем вы занимаетесь, осознавайте свои действия в полной мере, смотрите на вещи открытым широким взором. Ищите связь там, где её на первый взгляд нету. Помните, что весь процесс программирования это лишь способ кодогенерации машинных команд и данных, которые этими командами будут обрабатываться.