Информация
- В рейтинге
- 1 354-й
- Откуда
- Рига, Латвия, Латвия
- Дата рождения
- Зарегистрирован
- Активность
Специализация
Фулстек разработчик, Архитектор программного обеспечения
Ведущий
JavaScript
HTML
CSS
Node.js
Vue.js
Веб-разработка
Progressive Web Apps
PostgreSQL
MySQL
GitHub
Т.е., в более гибком варианте поведения мне нужно будет изменить код слушателя, я правильно понял?
Если вы сначала написали код, а потом решили, что он должен работать по-другому, то да — код нужно будет менять.
Моего расширения — нет. Но если бы я не ввел DI Zend'а, то — да.
Я делаю расширения к уже существующим приложениям и встраиваюсь в них. Они и диктуют "правила игры". Может завтра клиент решит в OroCRM или WordPress встроиться — я ж не знаю :)
Тот же Monolog (имплементации PSR3-логгера) позволяет делать это все и многое другое без изменения кода приложения — на уровне конфигурационных файлов. Log4php позволял делать примерно такие же финты (но он из до-PSR3 эпохи). Можно хоть вообще логирование отключить, если оно не нужно, а можно логировать все подряд только от определенного namespace'а (в Log4php, по крайней мере). Можно в файл, можно в БД, можно по email'у, а можно и туда, и туда. И все это — чисто на уровне конфигов уже существующих и широко используемых логгеров — Monolog на packagist'е показывает 24М загрузок. Log4php — 624K.
IMHO, логгер вполне широкоприменимый компонент, и он вполне может соседствовать с DBA. ОК, я могу еще согласиться, что DBA не место рядом с другим сервисом в зависимостях, но PSR3-логгер уж точно может идти куда угодно и с кем угодно. Как минимум, в контексте данной статьи.
Спасибо, что донесли до меня свое мнение. Нет, я не смогу выполнить ваши пожелания, т.к. сложные случаи имеют множество правильных решений, оптимальность которых зависит от контекста задачи и преследуемых целей, и без спецификации с вашей стороны того и другого моя статья для вас выйдет не менее пустой.
Может быть мы под логированием понимаем какие-то разные вещи? Я попытался до вас донести, что в сервисе MyService предположительно реализуется некоторая логика, шаги которой (трейс) было бы хорошо иметь в случае разбора полетов (например, для PSR3-интерфейса есть имплементация, для которой есть обработчик FingersCrossedHandler, позволяющий сбрасывать в лог сообщения всех уровней, если произошла ошибка, и не писать ничего, если ошибки не было). Я полагаю, что в более-менее сложной системе обязательно нужно логировать ключевые моменты в принятии решений (в файл, БД, email'ом — это уже настройки логгера, которые делаются админом приложения). Но вот формирование внятных сообщений — это обязанности разработчика приложения. А какое ж логирование без логгера? Я еще допускаю, что на низком уровне (ближе к БД) можно опустить логирование (генерируется большой объем сообщений), но в сервисах, реализующих бизнес-логику…
Из моего примера не следует, кстати, что нужно тестировать имплементацию PSR3-интерфейса, поставляемую в конструктор сервиса, из него следует только, что этот интерфейс должен быть замокирован, чтобы сервис мог в принципе работать (а не вылетать по ошибке "Call to a member function info() on a non-object"). Вот, например, моя типовая операция:
Я не проверяю в тестах, что сервис дергает метод info у logger'а, хотя и могу, если вдруг окажется, что данная диагностика критически важна в каких-то ситуациях, когда постфактум пытаешься понять, что произошло на живой системе, в случае проблем.
И я абсолютно не вижу причин, по которым PSR3-логгер не может соседствовать в моем сервисе с DBA и/или другими сервисами.
Придерживаясь правила "не более 4-х", мы можем выделить зависимости в отдельные сервисы, увеличив кол-во классов, и превратив 300 строк кода в даже не знаю сколько. Можете попробовать отрефакторить код примера, если у вас есть желание и время, и сравнить его с первоначальным — улучшиться ли читаемость и управляемость в результате.
В данном случае при рефакторинге придется наращивать кол-во узкоспециализированных классов, что я и называют "ростом вглубь", в отличие от "роста вширь", когда в классе используется "более 4-х" зависимостей с более широкой специализацией. Плюс "широкой специализации" — порог вхождения в проект ниже, чем порог вхождения в проект с "узкой специализацией". Плюс "узкой специализации" — позволяет строить в конечном итоге более сложные проекты, чем "широкая" (правда в этом случае нужно очень хорошо представлять себе предметную область, а "широкая" более снисходительна к экспериментам).
За ориентир "4 зависимости" — спасибо :)
Но это же все равно две зависимости, разве нет? Есть ли где-то в формулировке "принципа единой ответственности" ограничение на количество зависимостей для отдельного проектируемого класса? Если нет, то этих зависимостей может быть чуть менее, чем очень много.
И я все одно не понял, почему залезть в БД и залогировать это дело в рамках одного класса — есть нехорошо, есть "нарушение принципа единой ответственности"? Ну допустим, мой класс должен находить какие-то данные в базе по несколько причудливому алгоритму, типа "Вернуть А, если не найдено, то Б, если не найдено, то В". А в логах отметить, что именно было возвращено для последующего "разбора полетов" в случае чего. У меня есть логгер и есть адаптер к БД. Почему я не могу использовать оба этих объекта в своем классе?
В этом случае перед каждым тестирующим методом будет происходить реинициализация всех используемых моков и самого тестируемого объекта.
Спасибо за наводку, Fesor.
Меня интересует инициализация/сброс мокируемых зависимостей между определением их поведения в тестирующих функциях (будет весьма нехорошо, если мы проинициализируем моки один раз, а потом будем специализировать поведение одних и тех же моков). Что касается замечательного "принципа единой ответственности", то ему действительно следуют не все, но что делать, если иногда нужно в базу залесть и в логах об этом запись оставить? Для более-менее сложных систем количество объектов, которыми в итоге нужно манипулировать при выполнении некоторой операции может быть весьма велико. Либо мы растем вширь (кол-во зависимостей в конструкторе), либо вглубь (иерархия специализированных манипуляторов с единой ответственностью).
Посмотрите таблицы:
на примере того же Клиента:
Ну я же не знаю, чего не знаете вы :) С моей колокольни все это вполне укладывается в следствие из «слабосвязанные команды разработчиков»
В целом достаточно верно.
Не совсем так, возможен вариант, когда плагин добавляет обязательные поля в БД, а также на web-форму добавления/редактировния соотв. сущности (в UI).
Отличная идея! Без преувеличения (просто в Magento это сделано слегка не так). Я бы даже смотрел в сторону создания отдельной таблицы для каждой колонки (атрибута сущности; так сказать, максимальное приближение к «6-й нормальной форме»), но количество неизведанного геморроя заставляет останавливаться на 3NF (вернее, не отрываться от нее достаточно далеко).
Именно так. Поэтому и производные от DataObject, структурирующие «просто данные» под текущие условия использования.
Это да, есть такая беда. Можно для облегчения жизни задействовать views (а в postgres'е даже и materialized views), но без автоматизации сборки/разборки объектов по-атрибутно это будет условно-досрочное облегчение. А уж как БД админы будут благодарны девелоперам…
Кстати, если во втором варианте положить, что все объекты данных наследуют от DataObject (CustomerRef extends Customer extends DataObject), то это и будет то, о чем я и пытался сказать:
Другое дело, что обращение к ref-атрибуту в контексте базовой сущности не имеет смысла — этот атрибут вводится только в ref-плагине. Но если базовый функционал загрузил все доступные атрибуты сущности «Клиент» и передал их в ref-плагин, то в ref-плагине достаточно создать новый ref-объект на основании данных базового и autocomplete заработает (т.к. это уже будет другой объект с теми же данными, можно сказать — другая проекция данных на типизированный объект). В PHP можно даже просто проаннотировать тип переменной/аргумента, без реального изменения его типа, и autocomplete будет работать. Создание нового объекта на основании данных базового — это для строго типизированных языков.
Я бы даже сказал, что так должно быть всегда, когда это делается не из контекста самого плагина (или других плагинов, на нем базирующихся).
Они могут лежать в БД, представлены в виде JSON/XML/binary/encoded Base64, могут обрабатываться программой в виде ассоциативного массива, множества POJO-объектов, «однойбольшойдлиннойстроки» и т.п. Приложения постоянно трансформируют данные из одного формата в другой, начиная от UI'я и заканчивая БД и в обратную сторону. JSON/XML/YAML/… — это форматы передачи данных между различными (а иногда и разнородными) системами. У каждого из них своя «заточка», своя ниша. По-большому счету, исходный код — это тоже данные. Как правило, текстовые. Я встречался с решениями, когда код хранился в БД, извлекался и выполнялся по мере необходимости (то ли Liferay, то ли Odoo/OpenERP). BPML — это XML-данные или инструкции по обработке (код) в формате XML?
Иногда, особенно когда разработчики плохо представляют себе моделируемую предметную область или просто не в состоянии представить себе ее полностью (а чем сложнее система, тем выше вероятность возникновения такой ситуации), жесткая типизация объектов-данных может дать больше минусов, чем плюсов (особенно, если система нуждается в постоянной подстройке под постоянно изменяющиеся требования). И вот тут уже нужна универсализация.
Я с вами согласен, что идеальный плагин должен стать частью системы, но что делать с плагинами неидеальными? Которые core-разработчики не берут в свой код, а у этих плагинов, между прочим, весьма обширный круг пользователей? И сколько «идеальных плагинов» выдержит core и его разработчики, пока не начнут задаваться вопросом «а не сильно ль мы раздулись?»
По поводу выборки контента из JSON'а — я более чем уверен, что в вашем случае обработка идет не прямо по тестовым данным regexp'ом, а по их преобразованному (проиндексированному) аналогу (тому же ассоциативному массиву).