Google

Почему именно Clipper, а не что-нибудь другое

Давайте рассмотрим Clipper, как язык и средство разработки, не обращая внимания на его конкретную реализацию под названием CA-Clipper 5.x, с точки зрения "на чем писать информационные системы".

Рассмотрим основные характеристики и требования к ИС:

  • Надежность хранения данных
  • Производительность обработки данных
  • Функциональная расширяемость
  • Масштабируемость по железу и ОС
  • Другие, специфические (зависимые от тематики конкретной ИС) требования.

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

Хотелось бы отдельно прояснить вопрос о "надежности". У многих программистов и пользователей существует предвзятое мнение, что DBF - это очень ненадежно, что системы, построенные на xBase, подвержены разрушениям данных и индексов.
Извините, но это совсем не так. DBF тут совсем ни при чем. Разрушения происходят по вине технологии файл-сервер, в которой надежность всей системы зависит от каждой единицы "железа", используемого в обработке данных.
Если же использовать технологию терминал-сервер (например, в связке Linux+DosEmu), то разрушения данных просто не возникают, так как вся обработка данных производится на сервере и никак не зависит от пропаданий 220В на клиентских станциях.

Соответственно требования к средству разработки:
  • Производительность компилятора
  • Производительность во время выполнения
  • Препроцессор, способный подстраиваться под нужды программистов
  • Легкость понимания для начинающих и мощь для профессионалов
  • Легкое наращивание возможностей внешними библиотеками и/или через API
  • Поддержка различных источников данных
  • Масштабируемость по размерам кода, платформам, ОС, процессорам, кластерам и т.п.
  • Поддержка различных стилей программирования - командный, процедурный, ОО, ...

Можно сказать, что это общие параметры, которые собственно ничего не дают при выборе системы разработки. И Clipper как язык вполне удовлетворяет этим требованиям - более мощного препроцессора, чем у Clipper я не видел (разве что fort?), RDD, OO, C-API это все имеется в наличии и очень даже хорошего качества.
С точки зрения языка более интересны параметры:

  • байт код или машинный
  • жесткая типизация или run-time проверки
  • ОО или процедуры
  • модель ОО
  • клиент сервер, файл-сервер или терминал-сервер
  • а может CORBA, Active-X
  • текстовый или GUI
  • поддерживаемые платформы
Байт код (псевдокод) или машинный

Как показывает история развития популярных ИС, практически все они имеют в том или ином виде интерпретаторы "бухязыка" для расширения ИС без изменения некоторого ядра ИС. И причем новые версии выходят со все большим использованием "бухязыков", т.е. большая часть работы ИС перекладывается на скриптовые, интерпретирующие или виртуальные машины. В качестве ярких примеров можно привести 1C, perl, rexx, php, Java, VB. Т.е. как сказал бы чукча - "тенденция, однако". К чему бы проявляется такая тенденция ? Все очень просто - на интерпретируемых языках гораздо легче выполнить требования по функциональной расширяемости и обеспечить простоту сопровождения ИС в отличие от ИС, построенных на машинных языках С/С++, ASM, Pascal и т.п.

Разумеется, в рамках интерпретатора тяжело организовать алгоритмы по обработке больших массивов данных или быстродействующих программ. И для такого рода алгоритмов должна быть предусмотрена возможность подключать код, написанный на "системных" языках. Как это реализовано в том или ином языке, обсуждать не будем, только упомянем, что C-API у Clipper достаточно продвинутое для подключения практически всего, что можно написать на Си.

Типизация

В результате переписки и споров с Maxim.Friedental@f105.n5010.z2.fidonet.org ( maxim@polyot.ru ) получился вот такай текст, добавлять и править не буду, приведу "как есть". Тест полностью написан Максимом и в большей части мы с ним согласны.

Для начала определимся с терминами. Имеем сложную предметную область. Для снижения сложности мы моделируем ее путем разбиения на взаимодействующие сущности.

Сущность имеет свойства. Свойство может быть либо информацией в определенном формате, либо операцией, определенной над этой информацией.

Тип - это совокупность _всех_ свойств сущности.

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

Недостатки типизации таковы:

1. Недостаточная гранулированность. Например, функция на С

	int foo (int a, int b) {
	    return a + b;
	}
может принимать только аргументы типа int и приводящиеся к ним. Тем самым она накладывает ИЗБЫТОЧНЫЕ ограничения на свои аргументы. Действительно, int означает, что для a и b должны быть определены операции -, /, *, %, <, ==, >, >>, << и уйма других. Но ведь они не используются в теле функции! Сравним с той же функцией на нетипизированном Smalltalk:
    foo: a and: b
	^ a + b.
У объекта a будет вызван метод '+' и ему передан аргумент b. Функция правильно работает при ЛЮБЫХ объектах a, для которых определена операция '+' и любых объектах b. Никаких других ограничений на объекты не накладывается. Однако при этом не производится статическая проверка, и все возможные проблемы вылезают во время выполнения. Единственным на сегодняшний день решением для типизированных языков (C++, Java) является определение большого количества интерфейсов, состоящих из одного/двух методов и формирование типа каждой сущности путем множественного наследования. Это неприемлемо.

2. Статичность типизации.

Весь смысл типизации заключается в статической проверке ДО выполнения программы. Если по тем или иным соображениям переменная должна менять свой тип, возникают определенные проблемы - либо язык вообще не позволяет делать такое (и такие языки уже мертвы), либо позволяет, но при этом выключаются все механизмы статической проверки типа! А учитывая то, что в типизированных языках традиционно не развита run-time поддержка информации о типах, возникает вопрос о целесообразности применения типизированного языка в данном конкретном случае.

3. Смех и грех - эквивалентность типов определяется по эквивалентности их идентификаторов, а не свойств. Например, C++ в ситуации

    class A {
    public:
	int var;
    };

    class B {
    public:
	int var;
    };
считает классы A и B - идентификаторами разных типов, хотя тип-то один и тот же. Это тоже самое, как если бы автомастерская бралась ремонтировать только желтые Жигули, и отказывалась от Жигулей всех других цветов :-)

4. Необоснованное увеличение количества ручной писанины для программиста.

Это скорее проблемы неразвитости конкретных IDE, нежели типизированных языков. Например, OCAML способен догадаться из записи

let x = 10, что тип x - int.
Но это скорее счастливое исключение из правил. В большинстве языков тип приходится указывать вручную, более того, приходится указывать его многократно.

Рассмотрим пример:

    int max (int a, int b) {
	return (a > b) ? a : b;
    }
Здесь тип указан трижды, хотя ДОЛЖЕН был быть указан только один раз - и так понятно из назначения функции, что тип второго аргумента и возвращаемое значение будут такими же, что и тип первого аргумента. В некоторых языках (Eiffel) есть специальные конструкции для этого, но особой экономии при этом не получается. Синтаксический мусор, возникающий при типизации, существенно сказывается в основном не на затратах на написание программы, а на времени ее модификации. Это не дает полноценно применить некоторые современные методики разработки программ, например Extreme Programming (www.extremeprogramming.org).

5. Существует ряд мифов, связанных с типизацией.

Например, что существенный процент ошибок в программе связан с ошибками, которые можно отловить типизацией. Или что отлаживать run-time-овые ошибки типов очень сложно. Любой пользователь Clipper-а знает, что это не так (при нормальных средствах написания и отладки).
Некоторые считают, что типы помогают документировать программу. Этот аргумент также выглядит смешным, т.к.:
во-первых, кому нужна палочная дисциплина (а попробуйте пописать на С без типов ;-),
во-вторых, любое использование встроенных типов вроде int, сразу перечеркивает все потуги документировать программу. Int-ом может быть все, что угодно, от температуры на Аляске до состояния конечного автомата,
в-третьих, есть более удобные средства документирования программы.
Говорят, что типизация стимулирует создание системы типов, а, следовательно, и вдумчивого анализа предметной области. Ну... все, наверное, видели программы, опровергающие это высказывание. ;-)

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


Максим закончил. Мне бы хотелось еще добавить один фактор - о какой типизации может идти речь при обработке данных из БД (хоть DBF, хоть результатов SQL-запроса)! Что можно проверить на этапе компиляции, если типы получаемых данных заранее неизвестны?

Ну, если кому-то хочется иметь побольше проблем, то пусть обрабатывает БД на Си, до тех пор пока не надоест.

Процедурная или объектная ориентация

А почему, собственно, вообще возникает такой вопрос? Почему язык не может быть одновременно и тем и другим, а может еще и чем-то третьим?

Я еще понимаю, что "типизация" это принципиально. Потому, как создать типизированный run-time язык - это просто нонсенс (описание чуть выше), как и нетипизированный без run-time - что-то из области сумасшедших идей.

Вопрос должен звучать несколько иначе - какой синтаксис и модель лучше использовать, чтобы не было конфликтов в компиляторе и тормозов при выполнении ?

А политический выбор "ОО или неОО" пусть остается на совести писателей.

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

А вот ОО-модель Clipper даже назвать таковой стыдно. Тем более что как такого ОО и не существует, если не считать недокументированные возможности, на которые накручены через C-API несколько разных библиотек поддержки ОО. В данном случае важно только одно - компилятор Clipper поддерживает ОО-синтаксис и позволяет за счет средств расширения формировать и управлять объектами.

И еще один интересный вопрос - какая ОО-модель лучше подходит для управления объектами, хранящимися в БД? Особенно, если хранилище неОО, а обычные таблицы или результаты SQL-запросов.

Получается, что объекты должны наследовать структуру таблицы, а она неизвестна до ее использования, я даже не упоминаю про этап компиляции, структура становится известной лишь после открытия_БД/получения_ответа_от_SQL_сервера. Вот и получается, что структура объекта должна генерироваться в run-time, причем структура не является постоянной за время жизни объекта.

И много языков таких существует ? ..... И все они скриптовые!

Файл-сервер, клиент-сервер, терминал-сервер, CORBA, Active-X, GUI...
Собственно говоря, никаких языковых проблем для использования всех этих технологий у Clipper не существует, есть проблемы с наличием/отсутствием нужных библиотек или Clipper-совместимых компиляторов под нужные платформы.
Заключение
Так почему собственно Clipper, а не что-нибудь другое ?

Потому что мы не знаем другого языка, который умеет столько же, сколько и clipper.


© Ю.Хныкин, uri@itk.ru, 2000