Больше шаблонов проектирования на Delphi |
- Statistics
- Participants
- Translate into Russian
- Translation result
- 57% translated in draft.
Резюме: Эта сессия состоит из разработки небольшого приложения для чтения и красивого вывода XML и CSV файлов. По ходу дела, разберемся и продемонстрируем использование следующих шаблонов: "Состояние", "Интерпретатор", "Посетитель", "Стратегия", "Команда", "Хранитель" и "Фасад".
Джим Купер — технический руководитель сообщества групп пользователей Великобритании и архитектор компонентов синхронизации для Palm из Tabdee Ltd. Он стал использовать Delphi с первой версии и долгое время интересовался разработками для PDA. Выходец из Австралии, проживающий в Англии (временно, по желанию жены), он пишет статьи для The Delphi Magazine и выступает на конференциях в Европе, Великобритании и Соединенных Штатах. В настоящее время, он является консультантом Falafel Software.
jim@falafelsoft.com
Больше шаблонов проектирования
Jim Cooper Falafel Software
jim@falafelsoft.com www.falafelsoft.com
Введение
Пример
Разбор CSV-файлов
Шаблон "Состояние"
Реализация
Разбор XML-файлов
Шаблон "Интерпретатор"
Грамматика
Реализация
Шаблон "Посетитель"
Документы
Шаблон "Стратегия"
Шаблон "Команда"
Шаблон "Хранитель"
Шаблон "Фасад"
Выводы
Ссылки
Введение
Эта статья предназначена для ознакомления с менее известными шаблонами проектирования из книги "Паттерны проектирования", написанной группой авторов, назвавших себя "Бандой Четырех" (GoF - Gang of Four). Подразумевается, что читатель кое-что уже знает про шаблоны. Вам необходимо иметь четкое понимание объектно-ориентированных техник.
Мы увидим как перейти к использованию шаблонов в разработке на программе-примере, хотя и несколько надуманном. Мы пройдем через процесс проектирования системы, выбор используемого шаблона и его реализации на Delphi.
Обратите внимание, что шаблон не может быть выражен в коде. Приводимые здесь примеры являются лишь некоторыми способами реализации шаблонов, и вполне возможно, а на самом деле и желательно, привести для них и совершенно другие реализации при других обстоятельствах. Некоторые из таких вариантов будут обсуждаться по ходу дела.
Помните: Шаблон - это не шаблон кода, потому не используйте примеры их в этим образом.
Пример
В качестве примера, мы разработаем небольшую часть программы, которая сможет читать и выводить для просмотра XML и CSV файлы (значения, разделяемые запятыми). Программа будет автоматически определять, какой тип файла читается, соответственно делать его разбор и отображать содержимое файла.
Мы пройдем через процесс проектирования, решая какой шаблон использовать. Готовый код будет (будем надеяться!) нагляден, но он определенно сначала таким не был. Некоторые приемы рефакторинга примененные для улучшения кода использованы в качестве примеров в статье "Рефакторинг".
Разбор CSV-файлов
Первое, что мы должны решить — как читать файлы CSV. Чтобы было понятно, о чем мы говорим, CSV-файл представляет собой текстовый файл содержащий строки подобные этой:
Stuff,123,"More stuff, but this can contain commas",,LastField
Обратите внимание, что двойные кавычки используются для обертывания строк, содержащих запятые, и также для пустых полей.
Одним из традиционных способов разбора строк является использование конечного автомата. Великолепная книга "Фундаментальные алгоритмы и структуры данных в Delphi" Джулиана Бакнелла ("Tomes of Delphi: Algorithms and Data Structures") [1] как раз это и содержит. Обратите внимание, что его конкретный пример не обрабатывает возврат каретки или перевод строки, он предполагает что строки уже были разделены и поступают на вход алгоритма для извлечения полей. Статья "Рефакторинг" рассказывает интересную историю о том, как эта небольшая процедура выросла в полноценную реализацию шаблона. Здесь мы просто рассмотрим уже конечный результат.
Шаблон "Состояние"
"Позволяет объекту изменять свое поведение, когда изменяется его внутреннее состояние. Объект будет появляться для изменения своего класса."
GoF
Шаблон "Состояние" используется когда поведение объекта изменяется во время исполнения программы, в зависимости от его состояния. Индикаторы потенциальных возможностей использования шаблона являются длинные case операторы или списки условных операторов ("дурной вкус" switch операторов, для использования стиля рефакторинга). В Delphi (как и в большинстве языков) исходный объект не может в принципе изменять свой класс, поэтому мы должны использовать другие схемы для имитации такого поведения, что мы и рассмотрим.
Участвующими в реализации являются контекст и состояния. Контекстом является интерфейс, предоставляемый клиенту подсистемы, смоделированной по шаблону "Состояние". В нашем случае им будет класс TCsvParser. Клиенты никогда не увидят состояний, тем самым позволяя нам изменять их по своему усмотрению. Единственное, в чем заинтересован интерфейс клиентской подсистемы — извлечение полей из текстовой строки.
Мы сделаем это через использование конечных автоматов (final state machine — FSM). По сути, FSM является моделью множества состояний. В каждом состоянии, конкретные входные данные могут приводить к переходам в другие состояния. Существует два вида особых состояний. Состояние "Start" является состоянием FSM перед началом работы. Состояние "End" появляется, когда обработка завершена и обычно обозначается сдвоенным кругом. FSM для парсера показан ниже:
[http://conferences.embarcadero.com/article/images/32129/3198a.gif]
В шаблоне "Состояние", каждое из состояний становится подклассом базового класса состояния. Каждый подкласс должен реализовать абстрактный метод ProcessChar, который обрабатывает входящий символ и принимает решение о следующем состоянии.
Реализация
Интерфейсная часть исходного кода для шаблона "Состояние" при парсинге CSV-файлов:
unit CsvParser;
interface
uses Classes;
type
TCsvParser = class; // предварительное объявление
TParserStateClass = class of TCsvParserState;
TCsvParserState = class(TObject)
private
FParser : TCsvParser;
procedure ChangeState(NewState : TParserStateClass);
procedure AddCharToCurrField(Ch : Char);
procedure AddCurrFieldToList;
public
constructor Create(AParser : TCsvParser);
procedure ProcessChar(Ch : AnsiChar;Pos : Integer); virtual; abstract;
end;
TCsvParserFieldStartState = class(TCsvParserState)
private
public
procedure ProcessChar(Ch : AnsiChar;Pos : Integer); override;
end;
TCsvParserScanFieldState = class(TCsvParserState)
private
public
procedure ProcessChar(Ch : AnsiChar;Pos : Integer); override;
end;
TCsvParserScanQuotedState = class(TCsvParserState)
private
public
procedure ProcessChar(Ch : AnsiChar;Pos : Integer); override;
end;
TCsvParserEndQuotedState = class(TCsvParserState)
private
public
procedure ProcessChar(Ch : AnsiChar;Pos : Integer); override;
end;
TCsvParserGotErrorState = class(TCsvParserState)
private
public
procedure ProcessChar(Ch : AnsiChar;Pos : Integer); override;
end;
TCsvParser = class(TObject)
private
FState : TCsvParserState;
// Кэширование состояния объектов для лучшей производительности
FFieldStartState : TCsvParserFieldStartState;
FScanFieldState : TCsvParserScanFieldState;
FScanQuotedState : TCsvParserScanQuotedState;
FEndQuotedState : TCsvParserEndQuotedState;
FGotErrorState : TCsvParserGotErrorState;
// Поля, получаемые благодаря парсингу
Original (English): More Design Patterns in Delphi
Translation: © sim-sim, r3code, TDelphiBlog, v-kamkov .
