Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Shablony_korporativnykh_prilozheniy_Fauler_M.docx
Скачиваний:
3
Добавлен:
01.07.2025
Размер:
4 Мб
Скачать
☆

Введение

Создание компьютерных систем (может быть, вы этого еще не знаете) — весьма не­простое дело. По мере увеличения их сложности трудоемкость процессов конструирова­ния соответствующего программного обеспечения возрастает согласно экспоненциаль­ному закону. Как и в любой профессии, прогресс в программировании достигается толь­ко путем обучения, причем как на ошибках, так и на удачах — своих и чужих. Это изда­ние представляет собой учебное пособие, которое поможет вам усвоить информацию и передать полученные знания другим значительно быстрее и эффективнее, чем это удава­лось мне самому.

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

Архитектура

Одно из странных свойств, присущих индустрии программного обеспечения, связано с тем, что какой-либо термин может иметь множество противоречивых толкований. Ве­роятно, наиболее многострадальным оказалось понятие архитектура (architecture). Мне кажется, оно принадлежит к числу тех нарочито эффектных слов, которые употребляют­ся преимущественно для обозначения чего-то, считающегося значительным и серьез­ным. (Нет, я слишком прагматичен, чтобы цинично пользоваться этим фактом, стараясь привлечь внимание читающей публики.:—))

Термин "архитектура" пытаются трактовать все, кому не лень, и всяк на свой лад. Впрочем, можно назвать два общих варианта. Первый связан с разделением системы на наиболее крупные составные части; во втором случае имеются в виду некие конструк­тивные решения, которые после их принятия с трудом поддаются изменению. Также рас­тет понимание того, что существует более одного способа описания архитектуры и сте­пень важности каждого из них меняется в продолжение жизненного цикла системы.

Время от времени Ралф Джонсон (Ralph Johnson), участвуя в списке рассылки, от­правлял туда замечательные сообщения, одно из которых, касающееся проблем архитек­туры, совпало с периодом завершения мною чернового варианта книги. В этом сообще­нии он подтвердил мое мнение о том, что архитектура— весьма субъективное понятие. В лучшем случае оно отображает общую точку зрения команды разработчиков на резуль­таты проектирования системы. Обычно это согласие в вопросе идентификации главных компонентов системы и способов их взаимодействия, а также выбор таких решений, которые интерпретируются как основополагающие и не подлежащие изменению в буду­щем. Если позже оказывается, что нечто изменить легче, чем казалось вначале, это "нечто" легко исключается из "архитектурной" категории.

В этой книге представлено мое видение основных компонентов корпоративного при­ложения и решений, которые должны приниматься на ранних фазах его жизни. Мне по душе трактовка системы в виде набора архитектурных слоев (layers) (подробнее о слоях — в главе 1, "«Расслоение» системы"). Поэтому в книге предлагаются ответы на вопросы, как осуществить декомпозицию системы по слоям и как обеспечить надлежащее взаимо­действие слоев между собой. В большинстве корпоративных приложений прослеживает­ся та или иная форма архитектурного "расслоения", но в некоторых ситуациях большее значение могут приобретать другие подходы, связанные, например, с организацией кана­лов (pipes) или фильтров (filters). Однако мы сконцетрируем внимание на архитектуре сло­ев как на наиболее плодотворной структурной модели.

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

Корпоративные приложения

Созданием программного обеспечения занимается множество людей. Но говорить о программном обеспечении в целом — значит, не сказать ничего, поскольку разно­видностей программных приложений чрезвычайно много и каждая ситуация выдвигает особые проблемы, отличающиеся собственным уровнем сложности. Наглядным при­мером могут служить дискуссии с коллегами, работающими в сфере телекоммуника­ций. С определенной точки зрения "мои" корпоративные приложения намного проще, нежели телекоммуникационное программное обеспечение, — мне не приходится решать чрезвычайно сложные проблемы обеспечения многопоточного функционирования и тесной интеграции аппаратных и программных компонентов. Но во всем остальном мои задачи существенно сложнее. Корпоративные приложения чаще всего имеют дело с изо­щренными данными большого объема и бизнес-правилами, логика которых иногда про­сто противоречит здравому смыслу. Хотя некоторые приемы и решения более-менее уни­версальны, большинство из них адекватны только в контексте определенных ситуаций.

На протяжении всей профессиональной карьеры я занимался преимущественно кор­поративными приложениями, и это, разумеется, обусловило выбор типовых решений, представленных на страницах книги. (В числе близких аналогов понятия "приложение" можно назвать термин "информационная система"; читатели постарше, вероятно, вспом­нят выражение "обработка данных".) Но что именно подразумевает понятие корпоратив­ное приложение (enterprise application)! Точное определение сформулировать трудно, но дать смысловое толкование вполне возможно.

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

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

Данных, с которыми имеет дело корпоративное приложение, как правило, бывает много: даже скромная система способна манипулировать несколькими гигабайтами ин­формации, организованной в виде десятков миллионов записей; и задача манипуляции этими данными вырастает в одну из основных функций приложения. В старых системах информация хранилась в виде индексированных файловых структур, подобных разрабо­танным компанией IBM VSAM и ISAM. Сейчас для этого применяются системы управ­ления базами данных (СУБД), большей частью реляционные. Проектирование таких систем и их сопровождение превратились в отдельные специализированные дисциплины.

Множество пользователей обращаются к данным параллельно. Как правило, их коли­чество не превышает сотни, но для систем, размещенных в среде Web, этот показатель возрастает на несколько порядков. Как гарантировать возможность одновременного дос­тупа к базе данных для всех, кто имеет на это право? Проблема остается даже в том случае, если речь идет всего о двух пользователях: они должны манипулировать одним элементом данных только такими способами, которые исключают вероятность возникновения ошибок. Большинство обязанностей по управлению параллельными заданиями прини­мает на себя диспетчер транзакций из состава СУБД, но полностью избавить прикладные системы от подобных забот программистам чаще всего не удается.

Если объемы данных столь велики, в приложении должно быть предусмотрено и множество различных вариантов окон экранного интерфейса. Вполне обычна ситуация, когда программа содержит несколько сотен окон. Одни пользователи работают с корпо­ративным приложением регулярно, другие обращаются к нему эпизодически, но пола­гаться на их техническую осведомленность в любом случае нельзя. Поэтому данные долж­ны допускать возможность представления в самых разных формах, удобных для пользо­вателей всех категорий. Фокусируя внимание на вопросах взаимодействия пользователей с приложением, нельзя упускать из виду и тот факт, что многие системы характеризуются высокой степенью пакетной обработки данных.

Корпоративные приложения редко существуют в изоляции. Обычно они требуют ин­теграции с другими системами, построенными в разное время и с применением различ­ных технологий (файлы данных COBOL, CORBA, системы обмена сообщениями). Время от времени корпорации стараются провести интеграцию собственных подсистем с при­менением некоторой универсальной технологии. Поскольку обычно такой работе конца-краю не видно, все сводится к применению нескольких различных методов интеграции. Ситуация усугубляется, если интеграции подлежит информация из совершенно разно­родных источников.

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

Не стоит забывать и о том, что принято обозначать расплывчатым термином бизнес-логика. Я нахожу его забавным, поскольку могу припомнить только несколько вещей, менее логичных, нежели так называемая бизнес-логика. При создании операционной системы, например, без строгой логики не обойтись. Но бизнес-правила, которые нам сообщают, следует принимать такими, какие они есть, поскольку без серьезного полити­ческого вмешательства их не преодолеть. Мы вынуждены иметь дело со случайными на­борами странных условий, которые сочетаются между собой самым непредсказуемым образом. Определенно известно только одно: вся эта мешанина еще и изменяется во вре­мени. Разумеется, тому есть какие-то причины. Так, например, выгодный клиент может выговорить для себя особые условия оплаты кредита, отвечающие срокам поступления средств на его расчетный счет. И пару тысяч таких вот частных случаев способны сделать бизнес-логику совершенно нелогичной, а соответствующее программное приложение — запутанным и не поддающимся восприятию с позиций здравого смысла.

Некоторые интерпретируют понятие "корпоративное приложение" как синоним тер­мина "большая система". Однако важно понимать, что не все подобные приложения на самом деле "велики", хотя их вклад в деятельность "корпорации" может быть довольно весомым. Многие считают, что "малые" системы причиняют меньше беспокойства, и до определенной степени это справедливо. Крах небольшого приложения обычно менее ощутим, нежели сбой крупной системы. Впрочем, я думаю, что пренебрегать совокуп­ным влиянием многих мелких систем непозволительно. Если вы в состоянии предпри­нять какие-либо действия, которые помогут улучшить функционирование небольших приложений, общий результат может оказаться весьма существенным, так как нередко значимость системы далеко превосходит ее "размеры". И еще. При любой возможности старайтесь превратить "большой" проект в "малый" за счет упрощения принимаемой ар­хитектуры и офаничения множества реализуемых функций.

Типы корпоративных приложений

При обсуждении способов проектирования корпоративных приложений и вариантов выбора типовых решений важно понимать, что двух одинаковых приложений просто не существует и различия в проблемах обусловливают необходимость применения различ­ных средств достижения поставленных целей. Меня захлестывает волна протеста, когда я слышу советы делать что-либо "только так и не иначе". По моему убеждению, наиболее любопытная и дерзкая часть проекта, как правило, связана с анализом пространства аль­тернатив и взвешенным выбором в пользу одной из них. Рассмотрим для примера три до­вольно типичных варианта корпоративных приложений.

Первый — электронная коммерческая система типа "поставщик—потребитель" ("bu­siness to customer" — В2С); к ней обращаются с помощью Web-обозревателей, находят нужные товары, вводят необходимую информацию и осуществляют покупки. Подобная система предназначена для обслуживания большого количества пользователей одновре­менно, поэтому проектное решение должно быть не только эффективным по критерию использования ресурсов, но и масштабируемым: все, что требуется для повышения про­пускной способности такой системы, — это приобретение дополнительного аппаратного обеспечения. Бизнес-логика подобного приложения довольно прямолинейна: прием за­каза, незамысловатые финансовые операции и отправка уведомления о доставке. Необ­ходимо, чтобы каждый мог быстро и легко обратиться к системе, поэтому Web-интер­фейс должен быть предельно простым и доступным для воспроизведения с помощью максимально широкого диапазона обозревателей. Информационный источник включает базу данных для хранения заказов и, возможно, некий механизм обмена данными с сис­темой складского учета для получения информации о наличии товаров и их отгрузке.

Сопоставьте рассмотренную систему с программой, автоматизирующей учет согла­шений имущественного найма. В некоторых аспектах последняя намного проще, нежели приложение электронной коммерции В2С, поскольку круг ее пользователей, работаю­щих одновременно, существенно уже — скажем, не более сотни. В чем она сложнее, так это в бизнес-логике. Составление ежемесячных счетов за аренду, обработка различных событий (таких, как преждевременный возврат имущества и просроченный платеж), проверка вводимой информации при заключении договора — все это достаточно трудо­емкие функции. В индустрии лизинга успех во многом определяется выбором одного из множества вариантов, незначительно отличающихся от классических примеров сделок, которые заключались в прошлом. И бизнес-логика этой предметной области весьма сложна, поскольку правила игры слишком свободны.

Подобная система отличается сложностью и в отношении пользовательского интер­фейса. Зачастую требования к интерфейсу таковы, что их нельзя удовлетворить только средствами HTML; приходится пользоваться интерфейсными средствами, предоставляе­мыми более традиционной моделью "толстого" клиента. Усложнение процедур взаимодей­ствия пользователя с программой вынуждает применять и более изощренные варианты транзакций: например, оформление договора аренды может продолжаться несколько ча­сов, и все это время пользователь выполняет одну логическую транзакцию. Схема базы данных также заметно расширяется и может включать несколько сотен таблиц и соеди­нений с внешними пакетами, предназначенными для оценки стоимости активов и цены аренды.

В качестве третьего примера рассмотрим простейшую систему учета расходов для не­большой компании, обладающую самой незатейливой логикой. Пользователи системы (их всего несколько) могут обращаться к ней с помощью стандартизованного Web-интерфейса. Источником информации является база данных с двумя-тремя таблицами. (г;иако даже к такой тривиальной задаче нельзя относиться легкомысленно. Вероятно, систему потребуется сконструировать очень быстро, в то же время принимая во внима­ние возможности ее роста за счет будущего включения модулей финансового анализа и налогообложения, генерации отчетной документации, взаимодействия с другими прило­жениями и т.п. Попытки применения тех же архитектур, которые приемлемы в двух пре­дыдущих случаях, в данной ситуации чреваты существенным замедлением темпов работ. Если программа обеспечивает получение тех или иных преимуществ (а такими должны быть все корпоративные приложения), задержка с ее внедрением в эксплуатацию означает прямые финансовые потери. Поэтому отнюдь не хотелось бы принимать решения, кото­рые воспрепятствуют развитию системы в дальнейшем. Однако, если оснастить систему дополнительными службами с прицелом на будущее, но сделать это неправильно, новый уровень сложности как раз и может затруднить ее эволюцию, приостановить процесс внедрения и отсрочить получение преимуществ. Несмотря на "незначительность" каж­дого отдельного приложения такого класса, все они, рассматриваемые в совокупности, способны оказать серьезное положительное (или отрицательное) влияние на показатели деятельности "корпорации".

Реализация каждой из трех упомянутых систем сопряжена с трудностями, но трудно­сти эти в каждом случае специфичны. Как следствие, нам не удастся предложить единую архитектуру, приемлемую во всех ситуациях. Выбирая архитектуру и варианты проекти­рования, следует принимать во внимание особенности конкретной системы. Вот почему на страницах книги вы не найдете универсальных рецептов, пригодных на все случаи жизни. Напротив, наличие многих типовых решений не избавляет вас от необходимости выбора из нескольких альтернатив. Даже если вы отдадите предпочтение какому-либо решению, вам, возможно, придется модифицировать его, чтобы приспособить к реаль­ным условиям. Нельзя работать над проектом корпоративного приложения бездумно. Все, на что способна какая бы то ни было книга, — это дать некую исходную информа­цию, оттолкнувшись от которой вы можете начать мыслить самостоятельно.

Все сказанное применимо и к выбору инструментальных средств программирования. Приступая к работе, следует — думаю, это очевидно — максимально сузить круг инстру­ментов. Однако не стоит забывать, что разные средства проявляют свои лучшие качества в разных ситуациях. Избегайте использования инструментов, предназначенных для дру­гих целей, иначе они не только не принесут пользы, но и причинят вред.

Производительность

Многие архитектурные решения напрямую связаны с аспектами производительности (performance) системы. Чтобы говорить о производительности, я предпочитаю увидеть работающую систему, измерить ее характеристики и, учитывая полученные результа­ты, применить строго определенные процедуры оптимизации. Однако некоторые ар­хитектурные решения определяют параметры производительности таким образом, что устранение возможных проблем средствами оптимизации затрудняется. Именно поэтому к принятию таких решений всегда стоит подходить с большой ответственностью.

В книгах, подобных этой, трудно толковать вопросы производительности, поскольку любой совет не может приниматься как незыблемый факт до тех пор, пока не будет ис­пытан в конкретных условиях. Я слишком часто сталкивался с ситуациями, когда вари­ант проектирования принимался или отвергался по причинам, связанным с производи­тельностью, а затем замеры параметров осуществлялись в реальной среде и полностью опровергали прежние выводы.

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

Существует одно важное обстоятельство: значительное изменение конфигурации приложения способно перечеркнуть все ранее установленные факты, касающиеся вопро­сов производительности. Поэтому, обновив версию виртуальной машины, СУБД, аппа­ратного обеспечения или любого другого компонента системы, вы обязаны заново про­вести оптимизацию и убедиться, что принятые меры возымели действие. Нередко бывает и так, что приемы оптимизации, с успехом применявшиеся в прошлом, в новых условиях вызывают обратный эффект.

Еще одна проблема любой дискуссии по вопросам производительности состоит в том, что многие термины употребляются и интерпретируются непоследовательно и неверно. Наиболее характерный пример — понятие, описывающее способность приложения к мас­штабированию (scalability): тут можно назвать не менее пяти-шести различных толкова­ний. Ниже рассмотрены термины, применяемые в этой книге.

Время отклика (response time) — промежуток времени, который требуется системе, чтобы обработать запрос извне, подобный щелчку на кнопке графического интерфейса или вызову функции API сервера.

Быстрота реагирования (responsiveness) — скорость подтверждения запроса (не путать с временем отклика— скоростью обработки). Эта характеристика во многих случаях весьма важна, поскольку интерактивная система, пусть даже обладающая нормальным временем отклика, но не отличающаяся высокой быстротой реагирования, всегда вызы­вает справедливые нарекания пользователей. Если, прежде чем принять очередной запрос, система должна полностью завершить обработку текущего, параметры времени отклика и быстроты реагирования, по сути, совпадают. Если же система способна под­твердить получение запроса раньше, ее быстрота реагирования выше. Например, приме­нение динамического индикатора состояния процесса копирования повышает быстроту реагирования экранного интерфейса, хотя никак не сказывается на значении времени отклика.

Время задержки (latency) — минимальный интервал времени до получения какого-либо отклика (даже если от системы более ничего не требуется). Параметр приобретает особую важность в распределенных системах. Если я "прикажу" программе ничего не де­лать и сообщить о том, когда именно она закончит это "ничегонеделание", на персо­нальном компьютере ответ будет получен практически мгновенно. Если же программа выполняется на удаленной машине, придется подождать, вероятно, не менее нескольких секунд, пока запрос и ответ проследуют по цепочке сетевых соединений. Снизить время задержки разработчику прикладной программы не под силу. Фактор задержки — главная причина, побуждающая минимизировать количество удаленных вызовов.

Пропускная способность (thmughput) — количество данных (операций), передаваемых (выполняемых) в единицу времени. Если, например, тестируется процедура копирования файла, пропускная способность может измеряться числом байтов в секунду. В корпора­тивных приложениях обычной мерой производительности служит число транзакций в секунду (transactions per second — tps), но есть одна проблема — транзакции различаются по степени сложности. Для конкретной системы необходимо рассматривать смесь "типо­вых" транзакций.

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

Загрузка (load) — значение, определяющее степень "давления" на систему и изме­ряемое, скажем, количеством одновременно подключенных пользователей. Параметр загрузки обычно служит контекстом для представления других функциональных ха­рактеристик, подобных времени отклика. Так, нередко можно слышать выражения наподобие следующего: "время отклика на запрос составляет 0,5 секунды для 10 пользо­вателей и 2 секунды для 20 пользователей".

Чувствительность к загрузке (load sensitivity) — выражение, задающее зависимость времени отклика от загрузки. Предположим, что система А обладает временем отклика, равным 0,5 секунды для 10-20 пользователей, а система В — временем отклика в 0,2 се­кунды для 10 пользователей и 2 секунды для 20 пользователей. Это дает основание утвер­ждать, что система А обладает меньшей чувствительностью к загрузке, нежели система В. Можно воспользоваться и термином ухудшение (degradation), чтобы подчеркнуть факт меньшей устойчивости параметров системы В.

Эффективность (efficiency) — удельная производительность в пересчете на одну еди­ницу ресурса. Например, система с двумя процессорами, способная выполнить 30 tps, более эффективна по сравнению с системой, оснащенной четырьмя аналогичными про­цессорами и обладающей продуктивностью в 40 tps.

Мощность (capacity) — наибольшее значение пропускной способности или загрузки. Это может быть как абсолютный максимум, так и некоторое число, при котором величи­на производительности все еще превосходит заданный приемлемый порог.

Способность к масштабированию (scalability) — свойство, характеризующее поведение системы при добавлении ресурсов (обычно аппаратных). Масштабируемой принято счи­тать систему, производительность которой возрастает пропорционально объему приоб­щенных ресурсов (скажем, вдвое при удвоении количества серверов). Вертикальное мас­штабирование (vertical scalability, scaling up) ~ это увеличение мощности отдельного сервера (например, за счет увеличения объема оперативной памяти). Горизонтальное масштабирование (horizontal scalability, scaling out)— это наращивание потенциала систе­мы путем добавления новых серверов.

Следует отметить, что существует проблема: выбор проектного решения оказывает не одинаковое влияние на факторы производительности. Рассмотрим для примера две программные системы, работающие на однотипных серверах: первая (назовем ее Sword-fish) обладает мощностью в 20 tps, а вторая, Camel, — в 40 tps. Какая система более про­изводительна? Какая из них в большей мере способна к масштабированию? На основа­нии имеющейся информации мы не можем ответить на вопрос, касающийся возможно­стей масштабирования. Единственное, что понятно, — Camel более эффективна в кон­фигурации с одним сервером. Добавим еще один сервер и допустим, что производитель­ность Swordfish теперь исчисляется величиной 35 tps, а продуктивность Camel возрастает до 50 tps. Мощность Camel все еще выше, но параметры масштабирования Swordfish вы­глядят предпочтительнее. Продолжив пополнение систем серверами, мы обнаружим, что производительность Swordfish всякий раз возрастает на 15 tps, в то время как аналогич­ный показатель Camel — только на 10 tps. Полученные данные позволяют прийти к за­ключению, что Swordfish лучше поддается горизонтальному масштабированию, хотя Camel демонстрирует большую эффективность при количестве серверов, меньшем пяти.

Проектируя корпоративную систему, часто следует уделять больше внимания обеспе­чению средств масштабирования, а не наращиванию мощности или повышению эффек­тивности. Производительность масштабируемой системы всегда удастся увеличить, если такая потребность действительно возникнет. Помимо того, добиться возможности мас­штабирования проще. Нередко проектировщикам и программистам приходится извора­чиваться, чтобы увеличить мощность системы для конкретной аппаратной конфигура­ции, хотя, вероятно, было бы дешевле приобрести дополнительное оборудование. Если, скажем, система Camel стоит дороже, чем Swordfish, и разница в стоимости покрывает цену нескольких серверов, то Swordfish обойдется дешевле даже в том случае, когда необ­ходимый уровень производительности составляет всего 40 tps. Сейчас модно сетовать на возрастающие требования к аппаратному обеспечению, непременно сопровождающие выпуск очередных версий популярных программ, и я присоединяю свой голос к хору возмущенных потребителей, когда при установке последней версии Word мне велят об­новить конфигурацию компьютера. Но использовать более новое оборудование зачастую просто выгоднее, нежели заставлять программу "крутиться" на устаревшей технике. Если корпоративное приложение поддается масштабированию, добавить несколько серверов дешевле, чем приобрести услуги нескольких программистов.

Типовые решения

Концепция типовых решений (patterns) известна уже давно, и мне не хотелось бы вы­ступать здесь с очередным очерком истории ее развития. Однако я не собираюсь упускать удачную возможность рассказать о собственной точке зрения на предмет.

Какого бы то ни было общеупотребительного определения типового решения не су­ществует. Вероятно, лучше всего начать с цитаты из книги Кристофера Александера (Christopher Alexander), вдохновившей не одно поколение энтузиастов: "Каждое типовое решение описывает некую повторяющуюся проблему и ключ к ее разгадке, причем таким образом, что вы можете пользоваться этим ключом многократно, ни разу не придя к од­ному и тому же результату" [1]. К. Александер — архитектор, и говорит он о сооружени­ях, но данное им определение, по моему мнению, прекрасно подходит и для типовых ре­шений в программировании. Основа типового решения — подход, достаточно общий и эффективный для того, чтобы обеспечить преодоление периодически возникающих про­блем определенной природы. Типовое решение можно воспринимать и в виде некоторой рекомендации: искусство создания типовых решений состоит в вычленении и кристал­лизации таких относительно независимых рекомендаций, которые допускают возмож­ность более или менее обособленного применения.

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

Как только вы ощутили потребность в типовом решении, следует подумать, как при­менить его в конкретных обстоятельствах. Основная особенность типовых решений со­стоит в том, что ими нельзя пользоваться слепо; вот почему многие инструментальные оболочки, основанные на типовых решениях, и получаемые с их помощью результаты так убоги. Мне кажется, что типовые решения можно сравнить с полуфабрикатом: чтобы получить удовольствие от еды, вам придется довершить приготовление блюда по собст­венному рецепту. Используя типовое решение, я "поджариваю" его всякий раз по-другому и наслаждаюсь отменным и своеобразным вкусом.

Каждое типовое решение относительно независимо от других, но говорить об их пол­ной взаимной изолированности нельзя. Зачастую одно решение непосредственно приво­дит к другому либо может применяться только в контексте некоего базового решения. Так, например, решение наследование с таблицами для каждого класса (Class Table Inheritance) обычно используется совместно с решением модель предметной области (Domain Model). Границы между решениями не отличаются четкостью, но я пред­принял все возможное для того, чтобы представить каждое решение в самодостаточной форме. Поэтому, если кто-то предлагает "применить решение единица работы (Unit of Work)", будет довольно проследовать к соответствующему разделу книги, не читая всего остального.

Если у вас есть опыт проектирования корпоративных приложений, многие типовые решения, о которых речь пойдет ниже, вам, вероятно, уже знакомы. Надеюсь, вы не бу­дете разочарованы (ваше возможное недоумение я уже пытался предупредить в преди­словии). Типовые решения — не научные открытия; они в большой мере представляют собой обобщения результатов, накопленных в соответствующей области. Поэтому более уместно говорить о "поиске" типового решения, а не о его "изобретении". Моя роль со­стоит в обнаружении общих подходов, выделении главного и оформлении полученных выводов. Ценность типового решения для проектировщика заключается не в новизне идеи; главное — это помощь в описании и сообщении этой идеи. Если собеседники осве­домлены о том, что именно представляет собой интерфейс удаленного доступа (Remote Facade), одному из них достаточно произнести фразу "этот класс является интерфей­сом удаленного доступа", чтобы расставить все точки над /. А новичку вы сможете подска­зать, что, дескать, "для этой задачи больше подойдет объект переноса данных (Data Transfer Object)", и он, обратившись к странице 646 настоящей книги, найдет все не­обходимое. Классификация типовых решений способствует созданию словаря проекти­ровщика, а потому выбору их названий следует уделять должное внимание.

В главе 18, "Базовые типовые решения", упомянут ряд базовых типовых решений универсального характера; они используются при описании всех остальных решений, ориентированных на корпоративные приложения.

Структура типовых решений

Каждому автору приходится выбирать форму представления типовых решений. Неко­торые принимают за основу подходы, которые изложены в классических книгах, посвя­щенных типовым решениям (например, [1, 20, 34]). Другие изобретают что-то свое. Я долго не мог составить твердое мнение о том, в чем же состоит превосходство того или иного варианта над остальными. С одной стороны, меня не могло удовлетворить нечто столь же краткое, как форма из [20]; с другой — описание каждого решения должно было бы уместиться в пределах небольшого раздела книги. Ниже описан компромиссный ре­зультат, к которому я пришел после длительных размышлений.

Первое — название решения. Выбор подходящего имени довольно важен, поскольку одна из целей, преследуемых при использовании типовых решений, состоит в создании словаря, который облегчает взаимодействие проектировщиков в ходе выполнения проек­та. Например, если я сообщу, что мой Web-сервер реализован на основе контроллера за­просов (Front Controller) и представления с преобразованием (Transform View), и эти типовые решения будут знакомы и вам, у вас наверняка сложится однозначное пред­ставление об архитектуре приложения.

За именем следуют аннотация и эскиз. Аннотация состоит из одного-двух предложе­ний и содержит краткое описание типового решения; эскиз служит визуальной иллюст­рацией решения и часто (хотя не всегда) представляет собой UML-диаграмму. Аннота­ция и эскиз предназначены для создания наглядного "образа" решения и облегчают его запоминание. Если у вас уже есть решение (в том смысле, что оно вам известно, хотя, возможно, вы не знаете его названия), аннотация и эскиз — это, пожалуй, все, с помо­щью чего можно его быстро отыскать.

Далее вкратце обозначается проблема, которая стимулировала появление типового решения. Если таких проблем несколько, выбирается более характерная. Затем приво­дятся два обязательных раздела.

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

Раздел Назначение содержит сведения о том, при каких обстоятельствах следует при­менять типовое решение, а также о его сравнительных преимуществах и недостатках. Многие из решений, упомянутых в этой книге, взаимозаменяемы: таковыми являются, скажем, контроллер страниц (Page Controller) и контроллер запросов (Front Controller). Часто одинаково пригодными оказываются несколько решений, поэтому, обнару­жив одно из них, я спрашиваю себя, в каких случаях им не стоило бы пользоваться. Ответ на этот вопрос нередко подводит меня к выбору альтернативного решения.

В разделе Дополнительные источники информации приводятся ссылки на отдельные библиографические источники, имеющие отношение к обсуждаемой теме. Внимание ог­раничивается тем материалом, который, как мне кажется, способен оказать наибольшую помощь в освоении особенностей типового решения. Из рассмотрения исключены ис­точники, не содержащие ничего нового, работы, с которыми я не знаком лично, публи­кации в редких или экзотических изданиях, а также нестабильные и сомнительные ги­перссылки.

В конце раздела, посвященного тому или иному решению, по мере необходимости могут быть приведены простые примеры использования, проиллюстрированные фраг­ментами кода на языках Java и/или С#. Выбор языков программирования (это уже отме­чалось в предисловии) обусловлен вероятными предпочтениями большинства читателей. Очень важно понимать, что любой пример не есть типовое решение. При реализации решения оно не будет выглядеть точно так же, как пример, поэтому примеры не следует трактовать как некую разновидность макросов. Я намеренно пытался упрощать их на­столько, насколько это было возможно, так что соответствующие типовые решения предстанут перед вами в такой прозрачной форме, какую я только мог вообразить. Стре­мясь к простоте, я силился продемонстрировать в примерах сущность типовых решений и старался избегать деталей, которые могли бы отвлечь внимание от главного. Все част­ные проблемы опущены, но имейте в виду, что они могут быстро всплыть в условиях конкретной конфигурации системы. Вот почему типовые решения всегда следует испы­тывать на практике.

Вместо рассмотрения одного или нескольких сквозных примеров я решил прибегнуть к простым независимым примерам. Последние легче воспринимать, хотя не всегда по­нятно, как использовать совместно. Сквозной пример демонстрирует проблемы в их свя­зи, но не удобен для быстрого знакомства с природой конкретного решения. Я не исклю­чаю возможности компромисса, но мне найти его не удалось.

Код примеров написан так, чтобы обеспечить простоту восприятия. В результате не­которые вещи остались, что называется, "за кадром" (в частности, так произошло с об­работкой ошибок— вопросом, недостаточно хорошо проработанным мною для того, чтобы предложить здесь какое-либо типовое решение). Каждый пример обставлен слиш­ком большим количеством упрощающих условий, и поэтому я посчитал нецелесообраз­ным обеспечить возможность загрузки образцов кода с моего Web-сайта.

Ограничения предлагаемых в книге типовых решений

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

Представленные здесь сведения касаются всех типовых решений в области корпора­тивных приложений, известных мне на данный момент. Я не собираюсь утверждать, что полностью осведомлен обо всех их разновидностях и взаимозависимостях. Книга отражает уровень моего сегодняшнего опыта с учетом тех знаний, которые были приоб­ретены в процессе ее написания. Но мысль не стоит на месте, и опыт, разумеется, будет пополняться и после выхода книги в свет. Программирование — бесконечный процесс, и в нем нет предела совершенству.

Собираясь воспользоваться типовыми решениями, не забывайте, что они только от­правная точка, а не пункт назначения. Ни один автор не в состоянии предвидеть всего многообразия форм и вариантов программных проектов. Я написал эту книгу, чтобы по­мочь вам сдвинуться с места, так что извлекайте уроки. Помните, что любое типовое ре­шение — это начало трудного пути, и только вам решать, стоит ли брать на себя ответст­венность (и не лишать себя удовольствия) пройти его до конца.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]