Pull to refresh
25
0
Владимир Репин @VladVR

User

Send message
Я не предлагаю загружать избыточные данные, я предлагаю декомпозировать код, после того как он «нашинкуется» на методы, от избыточная сложности избавиться будет легче. Для флага передаваемого хардкодом не надо придумывать енумы и константы. Его не должно быть. Да, сидеть и заниматься целенаправленно рефакторингом какого то говнометода может быть долго, неинтересно и неоправданно. Надо постепенно расти, стремиться к тому, чтобы изначально не писать таких методов.

ЗЫ и да, в той упомянутой статье есть такой пункт — не надо бояться выкинуть свой код в помойку. Конечно, у нас всегда не хватает времени, жмут оценки, давит начальство, «все было против нашей сборной по футболу»(с)
В статье про SRP я показывал как это делается. Пока вся реализация находится внутри этого метода, копипастить функцию может быть неудобно.
Однако если ее декомпозировать, то эти две функции начнут выглядеть примерно так.
getShortMessage(id) {
const data = loadMessageData(id);
return composeShortMessage(data);
}

Нетрудно представить как будет выглядеть второй метод при этом.
Защищена не папка, а сам приватный ключ правами 600.
Тогда вообще не о чем беспокоиться.

И эта конкретная задача только на windows протестирована. там на папках users/name уже развешаны необходимые права. Если вешать права на файлы и контролировать их самостоятельно, то этот абзац не имеет значения.
Там уже хранится приватный ключ гит. Если, например, другой пользователь залогинится на ваш рабочий комп и получит доступ к этой папке в вашем профиле, то безопасность уже нарушена.

Но я не против услышать другие разумные варианты, где лучше хранить два оставшихся токена.
Возникает ситуация, когда поток «экшн» — «стэйт» — «экшн» — «стэйт»… начинает напоминать символ бесконечности
Это то, что фейсбук изначально предлагал избегать. здесь avoid cascading effects.

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

Как это все решаю я — вот нафантазировал пример
export class MyService {
  constructor(dispatcher: MyStateDispatcher, api: MyApi){}
  public async expandButtonPressed(): Promise<void> {
    this.dispatcher.showProgressBar();
    this.dispatcher.expandSection();
    try {
      const data = await api.getDetails();
      if(someCondition) {
         this.dispatcher.showDetails(data);
      } else {
         const otherDetails = await this.api.getOtherDetails();
         this.dispatcher.showOtherDetails(otherDetails);
      }
    } finally {
       this.dispatcher.hideProgressBar();
    }
  }
}


вьюшка же делегирует нажатие кнопки этому методу.
А потом приходит заказчик и просит сделать следующие истории:
Забивать гвозди на марсе.
Рассчитывать фазы луны в штате США где запрещены гвозди.
От фаз луны должно зависеть выращивание картошки.
Надо чуть подправить рассчет фаз луны, но гвозди должны забиваться как и раньше.
и т.д.

Анекдот такой был: пищевое отравление обычно начинается со слов — да ладно, чего ему будет в холодильнике.
А отравление проекта начинается со слов — дай ка я совмещу эти две вещи и когда их надо будет менять я буду менять это в одном месте.

С моего опыта на одно такое изменение, где надо поправить два участка кода одновременно приходится два изменения где надо поправить один и второй не должен измениться.
Правда, я встречался и с мнением, что UDF (именно в контексте статически типизированных языков) затрудняет прослеживание логики выполнения программы.
Не думаю, что есть какая либо разница с динамически типизированным языком.
Я с помощью генератора убираю action, action type и reducer от разработчика, и оно начинает выглядеть как вызоа конкретной функцией над узлом состояния. Примерно вот так
// функция изменяющая состояние
function disable(prev: MyState): MyState {
  return { ...prev, isDisabled: true };
}
// потребитель
export class MyService {
  constructor(dispatcher: MyStateDispatcher){}
  public disableButtonPressed(): void {
    this.dispatcher.disable();
  }
}

конечно где то в вики надо обозначать, что есть связь между методом disable у диспетчера и функцией disable которая будет создавать переход.
Отслеживание выполнения ограничивается этой связью.
что и на что они променяли, выбрав такую архитектуру. И оправдался ли их выбор.

В целом пока что только положительный опыт от применения архитектуры.
Со время написания соответствующей статьи мое мнение не изменилось. Пока что UDF это лучшее, наиболее чистое решение для фронтенда.
Я готовлю кодогенератор, который позволит работать с redux максимально эффективно и максимально чисто. Но только для Typescript, такой генератор возможен благодаря информации о типах. Долгое время не было времени им заняться.
Сегодня вы их согласовали, а завтра опять у каждого актора свои потребности.
У нас есть один такой проект с двумя «акторами». Однажды заказчики пришли и сказали — а давайте вы стартуете еще один проект на той же кодовой базе, используя наработки. Плюс часть страниц очень похожа, а то и полностью совпадает. И наши ребята стартовали это. Помучавшись какое то время от того, что разработчик одного проекта что то ломает в другом, естественно разработчик не будет запускать «чужой проект», чтобы проверить, их «разделили», потом разделили побольше, потом еще больше, в результате это стали две папочки с полностью непересекающимся кодом. А там где до сих пор не разделили, сплошь и рядом встречаются строчки if(isSecondProject). Вот именно что то такое вы считаете имелось ввиду принципом SRP?
Я считаю, что их надо декомпозировать еще на уровень выше. Не раскладывать в две папки в одном проекте, а разложить в два отдельных проекта. И да, SRP все еще про декомпозицию, хоть и на другом уровне. И даже в этом, абсурдном, не дающем абсолютно ничего разработчику, понимании, вопрос все равно в отделении одного от другого, мух от котлет.
Мне кажется не стоит недооценивать говнокод. Вот я копаю проект, с которого меня местами бомбит. Базовый класс сервис, метод Read, внутри вызывает Write. В другом месте снаружи приходит флаг isShallow и передается в следующий метод, принимающий флаг isDeep, не инвертируя.
Ну это так, по мелочи.
Я бы сказал, больше похоже, что SPoF — обратная сторона принципа DRY. Именно он призывает во чтобы то ни стало избавляться от дублирования.

SRP скорее наоборот. К примеру у нас была ситуация, где для разных пользователей была чуть разная логика проверки доступа к ресурсу. С точки зрения DRY — надо избежать дублирования. C точки зрения SRP — это две разные ответственности и им не стоит жить в одном методе.
Эти принципы созданы не «для приложений». Эти принципы созданы для разработчиков, для того, чтобы они писали «хороший» код и не писали плохой. Причина, зачем вообще нужен SRP — при изменении кода по просьбе одного «актора», если этот код также делает что то для другого актора — появляется вероятность внести ошибку. Если перейти в плоскость ответственностей, там происходит тоже самое. Если в методе переплетены две ответственности, то меняя одну из них, появляется вероятность сломать другую.
Уже на этой точке должно быть видно, что это один и тот же принцип.
Все принципы, так или иначе, направлены на единственную благородную цель — снизить количество ошибок. Снижать цикломатическую сложность нужно, потому что чем больше сложность, тем больше вероятность внести ошибку. Повышать тестируемость кода нужно, чтобы снизить вероятность ошибок. И так далее.

И да, я в статье упоминал про таких людей, которые говорят «этот принцип тут не применим». Это неверный ответ.
Принцип Open-Close вполне чёткий. Если при добавлении новой функциональности пришлось поменять существующий код — принцип нарушен. Причем он нарушен был раньше, сам факт изменения существующего кода лишь сигнализирует о нарушении.
ответственность должна быть перед одним стейкхолдером".
и если стейкхолдер всего один, то весь код можно написать в одной функции.
И про «одну причину для изменения» все тоже также. Есть причина — надо поменять способ хранения данных в базе. Вторая причина — надо поменять протокол связи по сети. Если же ответственность метода воспринимать как «отправлять отчет», то обе эти причины звучат как нужно поменять отправление отчета, а остальное это детали. Акторы, юзеры, стейкхолдеры, как это все ни назови, это все об одном и том же. И проблема у всех определений одна и та же. И есть еще такой принцип в Domain Driven Design, называется Domain Distillation. Другое название, другой «уровень», а принцип тот же — декомпозируй.
Т.е. «функция должна делать одну вещь» – это слишком расплывчато, а «Ответственность кода не должна быть слишком большой» – норм?

Первое не расплывчато, оно не работает вообще.
Второе работает примерно так — отправить отчет провайдеру — слишком большая ответственность. Считать данные(1), преобразовать данные(2) и отправить объект(3) в сеть — три ответственности поменьше. Суммарно они равны первой, слишком большой ответственности.
Кроме того, что люди реализуют эти три ответственности в одном методе, они еще и «переплетают» их. То есть считав одну строку или число из базы, сразу стремятся преобразовать во что то. Тут и появляется помножение сложностей двух задач.

Ну и повторюсь, декомпозиция и разбиение на подзадачи – это хорошо, но к SRP никакого отношения не имеет.
Разбиение одной большой задачи на три маленьких это тоже самое что разбиение одной большой ответственности на три маленьких. Если вам запретить использовать большую ответственность для отговорок, и настоять на том, что ваш метод таки имеет три ответственности, то принцип SRP тут именно и при чём. Именно он скажет, что ответственность должна быть одна, следовательно метод надо разбить на три части.
функция должна делать одну вещь и делать ее хорошо
И это определение не работает точно также, по той же причине. Не получится достоверно определить когда «вещь» одна, а когда нет. И как результат — ваш код изобилует т.н. code smells, а вы избегаете декомпозиции, прикрываясь тем, что якобы функция делает одну вещь, контролирует космическую станцию, ну или отправляя отчет в страховую компанию.
но это из разряда: «Нормально делай – нормально будет».
это из разряда: complexity kills, decompose it and you win.
От «редуктора» покоробило. Лучше было остаться с редюсером.

Правильный ответ: DELETE_ITEM_SUCCESS может обрабатываться как редуктором items, так и редуктором favoriteItems.
Это тоже неправильный ответ. Ну или не лучший из правильных. Я реализую это как действия редюсера, который стоит в узле дерева выше этих двух. То есть у одного Action всегда только один редюсер, который его обрабатывает. А этот родительский редюсер в свою очередь вызывает соответствующие чистые функции создающие новое дочернее состояние для этого родителя. Родительский редюсер же интегрирует это в состояние. примерно так.
export function parentReducer(prev: State, action: IAction){
switch (action.type)
case Remove:
return {
...prev,
items: childFunctions.removeItem(prev.items, action.itemId),
favoriteItems: childFunctions.removeItem(prev.favoriteItems, action.itemId),
};
}

Так обычно колеса и тестируют всякие обозреватели. Выходят в холмистую местность и шуруют непрерывно в гору. Те колеса у которых система охлаждения послабже — перегреваются и нуждаются в перерыве где нибудь посередине горы. Там где получше — выдерживают тест.

Information

Rating
Does not participate
Location
Нижний Новгород, Нижегородская обл., Россия
Date of birth
Registered
Activity