Неуемная рекламная кампания, превратившая Java в своеобразную поп-технологию компьютерной индустрии, способствовала массовым заблуждениям относительно ее истинных достоинств и перспектив. Жертвой Public Relations, однако, обычно становится КАЧЕСТВО. В случае с Java отделить PR от реальности особенно трудно. Предлагаемая статья написана на основе моих недавних публикаций в журналах Computer и IEEE Internet Computing, которые вызвали определенный резонанс и, как хотелось бы надеяться, привлекли внимание пользователей и разработчиков к некоторым фундаментальным слабостям Java. Тем не менее, следует признать, что полученное Java паблисити оказалось слишком сильным, и мы должны научиться жить в мире, где эта технология будет занимать достаточно важное место. Вот почему я постарался сделать свою критику максимально конструктивной, вынося на суд коллег некоторые конкретные предложения, которые могли бы способствовать усовершенствованию Java - с тем, чтобы столь ярко вспыхнувший факел не превратился в кучку пепла с соответствующими печальными последствиями для всей компьютерной индустрии.
Я бы сравнил нынешнюю Java с автомобилем, чей кузов, мотор и руль находятся еще в стадии проектирования, хотя в то же самое время этот частично готовый автомобиль уже движется по производственному конвейеру и в конце концов выходит за ворота завода, попадая к потребителю. А уж тому только остается удивляться и погружаться в тяжкие раздумья, как же он будет ездить на этом полуфабрикате. Да, пока что Java является одновременно поступающим к потребителю продуктом и привлекательным набором идей, чье время еще не пришло.
Но почему же мы покупаем частично законченный продукт? Увы, приходится признать, что чем дальше, тем все чаще происходят в компьютерной индустрии такого рода вещи, и никто даже не выражает удивления по этому поводу. Менеджеры и разработчики принимают решения, ориентируясь на ту долю рынка, которую удалось захватить предприимчивому вендору; интересоваться же объективными техническими достоинствами продуктов становится неприличным. Что из того, что технология готова лишь наполовину?! Коль скоро Java поддерживают главные игроки рынка, то когда-нибудь ее доведут до приемлемого уровня! Когда же это произойдет, ваша компания с той же готовностью, чтобы не сказать - безропотностью, примет следующий Большой Продукт.
Как бы там ни было, Java становится мэйнстримом - даже если ее приберет к рукам Microsoft (а скорее - как раз вследствие этого!). Так что компьютерной индустрии ничего не остается, как начинать приспосабливаться к неизбежности Java. Однако Java и связанные с ней технологии, такие как JavaBeans и IIOP (Internet - версия CORBA для Java) еще не устоялись - они "полупрожарены" и неадекватны тем реальным задачам, которые стоят перед разработчиками. Вот почему усовершенствование Java должно быть предметом беспокойства всей компьютерной индустрии.
Неизбежность широкого распространения Java помещает каждого из нас между молотом и наковальней: мы должны использовать технологию, понимая, что полностью на нее положиться пока не можем. Очевидно, что борьба с вводящей в заблуждение рекламой бессмысленна; однако, может быть еще не поздно что-то изменить в самой технологии.
Java как UNCOL
Для начала попытаемся ответить на вопрос: а является ли Java - и как язык программирования, и как технология - чем-то качественно новым и обещающим реальное продвижение в решении всем известных проблем программной инженерии? Не вдаваясь в детали, можно ответить однозначно: нет. Если при решении этих проблем оказались не вполне адекватными языки-предшественники, то и от Java тоже ничего больше ожидать не приходится - ведь это "блюдо" очень напоминает Cи/C++, и даже если оно разогрето и иначе сервировано, в нем все равно недостает тех ингредиентов, которые могли бы радикально изменить безрадостное состояние, в котором пребывает теория и практика software engineering. Впрочем, давайте, отбросив рекламное лицемерие, обратимся к подробному анализу "pros and cons", тем более, что "pros" у Java тоже есть.
Я написал свою первую программу для лампового компьютера, который имел фантастическую память на магнитном барабане аж в 32 Кбайт плюс неограниченное количество перфоленты. Между прочим, на этой изумительной машине прекрасно исполнялось 32-разрядное ПО - чем не могут похвастаться некоторые Win95 приложения! Вскоре, однако, компьютеры стали строиться на транзисторах, и я принялся - под шелест опадавших календарных листков - осваивать Алгол, Фортран, Паскаль, Ада, С++, наконец. Я всегда приветствовал прогресс в языках и технологиях, но и всегда хотел большего. А именно: я жаждал пришествия поистине универсального языка, написанные на котором программы работали бы на любом компьютере и в любой операционной среде.
Эту мечту лелеяли многие - и не последние - программистские умы. Еще в 1963 году эта популярная мечта получила имя "UNCOL (Universal Common Language) Problem", под которым и впечаталась в сознание целого поколения. Тогда казалось, что UNCOL-проблема будет разрешена с помощью Алгола. Увы!..
Затем появился Паскаль, который был воспринят с энтузиазмом, бывшим в значительной степени реакцией на засилье слишком громоздкого, запутанного и неуправляемого PL/1 (не узнаете сегодняшнюю ситуацию с C/C++ ?!). Однако и Паскаль не решил проблему UNCOL. Затем настал черед Ады, которая, как предполагалось, будет в этом отношении лучше Паскаля; однако и этот язык не преуспел - может быть, потому, что связанная с ним командная экономика оказалась несовместима со свободным рынком, питающим отвращение к любой монополии. Наконец, настала эпоха C++... Надеюсь, ясно, куда я клоню: весь многолетний опыт программной индустрии показывает, что ПО в принципе не может быть написано на стандартизованном универсальном платформно-нейтральном языке.
Заложники унаследованного кода?
Пока Java наслаждается своими 15-ю минутами славы, но что дальше? Давайте задумаемся над таким вполне насущным вопросом: какое наследство он оставит? Мой прогноз относительно жизненного цикла этого языка: полагаю, что написанные на Java приложения должны будут радикально модернизироваться или полностью заменяться не далее, как по истечении десятилетия. Надо бы гарантировать, что построенные на основе этой технологии приложения станут унаследованными системами, с которыми не будет неразрешимых проблем.
Именно поэтому вопрос "Java-как-UNCOL" становится действительно важным. Например, по некоторым оценкам, затраты только Министерства Обороны США на решение проблемы 2000 года составят около 30 млрд. долл. Вопрос поддержки унаследованных Cobol-систем, взявший в заложники принятой в 70-х годах технологии огромное количество серьезных организаций, обусловил возникновение мощной специальной индустрии, которая - в свою очередь - поддерживает на плаву IBM, Andersen Consulting, EDS и многие другие компании из списка Fortune 500. Вот так хвост начинает вертеть собакой. К 2010 году Java тоже выступит в роли такого "хвоста".
Если Java — ответ, то на какой вопрос?
Элегантный минимализм - одна из целей, заявленных при создании Java. Однако история учит, что минимализм редко приводит к успеху. Безопасный, надежный, минималистский Паскаль потерпел неудачу в качестве основного промышленного языка. Минимализм интерпретировался рынком как "неполнота" - Паскаль постоянно страдал от своей репутации "игрушечного" языка.
Java как универсальный язык также весьма неполон: в нем отсутствуют средства ввода-вывода, встроенные функции, интерфейсы прикладных программ (API) с операционными системами и многие другие возможности.
Несмотря на все заклинания насчет "простоты", минимализм на практике так и не стал свойством, определяющим успех языков программирования, как, впрочем, и других сегментов программной индустрии. Вот и Java может быть лучшим и более элегантным, чем сам C++, но рынок редко вознаграждал "объективно лучшие" технологии.
То, что индустрия программирования нуждается в новой парадигме, не вызывает сомнений; а вот предлагает ли Java эту новую парадигму? Вообще в отношении Java слово "парадигма" кажется слишком сильным. Большая часть Java - это разбавленный C++ и модифицированный Паскаль (сильная типизация, ключевые слова, пакеты - от версии USSD P-code и переносимый байт-код). Потоки в Java - бесспорный шаг вперед по сравнению с управлением задачами в UNIX, однако ничего нового в этом механизме нет.
Апплеты - единственная особенность Java, которая несет признаки "новой парадигмы"; однако, хотя апплеты и сервлеты (servlets) действительно являются сущностями с новым содержанием, непонятно, почему они должны быть написаны именно на Java.
Все это и позволяет мне спросить: если Java - это ответ, то каков же вопрос? Какую проблему нельзя было бы решить без Java? Давайте поищем ответ на этот вопрос, рассмотрев некоторые конкретные особенности Java - технологии.
Начнем с перечисления некоторых затруднений, с которыми неизбежно столкнутся Java - программисты. Прежде всего, в этой технологии сохранилось слишком много потенциально ведущих к ошибкам особенностей C/C++. Из массы синтаксических проблем упомянем лишь несколько сюрпризов, которые ожидают Java - программистов.
Начнем с общепризнанно плохой привычки языка Cи к "инкременту" и "декременту". Попытайтесь с трех раз угадать, что означает на Java следующий оператор:
int i = ++ i-;
Далее, следуя прискорбной традиции языка Cи, массивы в Java начинаются с нулевого индекса (вместо единицы). Сколько программистов будет часами пытаться локализовать вызываемые этим непонятно откуда взявшимся правилом ошибки!
Плохо контролируемые правила области видимости в Java добавляют новые сложности к уже имеющимся в языке Cи. Конструкции области видимости, диапазона и синхронизации чрезмерно усложнены, иногда противоречивы и в большинстве своем плохо продуманы. В Java не менее десяти (!) модификаторов: public, private, protected, static, final, native, synchronized, abstract, threadsafe и transient. "Священный" модификатор public полностью разрушает инкапсуляцию в Java. Static, final и public противоречивы и плохо мотивированы. Native вообще невероятен в языке, про который говорят, что он платформно-нейтрален.
Подобно Pascal, Java не имеет встроенных средств ввода-вывода, место которых занимают плохо определенные неполные библиотеки. Как следствие, эти библиотеки уже начали размножаться, что фактически приводит к возникновению ряда диалектов Java. Вот этим самым отсутствием стандарта языка (которое в конечном итоге "размыло" Pascal) тут же воспользовалась Microsoft, налетевшая как коршун на не успевший окрепнуть язык и целенаправленно ведущая дело к разрушению "чистого" Java.
Потоки как благие намерения
Потоки (threads) - это та особенность Java, которую можно было бы только приветствовать; к сожалению, здесь проектировщики Java не достигли совершенства.
Саму по себе концепцию легковесных потоков (lightweight threading) можно признать шагом вперед по сравнению с тяжеловесным управлением задачами (heavyweight tasking) в ОС Unix. Однако открывающиеся возможности можно использовать не только во благо. Фактически Java воскрешает "атомарные процедуры" (atomic procedures) Дональда Кнута в качестве основы механизма взаимного исключения. Однако создатели Java остановились на полпути, и на деле легковесные потоки способны привести к крайне нежелательным коллизиям.
|
Допустим, программа манипулирует данными из двойного буфера (рис.1а). Здесь определены два потока. Первый поток копирует данные из L2 в L1 и, в конечном итоге, в первый исполняемый поток T1. Другой поток делает все наоборот: он копирует данные из L1 в L2 и, в конечном итоге, во второй исполняемый поток T2. При этом T1 и T2 имеют одновременный доступ к буферам L1 и L2. Фактически, порядок доступа не имеет значения; главное - избежать условий, которые могут привести к конфликтной ситуации (race conditions).
Чтобы гарантировать взаимное исключение, программист в Java использует атомарные функции; например, модификатор synchronized. Синхронизированные функции гарантируют взаимное исключение, позволяя внутри себя в некий момент времени быть активным только одному потоку. Предположим, программист декларировал синхронизированную функцию:
class LIST { synchronized public get (List L; char c) {...} } ... L1 = new LIST (...); L2 = new LIST (...);
Функция доступа "get" декларируется внутри спискового класса; затем порождаются два экземпляра этого класса - один для списка L1 и другой - для списка L2. Теперь предположим, что поток T1 использует эти объекты в следующем порядке:
get (L1...); get (L2...);
а поток T2 использует их в обратном порядке:
get (L2...); get (L1...);
|
Результирующая двухпоточная программа работает БОЛЬШУЮ ЧАСТЬ времени, но НЕ ВСЕ время, как это видно из рис. 1б. Основную часть времени поток T1 обращается к L1, а затем - к L2, а поток T2 имеет доступ к L2, после чего доступен L1 (на рисунке последовательность показана в виде вертикальных и горизонтальных пунктирных линий). Однако на некоторое время (и в зависимости от заложенного в операционную систему алгоритма квантования времени) поток T1 прерывается сразу после того, как получит исключительное право доступа к L1, и потоку T2 разрешается получить эксклюзивный доступ к L2. Когда же система квантования времени возвращает управление к T1, получается, что T1 блокирован потоком T2! Когда же управление возвращается к T2, то уже он оказывается блокирован потоком T1. Вот так и возникает классический тупик (deadlock) в двухпоточной системе.
Такого рода ситуация особенно неприятна тем, что она возникает нерегулярно, почти как случайный аппаратный сбой. Ее нельзя ни предсказать, ни легко обнаружить с помощью обычной процедуры отладки. Между тем, с точки зрения программиста, в приведенном фрагменте кода нет ничего криминального. Можно предсказать, что тысячи программистов будущего станут проводить сотни часов в попытках локализовать такие ошибки.
Лучшим решением по управлению потоками в Java могла бы быть адаптация механизма "маршрутных выражений" (path expressions) - как это сделано в Path Pascal. Маршрутные выражения известны уже около двадцати лет - и в этом смысле не отличаются от других возможностей, адаптированных в Java. Эти выражения можно было бы поместить в интерфейсный раздел классов, что позволило бы компилятору отслеживать проблемы синхронизации. Другими словами, синхронизация должна быть спецификацией интерфейса, а не механизмом кодирования.
Java как универсальный инструмент
Java изначально не создавалась как панацея, но ее оглушительный рыночный успех привел к иллюзии, что эту технологию можно использовать как универсальное средство практически везде - даже внутри крошечных смарт-карт. Понимая, что такую универсальность невозможно обеспечить с помощью единственной версии, Sun обеспечила три варианта: встроенная (embedded) версия и две версии уровня предприятия. Проблема, однако, в том, что "Java везде" не означает, что "Java работает везде". Что и понятно - ни один язык не может ответить на противоречивые запросы огромного количества возможных приложений. Следовательно, компания Sun сегментировала рынок и сама потенциально подготовила почву для размывания стандарта. Вот вам первая проблема: слишком много версий Java. В результате, вместо того, чтобы получить программу, которая "будучи однажды написана, исполняется везде", мы получили язык для программ, о которых можно сказать, что "будучи однажды написаны, тестируются везде". Даже простые Web-page апплеты часто не могут исполняться на некоторых из существующих браузеров
Если же программистское сообщество всерьез намерено превратить Java в lingua franca языков программирования, то нужно задуматься о разделении Java на хорошо определенные подмножества, соответствующие основным классам возможных приложений. В частности, необходимо надлежащим образом настроить Java Foundation Classes (JFC) на различные диалекты Java. При этом придется жить в условиях конкуренции с библиотекой Microsoft's Foundation Classes, которая - в соответствии с известными повадками Microsoft - претендует на то, чтобы заточить Java в корпоративные стены Windows API. Если это получится, то не столько благодаря рыночной удали Microsoft, сколько из-за неопределенности и фрагментации Java спецификаций.
Неуправляемое распространение диалектов и отсутствие должным образом определенных подмножеств - это очень серьезные дефекты, но, быть может, не самые главные. Основные проблемы связаны с приложениями Internet.
Java как язык без Глобальных Состояний
Java разрекламирована как распределенная компьютерная платформа на 20 лет вперед. Можно только удивляться, что при этом она имеет только минимальные и низкоуровневые средства для поддержки распределенных вычислений. Предлагать Java - технологию для построения распределенных приложений - это как если бы Ford добавил крылья к своему Taurus, после чего бы объявил о летающих автомобилях.
Очевидно, что Java специализирована для распределенных вычислений не более, чем HTTP механизм, который, как известно, не предусматривает понятия "состояние". В итоге, имеющиеся распределенные средства поддерживаются достаточно бессистемным образом, что -вопреки сложившемуся мнению - не позволяет рассматривать Java в качестве самого подходящего инструмента для создания Web приложений. Если говорить более конкретно, то Java может предложить очень немного сверх сокетов (sockets) - минимального механизма для распределенных вычислений. Отсутствие Java - механизмов для реализации многоадресного IP Multcasting, одного из краеугольных камней push-технологии, неминуемо приведет к дальнейшей фрагментации "Java - стандарта". Проблемы, связанные с работой сокетов, будут все время преследовать разработчиков, пытающихся строить приложения более сложные, чем demo на Web - страницах.
Возьмем для примера старый демо - апплет, изображающий прыгающий мячик, запускаемый одновременно на многих браузерах. Всякий, кто наблюдает за этим мячом собственном браузере, может попытаться направить его в новом направлении. Эта модификации местонахождения мяча должна распространиться на видимые представления мяча во всех браузерах. Как апплет может сразу же довести изменения в поведении мяча до каждого браузера? Как он будет разрешать конфликты, неизбежно возникающие, если два или более пользователя одновременно захотят заставить мяч прыгать по-своему?
Заметьте, что такой симпатичный механизм, как Java - потоки, не может быть задействован при построении распределенных приложений - ведь потоки могут работать только в одном месте. Следовательно, атомарные процедуры, работающие с использованием конструкции "synchronization", не могут помочь в решении проблем распределенного программирования (хотя, конечно, толковый программист способен сам изобрести и реализовать эти механизмы, но едва ли это оправдывает несовершенство технологии).
Таким образом, можно сделать вывод: в Java не реализована концепция "глобального состояния", а без этого механизма работа с распределенными данными всегда будет предметом головной боли для программистов.
А ведь принятая в Java иерархия классов приложений (AWT) могла бы свести проблему распределенного состояния к реализации распределенной версии давно известного механизма MVC (Model-View-Controller). В качестве модели "M" могла бы выступать любая разделяемая структура данных (в нашем примере - мяч). Каждый браузер обеспечивает вид (V) модели, и Java - апплет - контроллер (С) для каждого вида. Распределенное (глобальное) состояние - это, в сущности, то же самое, что и модель в механизме MVC. Одновременно инициализируется множество видов и множество контроллеров - один для каждого браузера. Законченный MVC-механизм мог бы реализовать методы NOTIFY и UPDATE над классами MVC, скрывая таким образом детали синхронизации и модификации распределенного состояния.
Другими словами, AWT мог бы включить как свою составную часть MVC, и таким образом элегантно решить проблему распределенного состояния. И сделать это еще не поздно - например, в рамках JavaSoft от Sun Microsystems. Так можно было бы избавить тысячи программистов от необходимости самим реализовывать что-то вроде MVC - механизма.
Механизмы распределенного управления
Для написания программ, выполняющихся на одном компьютере, любой язык программирования должен иметь оператор присваивания, условный оператор if-then-else и оператор цикла while-loop - это показали Боэм (Boehm) и Джаккопини (Jaccopini) еще в 60-х годах. Вопрос: каковы минимальные требования к набору операторов для написания распределенной и/или параллельной программы? Если мы дадим обоснованный ответ на этот вопрос, то можно будет говорить об элегантном минималистском и в то же время мощном расширении Java, которое и станет настоящим инструментом организации распределенных вычислений.
Очевидно, что необходимо иметь механизм ветвления (fork) или инициализации задач - будь то ветвление, поток или удаленный вызов процедуры. По минимуму это обеспечивается в Java с помощью сокетов (sockets) и сеансов (sessions), и - в конечном счете - механизмом Java RMI (Remote Method Invocation). Я же утверждаю, что для надлежащей работы необходимы еще по крайней мере четыре следующие фундаментальные конструкции:
- fan для параллельных данных;
- tree - редукция (свертка) параллельных данных;
- par - инициирование многоадресного параллельного управления;
- pipe - конвейер параллельного управления;
fan i = 1 to 10; send X[i] to X, Y[i] to Y; { double X, Y, Z; Z = X + Y } return Z to Z[i]; endfan
Здесь fan производит поэлементное суммирование двух векторов, используя множественные сокеты и множественные процессоры cpus. Java код и данные посылаются к каждому из 10 клиентов. Все апплеты выполняют одну и ту же операцию: Z = X + Y, но над различными, в зависимости от индекса i, элементами векторов X и Y. Один элемент результата возвращается вектору Z каждым апплетом.
Ясно, что по сравнению с сокетами и cpus оператор fan обладает значительно большей мощностью. Еще более важно, что он может быть проанализирован Java - компилятором, а потому является "безопасным". Компилятор и интерпретатор (JVM) могут работать вместе, что позволяет предупреждать пользователя о возможности возникновения конфликтной ситуации. Если же два или более cpus возвращают величину, к примеру, для Z[1], то к этой ситуации сразу же привлекается внимание пользователя.
Другая операция параллелизации данных - tree - также может оказаться весьма полезной. Нахождение наибольшего элемента массива или вычисление суммы чисел, хранящихся в некоторой структуре данных - примеры редукции (свертки). Эта операция берет в качестве входных величин множество значений, а возвращает скалярное значение. Следующая конструкция tree может быть использована для выполнения распределенного суммирования:
tree i = 1 to 10 send X[2*i-1] to left, X[i] to right; { int left, right, sum,; sum = left + right } return sum to X[2*i-1]; endtree
Эта конструкция распределяет апплет на 10/2 = 5 процессоров cpus вместе с четными и нечетными элементами (в парах) вектора X. Пары суммируются параллельно, и сумма возвращается. Теперь имеется 5/2 = 3 пары, которые суммируются, и результат возвращается к 3/2 = 1 cpu. В соответствии со своим именем - "tree" конструкция формирует бинарное дерево редукции, которое вычислят сумму X за (log2) параллельных шага.
В отличие от fan и tree, конструкции par и pipe являются параллельными конструкциями управления. Par - это специфическое ветвление:
par i = 1 to 10 1: send A to a, B to b; { int a; /* делать что-то еще*/ } return a to A; 2: send X to c; { int c; /* делать что-то еще */ } return c; 3: /* и т.д. - для других параллельных задач */ ... endpar
Par - это конструкция, родственная прославленному fork в ОС Unix, но имеющая преимущества в том отношении, что компилятор и интерпретатор могут делать такие выводы о состоянии вычислительного процесса, которые Cи - компилятор в Unix не способен сделать относительно fork-join. Она безопаснее и позволяет скрыть большое количество кода, включающего сокеты и потоки.
Последняя, также управляющая, конструкция pipe реализует механизм распределенного конвейера, обеспечивающий выполнение каждой стадии конвейера на своем cpu. Конвейеры не заключают в себе много параллелизма, но действительно обеспечивают предсказуемое и безопасное выполнение распределенной обработки. Следующий набросок показывает, как выразить конвейерное вычисление в виде множества n стадий, которые повторяются 10 раз.
pipe i = 1 to 10; 1: send ... {compute...} return...} 2: send ... {compute...} return...} ... n: send ... {compute...} return} endpipe
Pipe может быть использована, к примеру, чтобы выстроить последовательность обрабатывающих шагов в графическом приложении. Так, на стадии 1 может выполняться процедура обработки изображения, на стадии n - шаг рендеринга, а на промежуточных стадиях - какие-то другие шаги. Опять же, компилятор и JVM способны обеспечить выводы относительно корректности такого рода распределенной обработки, в чем не могут помочь сокеты и сеансы.
Все это вполне тривиальные иллюстрации некоторых возможностей, которые могли бы появиться в более зрелой Java. Такие средства необходимы - в любом языке - если мы хотим строить надежные и безопасные фрагменты распределенных приложений. Когда речь идет о реальных приложениях, обычно больших и плохо управляемых, упорядоченность и доказательность, внутренне присущие введенным конструкциям, становятся весьма существенными. Всего этого достаточно, чтобы сделать вывод: fan, tree, par и pipe служат иллюстрациями конструкций, необходимых в языке, претендующем на широкое использование в мире распределенных вычислений. Являются ли они минимальными в смысле, который Боэм и Джаккопини сделали значимым для операторов структурного программирования? Был бы рад услышать мнение коллег на этот счет.
Еще о ключевых проблемах
Здесь были перечислены только несколько из многих недостатков, скрытых за рекламным фасадом Java, которые будут отравлять жизнь программистам в будущем, когда те станут поддерживать тонны унаследованного Java-кода.
Пока же можно констатировать: создатели Java не слишком преуспели в обеспечении языковых механизмов, которые бы облегчили решение тех сложнейших вопросов, что в полный рост стоят перед индустрией программирования. Надо бы усовершенствовать Java так, чтобы следующие ключевые проблемы разработки ПО оказались в фокусе внимания разработчиков.
Требования и специфицирование. Формулирование и специфицирование требований - быть может, самая насущная проблема в программной инженерии; к сожалению, не сделано даже попытки адресовать эту проблему в Java, более того, активно пропагандируемый стиль разработки ПО на Java грозит потерей того не слишком существенного прогресса в этой области.
Устранение дефектов ПО. Собственно, практически весь прогресс в разработке ПО за последние 30 лет можно свести к методам сколь возможно раннего обнаружения дефектов. Java имеет позитивные черты: сильная типизация; ограничения на указатели; простое наследование. Однако есть и недостатки: перешедшие от C/C++ синтаксические проблемы; нестандартизованные API; уязвимость базовых классов (fragile base class problem) - когда изменения в интерфейсе абстрактного класса могут разрушить всю иерархию классов; проблема уязвимости интерфейса (fragile interface problem) - когда единственное изменение в спецификации интерфейса распространяется на все точки в большой программе, в которых задействован этот интерфейс, - и это далеко не полный список. В целом, Java знаменует лишь небольшой шаг к раннему устранению дефектов.
Компоненты. Мы знаем, что стоимость разработки ПО в расчете на один функциональный пункт (function point) увеличивается экспоненциально с увеличением размеров приложения: разработать вдвое больше кода стоит более чем вдвое дороже. Компонентная технология Java Beans действительно имеет перспективы с точки зрения удешевления разработок. Кажется, однако, что и другие технологии - ActiveX, CORBA, OpenDoc - обещают примерно то же.
Время цикла. Приложения должны изменяться приблизительно каждые 18 месяцев. Это налагает ограничения либо на размер системы, либо на ее функциональность, либо на то и другое. Java сама по себе не предлагает явных средств для поддержки такой скорости адаптации. Однако в сочетании со средствами быстрой разработки приложений RAD (Rapid Application Development), платформная нейтральность Java - технологии может быть полезной.
Сложность. Общеизвестно, что сложность приложений постоянно возрастает. При этом ожидания потребителей растут быстрее, чем способность программных технологий эти ожидания оправдывать. Java не производит впечатления качественно нового интеллектуального инструмента по сравнению с Адой, Паскалем и их собратьями. Нелишне отметить, что многим не менее интересным, чем Java, функциональным, графическим и другим языкам не было предоставлено возможности доказать свои преимущества на деле. Может быть, Java в этом отношении окажется удачливее, хотя заслуженно ли?!
***
Бесспорно, Java имеет действительно ценные черты - прежде всего это обработка ошибок, простое наследование и конструкции спецификаций интерфейсов. В Java удачно адаптированы многие черты языков - предшественников, прежде всего - C/C++, и реализована новаторская идея размеченных средствами HTML апплетов. Так что кое-какой результат имеется, хотя его и пришлось ждать 20 лет. Однако он слишком скромен для столь быстро растущей индустрии, каковой, без сомнения, является производство ПО. Может быть, оттого и разочаровывает при ближайшем рассмотрении этот не в меру разрекламированный язык, находящийся, впрочем (будем справедливы!), еще на ранней стадии своего развития. Конечно, хочется верить, что Java в конечном счете станет более зрелой технологией.
В этом могут помочь, например, аглеты (aglets) - апплеты-агенты (agent applets), разработанные в центре IBM Research, обеспечивающие частичное решение проблемы коммуникации между апплетами. Java Collaboration, CORBA и Java IDL (Interface Definition Language) - все эти инструменты дают Java шанс превратиться в действительно распределенный язык программирования, который в итоге будет включать удобные средства по управлению распределенным состоянием; разрешимы и другие проблемы, часть из которых обозначена в этой статье. Сможет ли компьютерная индустрия адаптировать необходимые усовершенствования с тем, чтобы Java - технологии стали по праву называться "индустриально-сильные"? Хочется надеяться, что это позволит нашей индустрии справиться с образовавшейся после взрыва звезды по имени Java черной дырой.
Толко когда Java и поддерживающие ее технологии станут действительно зрелыми, можно будет говорить о создании полностью новой и обладающей своей спецификой Java - платформы, но не раньше, тем более - не сейчас. Пока же есть опасение, что дарвиновские законы, работающие в программном бизнесе сколь активно, столь и беспорядочно, могут направить эволюцию Java совершенно не по тому пути. Необходимо хорошо продумать, как надлежащим образом решить проблему Java - в противном случае имеется риск увязнуть в миллионах строк низкоуровневого беспорядочного кода.
Список усовершенствований в Java может быть много длиннее, но и тех, что упомянуты здесь, достаточно, чтобы разработчики Java и Комитеты по стандартизации не расслаблялись в течение последующего десятилетия. Но что мне кажется сейчас более важным - это необходимость для всех осознать, что Java - технология имеет очень существенные дефекты, которые вполне можно устранить.
Наконец, позвольте мне сказать, что я не против Java. Я против ее неумеренной и лицемерной рекламы. Кстати, в том, что Java получила столь преувеличенно сильную поддержку со стороны компьютерной индустрии, виновато нынешнее печальное состояние C++. Именно язык C++ и поддерживающие его средства столь неадекватны современным требованиям, что почти все, что могло бы подвинуть эту платформу с ее ведущей позиции, приветствуется с большим энтузиазмом. Однако сегодня Java еще не готова заменить C++ - безотносительно к тому, насколько плох этот язык. Java - при условии преодоления многих ее проблем - может стать по-настоящему зрелой технологией. Во всяком случае, я надеюсь, что это произойдет в течение нескольких лет. Пока же заинтересованным лицам следует адекватно оценить нынешнее состояние Java и ее перспективы.
Тед Льюис (Ted Lewis) — профессор, аспирантская школа ВМФ США. Президент Technology Assessment Group (TAG) - компании, специализирующейся в области анализа компьютерных технологий - в основном по заказам руководителей компьютерных фирм. Член Совета Управляющих IEEE Computer Society. Был главным редактором журналов IEEE Software и Computer; сейчас член редколлегий журналов IEEE Spectrum и IEEE Internet Computing. Автор двух десятков книг.