У меня было две образовательные цели при создании Случайного парсера поэзии. (github, rubygems)
1: Практикуйте хороший объектно-ориентированный дизайн, как описано в Практический объектно-ориентированный дизайн в Ruby.
2: Создайте хороший интерфейс командной строки, как это описано Дэйвом Коуплендом в его докладе Создание потрясающих приложений командной строки с помощью Ruby и соответствующей книге Создание потрясающих приложений командной строки на Ruby.
Мой код не соответствует всем (или даже почти всем) принципам хорошего дизайна, описанным в этих книгах. Но я многому научился, пытаясь следовать этим принципам в течение того временного окна, которое я отвел себе на создание проекта.
Уроки выучены
Не используйте генератор драгоценных камней, пока полностью не поймете, что он делает
Когда я начал свой проект, я создал драгоценный камень, используя следующую команду:
bundle gem gem-name
Я нашел команду, описанную в этом RailsCast. Эта команда генератора устанавливает каталог моего проекта, файл gemspec и множество зависимостей в проекте. Эти леса были удобны... пока не стали ими.
В конце моего проекта я хотел сделать гем исполняемым, чтобы пользователь мог запустить его прямо в терминале с помощью команды
random_poetry_scraper
Я часами пытался заставить эту функцию работать. Некоторая магия шаблонов в файле .gemspec помешала мне установить исполняемый файл с помощью этого Добавления исполняемой процедуры. Я до сих пор точно не знаю, что пошло не так, но я надеюсь выяснить больше в будущем.
Разочарованный, я в итоге перестроил всю структуру драгоценного камня с нуля, не используя волшебство строительных лесов и этот учебник в качестве руководства. Процесс был утомительным, но стоящим. Было легко добавить исполняемый функционал, как только я понял все, что происходит в моем проекте. Я также удалил каталоги и зависимости, созданные скаффолдером, которые мне не нужны.
«Ожидайте, что выходные данные каждой программы станут входными данными для другой, пока неизвестной программы» (Путь Unix)
Цитата в заголовке раздела является частью кредо, которое часто называют Путь Unix. это кредо в действие в этой жемчужине.
Первый интерфейс, который выводит стихи и их атрибуты в виде JSON, инициируется с помощью флага —json.
random_poetry_scraper --json -num-poems=5
Второй интерфейс, называемый «читатель удовольствия», позволяет пользователю читать стихи в удобном для человека формате в терминале. Это инициируется флагом удовольствия.
random_poetry_scraper --pleasure -num-poems=5
Я решил, что интерфейс JSON следует «Unix Way», потому что я ожидаю, что выходные данные этого интерфейса станут входными данными для другой программы. Я не следовал правилу интерфейса «увлекательного чтения».
Помимо прочих отличий, это означает, что при вызове с флагом «-удовольствие» гем Random Poetry Scraper пишет удобные для пользователя сообщения stdout
, подобные следующим:
1 poem(s) fetched successfully 2 poem(s) fetched successfully ...
Напротив, при вызове с флагом «— json» гем будет записывать только чистый JSON в stdout
. Если бы я оставил сообщения X poem(s) fetched successfully
в интерфейсе JSON, кому-то, кто хотел бы использовать JSON из stdout
, программе пришлось бы тщательно анализировать эти сообщения.
«Зависит от поведения, а не от данных» (Практический объектно-ориентированный дизайн в Ruby, 49)
Один из принципов проектирования,подчеркиваемый снова и снова в POODR, заключается в том, что данные должны быть заключены в методы. Это облегчает изменение ваших программ. Возьмем пример переменной экземпляра, указывающей вес:
class Person @weight end
Если вы завернете экземпляр веса в ридер следующим образом:
class Person def weight @weight end end
Код сразу становится более пригодным для повторного использования в следующей ситуации. Предположим, что ваша программа будет работать на Марсе. Это означает, что для марсианских пользователей вместо земного веса должен отображаться вес на Марсе. Если вы обернули переменную экземпляра в weight
reader и вызвали person.weight
по всей программе, вам нужно только настроить наш код в одном месте, чтобы внести изменения.
class Person def weight @weight * martian_adjustment_factor end end
Однако если бы вы вызвали @weight
непосредственно, вам пришлось бы изменить намного больше кода.
В своем проекте я использовал считыватели для переноса одного типа данных, которого я никогда не ожидал: сообщений об ошибках и документации.
В моем проекте впервые было много таких функций:
Я реорганизовал их, чтобы они выглядели примерно так:
Я думаю, что это более читабельно и гибко.