Refactor 2 iteration #1
Open
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
В этом PR опишу, как бы я порефакторил текущую версию приложения.
Классно, что вынес логику в service-объект – стало удобнее! По коду такие комменты:
attribute_color,attribute_price,experiment_params) я бы предложил использовать command-query separation принцип (https://ru.wikipedia.org/wiki/CQRS), когда у любого метода есть одна из двух функций: "команда", типа "сделай что-то" или "запрос", типа "дай мне что-то". Сейчас же получается смешение - имя как будто query, а логика – как будто command. То есть должно быть либоparams = experiment_params, либоset_experiment_paramsвнутри которого выставляем инстанс-переменную@params(у command в имени всегда есть глагол)query_to_db_valid?, внутри которого мы делаем запрос в базу. Из-за этого получается, что мы делаем лишний запрос – можно всё объединить в одно условие:@params, а потом на следующей строчке делаем это ещё раз https://github.com/Froz10/experiments-api/blob/master/app/services/api/experiment_service.rb#L14 – получается можно 13 строчку удалить и ничего не изменитсяcallприсваиваются instance-переменные, которые в итоге не используются (@experiment,@experiments) – лучше в таком случае использовать локальные переменные (а@experimentвообще можно в переменную не записывать)В итоге сервис получится вот таким:
После такого рефакторинга видно, что в начале метода снова нарушается CQRS (
Experiment.colors,Experiment.prices). Для этого надо ещё порефакторить модельExperiment(тут комменты, которые я в прошлый раз что-то не отметил):colorsиpricesна самом деле нам нужны только для того, чтобы инициализировать переменные@colorsи@prices. Получается, что мы можем вместо явного вызова этих методов в методахcolorиpriceвызывать методы, а не инстанс-переменные. Вот так:callможем вообще убрать и всё будет так же работать.find_or_create_by. Получается вообще весь сервис – это один вызов метода с блоком из 2 строк внутри:distributionизlib, а внутри этого файла декларируем 2 класса –ColorиPrice. Это можно назвать антипаттерном в rails, т.к. по стандартным соглашениям по имени класса должно быть понятно, где он лежит. Тут есть 2 варианта:color.rbиprice.rb), либо, например, добавить namespace, чтобы классы называлисьDistribution::ColorиDistribution::Price. Мне лично больше нравится второй вариант:ExperimentотDistribution– пусть они связываются только через наш сервис-объект. Для этого уберем метод color/colors и price/prices из модели, а сервис изменим следующим образом:По названию метода
distributionнепонятно, что он делает. Опять же, либо мы делаем command "сделай что-то" (в таком случае будетdistrubute), либо "дай мне что-то", напримерnext_value. Мне наверное больше нравится второй вариант – переименуем методdistributionвnext_valueТеперь перейдём к контроллеру. Тут можно немного упростить логику метода
index. Тут снова нарушение cqrs – у нас есть методdevice_header, который назван как query, а выполняет функциюcommand(т.к. выставляет значение instance-переменной). Вместо этого я бы сделал его как query (при этом добавив memoization и убрав лишние "сущности" и назвав instance переменную так же, как и метод – неtoken, аdevice_token):А теперь можно добавить стандартный подход к аутентификации в rails и сделать её в before_action:
А метод
index, тем самым разгрузим и оставим только основнвую логику:Кстати, тут у нас немного путанница: на самом деле пользователь может попасть только в один эксперимент (именно поэтому я в сервисе использовал не
where, который возвращает коллекцию, аfind_by, который возвращает конкретную сущность). А в названии у нас множественное число – что не верно. Поэтому переименуем)