четверг, 4 декабря 2008 г.

ООП в языках

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

Итак попорядку. Мне не нравится термин <эдакое> программирование, потому что я понимаю под программированием процесс превращения дизайна программы в непосредственно код. То есть существует некоторый дизайн-документ, где подробно описан каждый аспект программы и чтобы дизайн-документ стал непосредственно программой, нужно написать реализацию согласно этому дизайн-документу на конкретном языке программирования. Помоему часто программированием называют как процесс создания дизайна программы, её архитектуры, так и процесс непосредственно материализации этого дизайна в виде кода на конкретном языке программирования. Вероятней всего это случилось от того, что в древние времена роль дизайнера и программиста исполнял один человек или даже группа, но всё равно не стояла задача разделения таких понятий. Вообщем не столь важны причины, просто отмечу, что я не люблю называть программированием весь процесс создания программы. Я разделаю весь процесс на две части: дизайн и программирование.

Под дизайном я понимаю обдумывание и документирование (иногда в голове) принципов работы будущей программы в виде какой-либо системы символов и понятий (понятийного аппарата). Дизайн может быть функциональный, объектно-ориентированный или какой-либо другой. Именно методика дизайна определяет какими способами ведется продумывание и построение/создание дизайн-документа. Можно рассматривать проблему с точки зрения множества объектов, которые взаимодействуют друг с другом или же как некий набор функционала, который имеет свойство быть декомпозированным в некую модульную структуру. Что конкретно вы применяете не так важно, потому что дизайн дело тонкое и не простое, лучше всего в этом процессе полагаться на то, что называется чутьем. Интуиция должна подсказивать какое решение верное, а какое нет. Ну и это в последствии проверяется на практике. Каждый хороший дизайнер/архитектор обладает хорошим чутьем.

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

Но конечно же эти два этапа последовательны только в идеале. Зачастую они прогрессируют параллельно. Части дизайна переписываются и изменения отражаются в коде программы. Процесс приобретает итеративный вид. Иными словами так чаще всего (если не всегда) случается на практике. Поэтому вероятно и обзывают всё это одним словом - программирование.

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

Что я имею ввиду? Давайте посмотрим на примере. Пусть в терминах объектно-ориентированного дизайна существует некая абстракция - интерфейс Reader. А также существуют его реализации: ZipReader, MemoryReader и FileReader.

Давайте напишем реализацию на языке Си:


struct Reader;
struct ReaderInterface { /* it's like vtable */
void (*close)(struct Reader *reader);
int (*read)(struct Reader *reader, void *memory, size_t size);
};

struct Reader {
struct ReaderInterface *interface;
void *private_data;
};

/* inline functions */
inline void rclose(struct Reader *reader)
{
reader->interface->close(reader);
}
inline int rread(struct Reader *reader, void *memory, size_t size)
{
return (*reader->interface->read)(reader, memory, size);
}

/* now we're defining somewhere interfaces with their private_data
meaning and with their own interface table */

struct Reader *create_zip_reader(const char *zipfile, const char *file_in_zip);
struct Reader *create_file_reader(const char *filename);
struct Reader *create_memory_reader(const void *ptr, size_t size);

/* and the usage */
void usage_function()
{
struct Reader *r = create_zip_reader("archive.zip", "file/in/archive.txt");
if (!r) {
error();
return;
}

int a = 0, b = 0, c = 0;

rread(r, &a, sizeof(int));
rread(r, &b, sizeof(int));
rread(r, &c, sizeof(int));

rclose(r);
}

А теперь напишем то же самое на С++:


struct Reader {
virtual ~Reader() {} // good C++ style, virtual destructor in interface
// virtual void close() = 0;
// no close() function, closing on delete
virtual int read(void *memory, size_t size) = 0;
};

Reader *create_zip_reader(const char *zipfile, const char *file_in_zip);
Reader *create_file_reader(const char *filename);
Reader *create_memory_reader(const void *ptr, size_t size);

void usage_function()
{
Reader *r = create_zip_reader("archive.zip", "file/in/archive.txt");
if (!r) {
error();
return;
}

int a = 0, b = 0, c = 0;

r->read(&a, sizeof(int));
r->read(&b, sizeof(int));
r->read(&c, sizeof(int));

delete r;
}

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

Каждая конкретная реализация методики полюбому имеет свои ограничения. А что если я захочу так, как делать нельзя? Приходится думать как обойти проблему в рамках поддержки этих конструкций в языке и если не получается, то мы спускаемся на уровень языка Си, где вообще никаких средств для реализации тех или иных идей из ООД нет. Или другой случай, когда появляется новая методика и её невозможно выразить в терминах старой методики, не переписывать же язык! Поэтому я считаю, что каждая конкретная реализация методики не нужна, ибо имеет слишком узкую область применения. А что если попробовать реализовать поддержку методик ООД, без конкретики. Допустим предложить некий синтаксис для непосредственных манипуляций с vtable или ещё что-нибудь достаточно общее, но не вводить понятия класс, объект и наследование. Впрочем надо сказать, что и самого языка Си уже достаточно, т.к. на примере выше можно чётко видеть, что простые inline функции почти решают проблемы нечитаемого вида обращений к виртуальной таблице. 

Впрочем, возможно я погорячился и поддержка ООД действительно не может иметь никаких иных удобных для использования форм. Да и в конце концов все средства ООД в основном в С++ это операции с virtual table путем формирования иерархий классов. Возможно это и не так критично для языка. Но что однозначно я бы покритиковал, это языки в которых "всё является объектом". Это неправильный подход к формированию средств кодогенерации однозначно.

Но если же отойти от примера с ООД и вспомнить про другие техники, начать мыслить обобщенно и пойти по этому направлению. В голове начинают всплывать всяческие страшные картины языка в котором можно модифицировать синтаксис и придумывать новые конструкции и/или модифицировать уже существующие меняя их семантику. И рядом тут ещё метапрограммирование где-то стоит. Но стоит взглянуть на современные языки типа Nemerle или D, то оказывается, что мысли не так далеки от правды. В этих языках уже можно определять новые синтаксические конструкции и представлять свою семантику в виде макросов, которые оперируют с AST (abstract syntax tree). Поживем - увидим. Может именно это направление и будет ключевым в ближайшие 10-20 лет развития средств кодогенерации. Язык с динамическим синтаксисом и плавающей семантикой. Как вам такое?

Комментариев нет: