Разрушая велосипедные фабрики: доступ к базам данных из php. Часть 1

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

Я не буду говорить о том, что какая-то библиотека является самой лучшей, и пользоваться нужно именно ей. Мы все понимаем, что показатель качества, а уж тем более медаль "самая лучшая на свете", выдается каждым программистом той библиотеке, которая наилучшим образом подходит под его комбинацию знаний и потребностей. Многие разработчики по различным причинам начинают создавать свои собственные библиотеки. Я и сам не избежал подобной судьбы, создав за несколько лет множество своих "квадратных велосипедов". Сейчас я смотрю на многие из них с улыбкой, но рад приобретенному опыту, рад тому, что я прошел через все стадии планирования и разработки. Почему программисты создают свои собственные библиотеки для… доступа к БД? А работа с графикой, алгоритмы... Большей частью решаемые нами задачи являются всеобщими, и в мире не так уж много людей, которые сталкиваются с действительно уникальными проблемами, придумывают для них новые решения. Самой плохой причиной, по которой может быть начата разработка собственного "велосипеда" — это "я не разобрался, как работает эта штука, и поэтому решил создать свое". Немногим лучше слова: "такого ни у кого еще не было — я сделаю лучше всех или первее всех". Прежде, чем вы потратите несколько месяцев или более (хорошая библиотека по определению не может быть сделана быстро), я советую четко сформулировать свои потребности и затруднения. Что я хочу, и почему я не могу пользоваться уже существующими решениями, библиотеками? Ответьте на вопрос: будет ли кому-нибудь в целом мире нужно то, что я собираюсь делать? И если вы не можете найти кого-то, кто сказал бы: "да, мне это нужно", то наверняка и вам эта библиотека вскоре окажется ненужной. Вы должны понимать, что создаваемое вами решение должно быть принято другими членами команды, плавно войти в существующий цикл разработки или улучшить его. Так что следует начинать с изучения существующих решений: только поработав и сделав пару коммерческих проектов, вы можете четко сформулировать, чем мне не нравится та, другая, библиотека. Я видел достаточно примеров того, как начинал строиться новый "велосипед" со словами "все существующие решения слишком сложны или содержат много избыточного". А через некоторое время эксплуатации, по мере возникновения все больших требований к библиотеке, этот "велосипед" начинал увеличиваться в размерах и уже напоминал те самые библиотеки, от использования которых отказались в самом начале. И даже гораздо хуже качеством. Например, из-за начальной цели сделать как можно проще и быстрее было принесено в жертву планирование (надеюсь, перед тем, как сесть за клавиатуру, вы нарисовали на большом листе ватмана множество квадратиков, схем, показывающих, как все будет устроено, вы документировали тот набор функций, которые следует реализовать). Так что, бывает, после разработки библиотеки проще выкинуть ее в мусорку и, обогатившись опытом и зная все скрытые ямы, начать работу над следующей версией с нуля.

Не могу умолчать о проблеме "унаследованных решений". Так, создав и внедрив некоторый продукт, вы становитесь его заложником: бизнес вложил деньги и хочет в ближайшие годы зарабатывать на созданной вами библиотеке, а не тратить деньги на "я потренировался и теперь готов сделать что- то хорошее". Каждый продукт требует затрат на внедрение, на обучение персонала. В самом начале компьютерной эпохи рост продаж и "железа", и "софта" был лавинообразным. Каждый год продавалось товаров в разы больше, чем в прошлом году. Тогда действительно можно было сказать: "мы зашли в тупик, разрабатывая прошлую версию, допустили слишком много ошибок, так что следующую версию мы сделаем с нуля, плюнем на совместимость программы со старой версией". Тогда это было оправдано, сейчас — нет: рынок уже сложился, и расшевелить его (я не говорю о компьютерных энтузиастах) крайне тяжело. Например, macromedia/adobe flash — несколько последних версий, несмотря на потраченные годы разработки, носят косметический характер. Добавлялись новые возможности, но старые, устаревшие и давно неактуальные, сохранялись, пока flash не превратился в свалку анахронизмов. И действительно, что бы вы сказали, если бы для каждой новой версии вам приходилось учиться заново? Таких примеров попадания в заложники собственного продукта очень много. Тот самый php, о котором я планирую рассказать — яркий пример попадания в заложники. Разработчики давно просили о нормальной поддержке ООП, о родной поддержке Unicode, о пространствах имен, о стандартизированном framework'е. Увы, в настоящий момент есть множество сайтов, написанных на старых версиях php, старых библиотеках. Выкинуть все архитектурные ошибки, которые тянутся несколько лет, и начать заново? А как быть с множеством этих сайтов, которые перестанут работать? Так что не надейтесь, что вы сможете перейти под новую версию некоторой библиотеки или программы сразу после ее выхода: вас тянут назад "унаследованные решения". Совсем недавно разработчики php официально заявили, что поддержка php версии 4.x этой осенью будет прекращена, и только недавно началась медленная миграция хостеров на новые версии php, местами в принудительном порядке (не буду называть одного из лидеров российского хостинг-рынка, принявшего столь жесткое решение), местами можно выбрать предпочитаемую версию php. Легче с обновлениями состоит дело разве что в некоммерческих open source решениях, где разработчик не продает свой код и не отвечает за убытки клиентов, связанные с ломкой старого цикла программирования и перехода на новый подход.

Еще есть фактор стать заложником "лидов". Если проект разрабатывается командой, из которой сразу уходят несколько ведущих программистов, то удар может быть от "просто тратим время и деньги на вхождение в суть вопроса новых разработчиков". Удар может быть и смертельным (некем заменить, нет толковой документации, кто вообще писал этот код — в нем невозможно разобраться). Ваши клиенты тоже знают об этом, так что, если вы занимаетесь gamedev и хотите продать издателю проект на "собственном невероятно крутом 3d-движке", то вас отфутболят с большей долей вероятности, чем если вы попросите денег на покупку отлаженной технологии у другой компании (пусть даже денег потребуется больше на покупку, чем на разработку). Конечно, если вы работаете в blizzard, то предыдущее предложение к вам не относится. Эти рассуждения характерны и для любой другой сферы — качественный продукт, как я говорил, не может быть сделан на коленке и за пару недель, и срок его эксплуатации тоже не может быть коротким. Я намеренно не рассматриваю вопрос экономической целесообразности: стоит ли купить уже существующую библиотеку или построить что-то свое. Деньги здесь считаются по более сложному правилу: сколько это будет стоить сделать нам, и сколько это стоит, если купить и чуть-чуть подрихтовать. Не забывайте про фактор "сопровождения и доверия".

Одним из наиболее часто используемых приемов в программировании является прием абстрагирования (так же, как и во многих других сферах деятельности). Мы просто отбрасываем как несущественный некий набор факторов и начинаем рассматривать систему как черный ящик. Черный ящик имеет вход и выход, но никто не знает, как он устроен внутри. Мы знаем, что, если нажать на некоторую кнопку или дернуть какой-то рычаг на входе ящика, то загорится какая-то лампочка на выходе — но что происходит при этом внутри, нам неизвестно. Так и некоторая библиотека: мы используем их потому, что не хотим писать собственный код, тратить время. Мы берем готовую библиотеку и рассматриваем ее как черный ящик — если некоторой функции из этой библиотеки подать на вход набор чисел, то она его отсортирует, но как, какой из множества алгоритмов будет для этого использован? Нас это не интересует. И нам это не нужно знать до тех пор, пока ящик работает исправно — пока числа сортируются как должно. Но как только что-то ломается, и ящик работает не так, как ожидалось (например, сортировка работает слишком медленно для больших массивов данных), то мы должны искать специалиста, который будет уметь чинить этот ящик. Увы, мир hitech изменяется быстро, и компания, которая продала вам эту библиотеку, может просто перестать существовать или прекратить поддержку клиентов. И хорошо если перед своей "смертью" она позаботится об оставшихся клиентах — так, недавно Microsoft официально закрыла развитие своей СУБД foxpro: последняя версия выпущена в начале 2005 г. — радует только, что поддержка будет идти до 2010 г. Если вы начали свой путь в it не в середине 90-х — начале 2000-х гг., то, возможно, вы и не слышали о foxpro. А когда-то на нем писались почти все системы учета финансов, складского учета. До сих пор во многих ВУЗах foxpro активно преподается. Несколько лет назад компания Sun (разработчик в том числе языка java) несколько кварталов подряд несла довольно чувствительные финансовые убытки, и пронеслась паника: а что же будет, если Sun разорится, кому достанется java, куда денемся все мы? Я и сейчас с удовольствием наблюдаю за тем, как на программистских форумах периодически проносятся волны паники: "никто не будет делать собственные движки для сайтов — cms bitrix захватит 50% рынка в 2008 г." Никто не хочет использовать delphi, .net для разработки финансовых приложений, складского учета — 1c вытеснит все. Возвращаясь к абстракции. Абстракция хороша тем, что вы не забиваете себе голову ненужными подробностями, как работает что-то. И плоха она тем же. Известный философский закон: "недостатки — это продолжение достоинств". Всякая программная абстракция — лишняя надстройка над чем-то. И она по определению не может быть идеальна всегда. Я исторически начал с java и только потом серьезно (не на уровне институтских лаб) начал писать на c++. Переход с java (со всеми его контейнерными классами, и особенно автоматической сборкой "мусора": выделение и освобождение памяти автоматизировано) к c++ (где даже простые строки были массивами и элементарная операция склейки строк была мучительна, без использования boost или stl) шло очень тяжело. Я говорил товарищам: как вы может писать на этом, я же сделаю на java за пару дней то, над чем вы мучаетесь несколько недель. И я был прав... до тех пор, пока эти мои задачи не стали сложнее, пока мне не потребовалось выжимать все до последней капли из ресурсов компьютера. Тогда я понял, что автоматическая сборка мусора, запустившаяся в ненужный момент — это очень плохо, что работа с памятью (таких удобных и приятных) строк java медленна и избыточна. Время идет: java меняется, сейчас в его составе достаточно улучшений по скорости. C++ тоже меняется — я внимательно присматриваюсь к новому стандарту C++0x ( сайт ). Мне становится тяжело писать уже на java, я хочу еще больше абстракций и высокоуровневости, но я знаю, что "серебряной пули нет". Неплохо пишет о проблеме абстракций великий гуру Джоэль Спольски (Joel Spolsky) "Закон дырявых абстракций" ( сайт ).

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

Паттерны проектирования. Каждая библиотека создается так, чтобы быть примененной в некоторой типовой ситуации и диктует определенное внутреннее устройство вашего кода, использующего данную библиотеку и определенную последовательность действий. Но паттерны — это не библиотеки. Паттерны проектирования отделяют и от алгоритмов тем, что алгоритмы ориентированы на действие (какие шаги нужно совершить, чтобы достичь чего-то), а паттерны проектирования ориентированы на структуру (как это чего-то должно быть архитектурно устроено), хотя граница довольно расплывчата. Паттерн не является законченным решением. Паттерн — это описание типового решения в типовой ситуации. Вы анализируете контекст и условия применения и подбираете некоторый паттерн. Паттерны применяются многократно, но это не значит, что программист не должен выполнить его "шлифовку" — паттерн просто указывает направление, куда "копать дальше". В книжках, посвященных паттернам, любят рисовать наглядные графические схемы. И чтобы здесь не появилась старая проблема — нет унификации, каждый рисует как хочет, — я настоятельно рекомендую купить и прочитать какую-нибудь книжку, посвященную UML (универсальному, унифицированному) языку моделирования, графической нотации. Идея паттернов появилась где- то в 70-х годах (в архитектуре), но широкое распространение получила только в мире компьютерных технологий с выходом в начале 90-х ставших классикой Design Patterns — Elements of Reusable Object-Oriented Software. Паттерны имеют свои имена — это удобно — теперь программисты могут использовать унифицированную терминологию. Хорошо придуманный паттерн дает возможность пользоваться им снова и снова. Потом появились антипаттерны — как узнать, что ваш проект плох, что в нем используются самые плохие решения, и какую методику выхода из сложившегося кризиса применить. Традиционно паттерны делят на "порождающие" (как создавать объекты), "структурные" (как организовать взаимодействие между различными частями вашего кода), "поведенческие" (то, как одна часть вашей программы будет вызывать другую часть, то, как будут обрабатываться и храниться запросы клиента на обработку данных и т.д.), "паттерны параллельного программирования", "паттерны доступа к данным". Паттерны несправедливо критикуют за то, что использующий их программист отвыкает думать, а просто механически подставляет шаблоны, взятые из книжки — забывая о том, что использование паттернов как лучших решений позволяет избежать изобретения очередного "велосипеда". Отдельно выделяют паттерны для "enterprise applications". Исторически первым, кто заговорил о таком семействе паттернов, был Martin Fowler в своей книге Patterns of Enterprise Application Architecture. В качестве сферы применения был выбран j2ee, но идеи могут быть применимы и к любому другому языку — даже к php, заработавшему репутацию "простого языка для начинающих". К таким приложениям предоставляются дополнительные требования:

— Постоянные данные. Данные должны сохраняться между несколькими вызовами программы (легко), даже на протяжении ряда лет (сложнее), также возможно, что за эти годы произойдет большое количество изменений в системе, и вы должны будете делать изменения в модели данных так, чтобы не рухнула вся построенная ранее пирамида (очень тяжело). Данных очень много, поэтому скорость, оптимизация, эффективность — помните о них… — Много пользователей получают доступ к данным одновременно. Это приводит к задачам поддержания целостности и синхронизации доступа. — Нет автономности: enterprise applications нуждаются в интеграции с другими приложениями, написанными для разных платформ и операционных систем, хранящих данные в различных форматах и различных типах хранилищ (реляционные и нет базы данных, xml, проприетарные форматы…). — Различное видение данных. Если у вас несколько подразделений (сотрудников), то каждое из них видит в данных свои нужные для работы моменты. Так, для одного и того же заказа: отдел X он интересует лишь если была отгрузка товара, отдел Y — если был заключен договор, отдел Z — только если... Следовательно, хоть мы и работаем с одной и той же информацией, но нуждаемся в различных способах ее сохранения, разграничения прав доступа и отчетности.

Самый простой способ доступа к данным — шлюз таблицы данных (Table Data Gateway) (подробнее о нем можно узнать на сайт ). Идея в том, что можно после планирования структуры базы данных создать класс, библиотеку — неважно, — которая бы содержала набор функций с конкретным sql-кодом внутри себя и производила типовые операции над таблицей с данными. Например, если у вас есть таблица "товары с полями" (код, название, цена, дата выпуска), то можно создать функции и именно их ("скажи нет прямой работе с sql") вызывать в коде своего приложения:

— добавить_товар(код, название, цена, дата)
— удалить_товар(код)
— обновить_сведения(код, новое_название, новая_цена, новая_дата)
— найти_товары_дешевле_чем(цена)
В следующий раз я продолжу рассказ о паттернах доступа к данным и библиотеках, которыми можно пользоваться в php.

black zorro, black-zorro.jino-net.ru


Компьютерная газета. Статья была опубликована в номере 40 за 2007 год в рубрике программирование

©1997-2024 Компьютерная газета