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

User

Send message
Мне тоже интересно, если программист пишет квадратичный алгоритм там, где можно написать линейный, как поможет компилятор? Быстродействие кода в любом случае в первую очередь зависит от программиста, а лишь во вторую очередь от компилятора. Компилятор лишь инструмент. И этот инструмент нужно либо теоретически знать, либо практически исследовать его возможности. И не только компилятора, но и других низлежащих систем. Например операция создания объекта — дорогая. Операция же мутации поля в классе — дешевая. Я вот писал алгоритм с деревом, написал его и чистыми функциями и мутирующими. Тест производительности — вставляем миллион элементов в дерево. Максимальная глубина дерева при этом 20, средняя — 10. Т.е. для вставки мутирующему алгоритму надо создать 1 объект на итерацию, чистому — в среднем 10 на итерацию. Не считая балансировки. И на этой выборке, неожиданно, чистый код оказался чуть меньше чем в 10 раз медленнее. Как тут поможет компилятор реабилитировать ФП и чистые функции в частности? Непонятно.
Сейчас 65" телевизор c ~2.5 метров, 4к, но смотрю по факту 1080.
Планирую брать проектор и экран 130" и отъезжать назад соотвественно.
Есть две ситуации — 1 человек говорит сомнительную вещь о том, что одно работает, другое нет
2. человек пишет код на 10 экранов и считает, что тот легко читаем.

Ответ один — напиши тест.
Всегда можно дать избыточно понятное имя, например fibonacciForNumbersGreaterThan1. Привычка давать короткие непонятные имена происходит как раз из тех смутных времен.
Правило про «только одну точку возврата» нужно только потому что люди пишут код одной большой портянкой. Искать еще один return в большой портянке — то еще удовольствие.
Надо выбираться из помойки, а не делать помойку чуть более опрятной. Когда код должным образом декомпозирован, такого вопроса как писать один return или два вообще не возникает.
Вот таким вот образом condition c двумя return-ами отделяется от вычисления, вуаля — проблема исчерпана.
int fibonacci(int position)
{
    return position < 2 ? 1 : fibonacciHigh(position);
}

int fibonacciHigh(int position)
{
     int answer = 1;
     int previousButOne = 1;
     int previous = 1;

     for (int n = 2; n < position; ++n)
     {
         previousButOne = previous;
         previous = answer;
         answer = previous + previousButOne;
     }     
     return answer;
}



PS правда может возникнуть проблема именования функций, но это меньшее зло
Это псевдокод, чтобы обозначить то, как примерно должны выглядеть методы.
Мне трудно это все доносить, люди, которые пишут код примерно так как здесь,
1
image
с трудом могут представить как это не делать код с флагом, а то и с двумя-тремя

Я же предполагаю, что код надо писать примерно так и хардкодные параметры, ака флаги, из такого кода испаряются сами собой.
2
image


Это примеры кода из реального проекта. их не предполагается внимательно читать, просто взгядом оценить, чтобы понять о чем идет речь.
Второй пример кода лучше не только потому, что он больше декомпозирован, но и еще потому, что получает список сущностей про списку ключей, а не как оригинальный, который получает всегда по одному ключу, что есть проблема N+1. Речь не о ней, конечно же, но стоило упомянуть, что код который лучше не обязан работать медленнее, он может и быстрее работать, чем «то что было раньше».

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

ЗЫ и да, в той упомянутой статье есть такой пункт — не надо бояться выкинуть свой код в помойку. Конечно, у нас всегда не хватает времени, жмут оценки, давит начальство, «все было против нашей сборной по футболу»(с)
В статье про 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 — при изменении кода по просьбе одного «актора», если этот код также делает что то для другого актора — появляется вероятность внести ошибку. Если перейти в плоскость ответственностей, там происходит тоже самое. Если в методе переплетены две ответственности, то меняя одну из них, появляется вероятность сломать другую.
Уже на этой точке должно быть видно, что это один и тот же принцип.
Все принципы, так или иначе, направлены на единственную благородную цель — снизить количество ошибок. Снижать цикломатическую сложность нужно, потому что чем больше сложность, тем больше вероятность внести ошибку. Повышать тестируемость кода нужно, чтобы снизить вероятность ошибок. И так далее.

И да, я в статье упоминал про таких людей, которые говорят «этот принцип тут не применим». Это неверный ответ.

Information

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