Найти тему
Мультипарсер

07. Немного истории

Оглавление

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

Нечто подобное на протяжении последнего (приблизительно) полувека происходит в сфере разработки программного обеспечения. Когда-то, в 80-х появились первые персоналки под управлением операционной системы MS-DOS. Тогда же получили мощное развитие языки программирования, например, всемогущий C++ и гроза всех интеллектуалов Assembler, поклонники которого хвастались друг перед другом, сколько тысяч строк содержит исходный код нового драйвера, созданного и отлаженного за три месяца бессонных ночей.

Но человек, сидящий в своей комнате-сарае-гараже и создающий что-то, развивающееся по законам, одному только ему понятным - это тот же средневековый ремесленник. Может быть даже алхимик. Если звёзды сойдутся и божества виртуального мира будут благоволить взъерошенному гению, в недрах тёмного угла родится какая-то софтина, делающая то, что ни кто до этого не делал. Тогда и деньги и слава и признание (может быть даже зависть) изумлённых коллег. Но бывает и наоборот. Переоценил свои силы, и вообще заехал не в ту степь... короче, злоба, разочарование и нервный срыв.

Для чего же нужны паттерны?

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

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

Что бывает, когда действуешь наугад

История программы "Мультипарсер" началась около 12 лет назад. Тогда я познакомился с человеком, который занимался металлоизделиями. У него было собрано несколько Excel-файлов с прайслистами абсолютно разных фирм, которые нужно было привести к единому формату, то есть заполнить по ним таблицу с тремя колонками: наименование, цена, единицы измерения.

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

Вначале я попытался решить поставленную задачу с помощью простеньких скриптов VBA, но быстро понял, что это не реально. Даже двух разных алгоритмов для распознавания прайслистов, соединённых в одном файле исходного кода достаточно, чтобы захлебнуться в обилии операторов IF-ELSE, а что же будет когда количество пойдёт на десятки. Стало совершенно ясно, что чтением каждого прайслиста должен заниматься отдельный класс. Программа должна проверять, его ли это "юрисдикция", и, если это так, передавать ему управление. Этой проверкой занимается виртуальная функция DetectReaderClass, которая читает прикреплённые к этому классу атрибуты PriceFieldAttribute с номерами и заголовками колонок. Если в листе заголовки колонок стоят в тех столбцах, что указаны в атрибутах - класс обработчика найден! Это алгоритм проверки по умолчанию, в случае необходимости, он может быть переопределён.

Проверка атрибутов класса-обработчика
Проверка атрибутов класса-обработчика
Динамически загружаем сборку и перебираем содержащиеся в ней классы
Динамически загружаем сборку и перебираем содержащиеся в ней классы

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

На следующих двух скриншотах показана функция обработки прайслиста в абстрактном классе и реализация функций проверки и вывода данных в одном из производных классов.

Обработка прайслиста
Обработка прайслиста
Реализация функций в одном из производных классов
Реализация функций в одном из производных классов

Зрелым взглядом

Вернулся я к этому проекту совсем недавно. Сама концепция задачи, брать данные из одного источника для наполнения некой базы данных, как я теперь смог убедиться, является довольно востребованной. Реализация, однако, потребовала кардинальных изменений. Я создал новый проект (который назвал "Мультипарсером") и постепенно, проверяя, дополняя, а иногда и просто создавая отдельные классы с нуля, перенёс функционал в новый проект.

Главное отличие "Мультипарсера" в том, что он может брать данные не только из файлов Excel, но и с веб-страниц, а так же в том, что структуру выходного файла можно менять.

Вся архитектура программы была мной подвергнута тщательной проверке на соответствие паттернам. Когда я спешно проектировал первую версию, мне всё же кое-что удалось угадать. Например, то как создавались объекты обработчиков на 95% соответствовало паттерну "Фабричный метод". Я промахнулся совсем чуть-чуть: функцию тестирования надо было вставить не в сам класс обработчика, а создать для этого отдельный класс крейтора, который в случае успешной проверки возвращает объект парсера. Функция обработки прайслиста вполне соответствует паттерну "Шаблонный метод", где порядок действий общий, но реализация действий в каждом производном классе своя. Наиболее существенным изменениям подверглись классы, работающие непосредственно с Excel - из них я убрал всё, что не имело прямого отношения к чтению и записи файлов, и весь функционал инкапсулировал в двух основных классах. Класс ExcelBook и класс ExcelPage, которые содержат ссылки на объекты библиотеки Microsoft.Office.Interop.Excel, упрощая работу с ними - паттерн "Фасад".

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

Глава 06 - Карта канала - Глава 08 (в процессе)