Реализация собственной СУБД с ООП (Рейтинг: +2)

Печать RSS
Данная статья обещает быть очень емкой, потому постараюсь разбить ее на удобные части. Статья затрагивает многие аспекты объектно-ориентированного подхода в программировании (ООПП), такие как Уровни абстракции, Инкапсуляция, Полиморфизм,Наследование, Шаблоны проектирования и так далее. Чтобы не запутаться во всем этом, я постараюсь дать краткие сведения о том аспекте, который я применяю, при первом его упоминании, а так же аргументирую его использования и плюсы в каждом конкретном случае.
О чем собственно статья? Многие из нас рано или поздно сталкивались с необходимостью хранения данных. Все начинается с файловой базы данных (ФБД), затем перетекает в наиболее простые и удобные системы управления базами данных (СУБД), а в наиболее сложных случаях мы переключаемся на дорогие и запутанные коммерческие СУБД от Microsoft и Oracle. Как ни странно, мне понадобилось реализовать удобную систему хранения данных в ФБД, так как планировалось установить полноценную систему с СУБД уже после установки первоначального ядра, а необходимость в сохранении данных к тому времени уже была.
Задачи и первые наброски
Усевшись перед компьютером, я открыл старый добрый блокнот и быстренько набросал требования к будущей системе. Если вспомнить наставления Крэга Лармана в его книге «Применение UML и шаблонов проектирования», то я подошел к начальному этапу с точки зрения процедурного подхода, а именно выделил функциональные, а не пользовательские требования. Объясню подробнее. При написании крупных систем очень удобно начинать с определения конечных пользователей этих систем, в данном случае конечными пользователями будут я и другие программисты, работающие с моей системой. После этого нужно определить их задачи, которые решаются с помощью разрабатываемой системы. В моем случае это удобный механизм работы с файлами как с реляционными таблицами, а так же простота и гибкость реализации (время поджимало). Почему же я отбросил этот важный шаг? Дело в том, что система довольно проста и ее будущий интерфейс будет позаимствован у существующих на момент проектирования СУБД, потому придаваться сложной проработке зачатков мысли я не стал. Здесь можно заметить важную особенность всех советов и наставлений «Гуру программирования» - ни один совет не является обязательным требованием, и следование ему в той или иной ситуации это умение, приходящее с опытом. Как правило, все советы и наставления требуют больше времени на этапе проектирования и реализации, но экономят его в дальнейшем за счет тех или иных приемов. Важно подробно разобраться в этих приемах и понять когда они будут работать, а когда лучше поступить по старинке. В моем случае ФБД это уже решенная другими программистами задача и заново изобретать ее интерфейс или реализацию, это лишняя трата времени и сил, ведь вряд ли я что-то изменю, не это моя цель. Умейте правильно применять те или иные техники, а не следуйте им безоговорочно.
И так задачи, поставленные мной для данной системы:
1. Возможность записывать, удалять, изменять и получать данные из таблицы;
2. Возможность работы с несколькими таблицами;
3. Приемлемая скорость работы;
4. Возможность расширять полученную систему;
5. Удобный и понятный интерфейс.
Заметьте, задачи никак не содержат в себе ни слово о реализации. Так, задача (3) могла бы быть разделена на несколько подзадач:
3.1. Использование индексации;
3.2. Кэширование запросов;
На этапе первичного осмысления важно отказаться от реализации, и задуматься только о задачах. Другими словами думайте о том, что нужно решить с помощью системы, а не о том, как это решить. Рассматривайте этот этап с позиции заказчика. Вы не знаете, как именно эта система будет работать, вы лишь знаете, что она должна вам дать, какие задачи решать.
Сейчас мы смотрим на систему с наиболее высокого уровня абстракции, и видим только самые общие ее части. Пришло время узнать о наиболее важном инструменте ООПП – абстракцией и ее уровнями.
Абстракция
Абстракция – это способ абстрагирования, ухода от всего лишнего по средствам взгляда на проблему с более «высокого» уровня. Так, мы будем рассматривать в нашей ФБД и конкретные файлы, и механизмы доступа к ним, и способы кэширования запросов, и организации структуры и так далее и так далее. Все это разные уровни абстракции, каждый из которых ограничивает наш взор теми или иными областями. Далее мы рассмотрим абстракцию на конкретных примерах, и при необходимости, возвращайтесь к этому отступлению.
Определение элементов системы
Сейчас мы еще не знаем, из чего будет состоять система, какие классы она будет содержать и как они будут связаны, мы лишь определились с кругом задач, которые должны решить. Ни пора ли нам взяться за структуру системы? Самое время!
Для успешного функционирования нашей файловой базы данных, нам понадобятся следующие знания:
* Файл – это структура, позволяющая хранить данные, связав их именем. Другими словами к данным, записанным в файл, мы сможем получить доступ, если знаем имя и расположение файла.
* Реляционная таблица – это файл, данные которого записываются определенным образом, согласно структуре таблицы.
* База данных (БД) – это совокупность данных, хранящихся определенным образом.
* Реляционная база данных (РБД) – это база данных, в которой данные хранятся в виде совокупности значений столбцов таблицы, которые образуют записи.
* Файловая реляционная база данных – это (в данном контексте) база данных, которая использует любые файлы как реляционные таблицы, если они соответствуют заданной структуре.
Как видите, каждое знание рассматривается в совершенно разных уровнях детализации. Так, файл это наиболее низкий уровень абстракции. Механизм взаимодействия с файлами должен уметь только записывать и считывать их них данные, при этом ни о какой реляционной организации нет речи. Более низкие уровни абстракции не знают о том, как они будут использоваться более высокими уровнями, потому они лишь предоставляют удобный интерфейс и выполняют довольно узкий круг функций. С другой стороны реляционная таблица включает в себя возможность организации данных в определенной структуре, а так же чтения этих данных определенным образом (записями или отдельными полями). Это уже более высокий уровень абстракции, который использует в качестве инструмента более низкие уровни (уровень файлов, для чтения и записи), при этом дополняя их новыми возможностями. Напоминает наследование в ООП, не правда ли? В действительности это агрегация, о которой мы поговорим чуть позже. Важно запомнить, что классы более высокого уровня абстракции могут использовать классы более низкого уровня, но не наоборот! Это очень важно, так как позволяет ограничить сложность системы за счет принципа черного ящика. Представьте, что при написании класса «Файл» нам пришлось бы задумываться о том, как будет реализован класс «Реляционная таблица»? Было бы очень сложно хранить в голове всю эту информацию, с учетом того, что таких связей может быть огромное множество. Запрет использования классами низкого уровня абстракции классов с более высоким уровнем позволяет нам беспокоиться, при реализации первых, только о решении простых, прикладных задач, а не задумываться о том, как этот класс будет использоваться другими. С другой стороны, важно, чтобы классы более низкого уровня абстракции давали исчерпывающий набор инструментов в своем интерфейсе, чтобы не пришлось их дополнять при использовании другими классами. Так, класс «Файл» мог бы включать такие функции, как: ЗаписьВФайл, ЧтениеИзФайла – но не позволять блокировать доступ к файлам с помощью функций flock. Возможно, это было бы достаточно на какое-то время, но в конечном итоге нам все же пришлось бы добавить эту возможность в класс работы с файлами. Не забывайте, на каком уровне абстракции вы находитесь при проектировании классов, это позволит вам забыть о некоторых не нужных на данном уровне деталях.
Теперь пора определить основные классы, которые нам понадобятся для работы. Упорядочу их по уровням абстракции:
1 уровень
1. Класс работы с файлами;
2. Класс работы с каталогами.
2 уровень
1. Класс для работы с файлами как с реляционными таблицами без сложных запросов. Является связывающим звеном всех элементов реляционной таблицы;
2. Класс-структура файловой реляционной таблицы, для вынесения задачи структурирования данных в этот класс;
3 уровень
1. Класс для работы с файлами реляционной таблицы с использованием сложных запросов;
2. Класс для работы с директориями как с хранилищами файлов реляционных таблиц.
Сразу замечу, что это совсем не исчерпывающий список используемых мной для решения поставленных задач классов, но на данном этапе этих классов мне достаточно, потому что, я только начинаю задумываться о реализации.

Работа с файлами
Начнем с реализации класса, позволяющего удобно работать с файлами. Выделим отдельный пакет «fileSystem», который будет включать классы, работающие с файловой системой на низком уровне абстракции. Теперь рассмотрим механизм работы с файлами.
Для начала работы файл необходимо открыть, причем в определенном режиме (r, r+, w, w+, a, a+) . Так же нужно задуматься о трансляции символов переноса строки для различных ОС. Windows записывает перенос строки в два символа, в то время как UNIX использует для этого один символ. Казалось бы, не существенно, но дальше вы поймете, что это очень важный момент. После открытия файла нужно подумать о том, не открыл ли этот файл другой программный поток, и не мешаем ли мы другой части программы, работающей с этим же файлом, своими действиями, другими словами нужно блокировать и вовремя разблокировать файл. Когда мы получаем доступ к файлу нам нужно либо прочитать из него данные, либо записать их туда. Познакомившись с возможностями PHP в этой области, приходим к функциям записи, чтения, а так же выбора текущей позиции, с которой начинается запись или чтение. После завершения мы должны закрыть файл и, соответственно, снять с него блокировку.
В общем, ничего сложного нет, но даже здесь опытный взгляд заметит очень важную особенность механизмов работы с файлами - пока файл не открыт, число возможных с ним действий ограничено. Класс «Файл» имеет различные состояния, которыми определяется его поведения. Что это за состояния?
1. Начальное состояние или «close». Файл закрыт и разблокирован;
2. Состояние чтения или «openRead». Файл открыт и заблокирован разделяемой блокировкой. Возможно только чтение из файла. Указатель перемещен в начало файла. Режим rb;
3. Состояние записи или «openWrite». Файл открыт и заблокирован исключетльной блокировкой. Возможна только запись в файл. Указатель перемещен в конец файла. Режим r+b.
Выделим функциональные возможности для каждого из состояний:
Состояние close
ОткрытьФайл – метод открывает файл в определенном режиме;
Состояние openRead
ОткрытьФайлВРежимеЗаписи
ЗакрытьФайл
ПрочитатьДанныеИзФайла
УстановитьТекущуюПозицию
ПолучитьТекущуюПозицию
ПолучитьРазмерФайла
ЗаблокироватьФайл
РазблокироватьФайл
Состояние openWrite
ОткрытьФайлВРежимеЧтения
ЗакрытьФайл
ЗаписатьДанныеВФайл
ПерезаписатьФайл
ОтчиститьФайл
УстановитьТекущуюПозицию
ПолучитьТекущуюПозицию
ПолучитьРазмерФайла
ЗаблокироватьФайл
РазблокироватьФайл
Как видите, многие методы в состояниях openRead и openWrite повторяются. Как при этом не дублировать код я объясню несколько позже, а сейчас возьмемся за структуру класса «File». Для начала вспомним паттерн «Состояние».
Пока все
Статья оказалась, как и предполагалась, довольно большой и подробной, из за чего пока завершаю ее написание. Если она окажется достаточно полезной для читателей, то обязательно продолжу, если же она будет обделена вниманием, то продолжать ее не вижу смысла. Пока дам исходники класса «File» и «Directory», а так же все, что с ними связанно. Если кому интересно, можете изучить их реализацию.
Архив: http://upwap.ru/1970088
Пароль: fromVisavi
Добавил:
Рейтинг: +2
Просмотры: 2131
Комментарии (1) »