Pull to refresh
16
0
Send message
Ну что вы такое говорите. Вся необходимая информация по АО публична и в учредителях только лица, уже фигурировавшие в статье: www.google.ru/search?q=АО+Сибком+учредители
func upgradeUser(endpoint, username string) error {
    getEndpoint := fmt.Sprintf("%s/oldusers/%s", endpoint, username)
    postEndpoint := fmt.Sprintf("%s/newusers/%s", endpoint, username)

    req, err := http.Get(genEndpoint)
    if err != nil {
        return err
    }
    data, err := ioutil.ReadAll(req.Body)
    if err != nil {
        return err
    }
    olduser, err := user.NewFromJson(data)
    if err != nil {
        return err
    }
    newuser, err := user.NewUserFromUser(olduser),
    if err != nil {
        return err
    }
    buf, err := json.Marshal(newuser)
    if err != nil {
        return err
    }
    _, err = http.Post(
        postEndpoint, 
        "application/json", 
        bytes.NewBuffer(buf),
    )
    return err
}

Функция upgradeUser реализует какой-то сценарий бизнес логики, соответственно она должна абстрагировать от деталей реализации. Но почему-то функция не абстрагирует от низкоуровневых ошибок, которые возвращают используемые в ней функций. Например: мы хотим проапгрейдить юзера, а в результате получаем ошибку ввода-вывода сети или http.ProtocolError или url.Error. Как я должен различать и обрабатывать их? Их даже заллогировать корректно нельзя, т.к. непонятен контекст. Часть ошибок может быть по моей вине, если я указал некорректный endpoint, с остальными ошибками, я скорее всего ничего сделать не могу. Также не забываем, что код, который будет использовать функцию upgradeUser, по хорошему, ничего не должен знать о деталях реализации. И что делать, если в процессе поддержки приложения мы отказались от http и перешли на бинарный протокол? Переписывать все места, где используется upgradeUser чтобы корректно обработать ошибки?
Делаем выводы:
1) Реализация этой функции далека от совершенства, т.к. в ней текут абстракции — не пишите так;
2) Если вы собираетесь вернуть полученную ошибку «наверх» без изменения (заворачивания в другую) трижды подумайте;
3) Следует четко понимать и определять какие ошибки может возвращать ваша функция, детали реализации функций скрывайте, в части обработки ошибок: заворачивайте их в более высокоуровневые либо логгируйте и возвращайте другую;
4) При правильной работе с ошибками разницы нет какой механизм вы используете, если писать корректный код на js с теми же async/await то рука будет болеть к вечеру от написания тонны try {} catch(), особенно если учитывать что там смешан поток обработки ожидаемый ошибочных и неожиданных исключительных ситуаций, и всегда надо будет прокидывать ошибку наверх вручную, если мы не знаем как ее обрабатывать.
У меня складывается впечатление, что автор просто или путает или отождествляет DI/IoC с Service Locator. Надо отделить мухи от котлет. Почему в статье нет пояснения к используемой терминологии?

DI — Dependency Injection, внедрение зависимостей, на самом деле очень банальная вещь: если вы определили конструктор с параметрами, вы уже воспользовались внедрением зависимостей через параметры конструктора. Различают также внедрение зависимостей через метод класса (в котором иногда выделяют две разновидности). Но я бы не рекомендовал пользоваться последним методом без необходимости, т.к. способ внедрения зависимостей через параметры конструктора более нагляден.

IoC — Inversion of Control, наилучшая формулировка, на мой взгляд такая: «Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций». Первая рекомендация вытекающая из такой формулировки это: «Используйте интерфейсы для определения зависимостей». Следование только этому правилу уже позволит построить менее связанную архитектуру, которую очень просто тестировать с помощью модульных тестов и легко изменять реализацию какой-то части, при необходимости.

Очень рекомендую почитать про DI и IoC на англоязычной википедии.

Далее, если у вас очень сложный проект (сотни классов), то вы можете упростить себе жизнь (или усложнить) воспользовавшись Service Locator. Использование Service Locator невозможно без DI и IoC, но не наоборот: если вы используете DI и IoC, то это не означает автоматическое использование Service Locator.
Вы перепутали TTL пакета и TTL ресурсной записи (допустимое время хранения в кеше): https://ru.wikipedia.org/wiki/DNS
Когда начал читать, ожидал что будет несколько методологий.
как можно организовать хеш-таблицу на FPGA

В качестве HDL-языка использовался SystemVerilog и корпоративный кодинг стайл.
Проблема: Если переписать эту функцию на JavaScript “в лоб”, то строка, переданная в качестве аргумента, будет скопирована. При работе с большими строками это приведет к лишней трате процессорного времени на их копирование и увеличению потребления памяти.

Решение в Node.JS: Чтобы предотвратить копирование больших строк при передаче в функцию, пришлось либо переводить их в бинарный формат (Buffer), либо оборачивать в объект и передавать его.

Раза 4 перечитал это место, так и не понял где «собака зарыта». В JS строки неизменяемые и передаются в функции по ссылке. При этом никакого копирования самого значения строки не происходит. Для чего их «оборачивать в объект»?
Как раз наоборот: когда компилятор C++ строит AST и встречает int name..., он не знаете что будет дальше — объявление переменной или функции, поэтому эту информацию надо или запоминать где-то или возвращаться назад по исходнику. К тому же для языков с подобными грамматиками сложнее программировать восстановление после сбоев во время синтаксического анализа. Для паскаля как раз проще, для него отлично подходит обычный леворекурсивный парсер без наворотов.
Для C++ еще есть известный пазл: константный указатель/указатель на константу
int *const p1
int const* p2
const int* p3
Выброс исключения тоже тут не поможет т.к. вы можете ошибиться при его выброса. Если вы можете выбросить исключение, значит вы можете и сразу обработать ошибку и вернуть невалидный результат.
А я и не говорил что это делает программист в коде явно. В 99% случаев непредвиденные исключения выбрасывает рантайм: выход за границы массива, разыменование нулевого указателя и т.п.
Я не вижу, в каких случаях стоит ловить какие-то исключения. Если это segmentation fault — не за чем его ловить, просто падаем.
Расскажите это тем, кто эксплуатирует высоко нагруженные web-сервера, что если вы ошиблись при кодировании и какой-то запрос к серверу приводит в каком-то месте к выходу за границы массива, то в таком случае вы предпочитаете молча падать вместе с парой сотней одновременно обрабатывающихся запросов, не успев даже вернуть 500 — Internal Server Error в ответ на проблемный запрос.
… если это пользовательское исключение — можно его заменить на возвращение invalid value.
А описание ошибки не нужно?
Если я читаю JSON конфиг, то получение вместо него XML — точно такая же непредвиденная ситуация
Для вас будет неожиданностью, что формат конфига может не соответствовать заявленному? Да одна запятая, и все — парсер не поймет ваш конфиг. И XML тут вообще притянут за уши, для парсера JSON без разницы что конфиг — это валидный XML, главное что это не валидный JSON. Потом рассказывайте девопсу, что для вас стало неожиданностью когда он по ошибке подсунул программе чужой xml конфиг, вместо родного JSON, и программа вывалила стектрейс вместо вменяемого сообщения о некорректном месте в конфиге.

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

Не низкому уровню приложения решать какие ситуации являются предвиденными, а какие — нет.
Не припомню чтобы я писал о чем-то подобном. Не могли бы вы более развернуто описать это свое утверждение?
Благодарю, великолепный вопрос.
Когда вы вызываете процедуру, всегда может произойти ошибочная ситуация, и вызов процедуры не приведет к желаемому результату. Теперь подумаем, по какой причине это может произойти?
Например отсутствует файл, неверный формат входных данных, нарушен контракт вызываемым кодом (передан нулевой указатель) — все эти ситуации описаны в документации и должны быть корректно обработаны программистом, они просто являются результатом работы процедуры (предвиденные ошибочные ситуации): когда вы парсите текст — знаете что его формат может не соответствовать ожидаемому, когда открываете файл — знаете что он может отсутствовать и т.д. Более того, вы всегда знаете в чем причина такого поведения, как обработать такие ситуации и где они возникают — выдать сообщение что файл не найден (или создать его), что данные не соответствуют требуемому формату и т.д.
Если же мы возьмем ситуацию к которой приводит логическая ошибка в программе: опечатались, не закодировали возможную ветвь обработки, ошибка в расчетах и т.д., то это непредвиденное исключение, ненормальное поведение программы. Вы никогда не знаете когда и где оно возникнет. Вставка проверок тут не поможет — вы банально можете ошибиться при кодировании самой проверки, да и компилятор сам вставляет подобные проверки для отлова исключительных ситуаций. Если возникает такая ситуация, то лучшее что вы можете сделать (в общем случае) — это корректно завершиться (вы же не знаете где и что произошло и к чему это приведет, если продолжить выполнение, не можете предусмотреть все ситуации, ибо потенциально их очень много). Бороться с подобными ситуациями можно только одним способом — патчем, и только в таком случае нужен стектрейс чтобы отладить программу.
Надеюсь я ответил на ваш вопрос.
Неужели? Почему же тогда в комментарии, с которого началась эта дискуссия автор радостно объясняет нам важность такого разделения, да еще как будто в го первыми до него додумались.

Не фантазируйте пожалуйста. У букв нет эмоций, мы сами окрашиваем текст в те или иные эмоции, когда читаем его. В комментарии я объяснял подход Go к этим вопросам, а решать насколько это важно и актуально должен каждый сам. И то, что в Go это было реализовано впервые, я тоже не писал. Ничто не ново под солнцем, и даже CSP, модель которого повлияла на дизайн корутин в Go, была описана в 1978 г.

Получается, мало того, что идея далеко не нова, так она еще и успела провалиться много лет назад. Зачем же это затаскивают в го? Наступают на те же грабли?

Вот на этом месте остановимся по подробней. Где она успела провалиться? В C++ — based языках? Поправьте меня, если это не так.
Итак, давайте по полочкам.
Я писал уже и повторю еще раз, что С++, C# и Java (и не только — JavaScript, например) используют единую модель при работе с ошибками и исключениями: единый механизм для работы с ними — throw, try-catch-finally; общая иерархия классов ошибок/исключений (C#, Java); даже если вы захотите работать с ошибками как с возвращаемыми значениями, стандартная библиотека (и не только) будет упорно вставлять палки в колеса, ибо так не принято в экосистеме. Да, Java разделяет эти понятия через checked и unchecked exceptions, об этом написано чуть ли не в каждом учебнике, но использует опять же единые механизмы, да и реализация тоже не безгрешна: Mikanor привел уже с пяток проблем вызванных ей. И это данность.
Резюмируем. В C++ и C# вообще не разделяются эти понятия, в Java же хоть и разделяются, но используются единые механизмы для работы с ними, к тому же местами подводит реализация. Так от чего в Java провалилась идея разделения предвиденных ошибок и непредвиденных исключений? Может из-за реализации? Тогда к Go это не имеет ни какого отношения, там другая реализация, там совершенно различаются механизмы (это что касается вопроса «Зачем же это затаскивают в го?»).
И да, если абстрактный программист в вакууме утверждает, что «это плохой дизайн», «слишком много кода (проверок)», «мне нравится работать с ошибками вот так-то» — это вкусовщина, на вкус и цвет все фломастеры разные. Давайте оперировать фактами: «эта реализация приводит к таким-то и таким-то проблемам вот в таких случаях» или «это не работает вот в таких-то и таких-то случаях».
Почитайте про парсеры. И это таки видится более частным применением, нежели по прямому значению — завалить программу.

Благодарю за дельное замечание. Да, механизмы panic/recover используются в стандартной библиотеке для «передачи управления наверх» в сложных алгоритмах, все как вы написали (например, encoding/json/decode.go:199). Но это инкапсулировано в библиотеке и ошибка декодирования будет возвращена вам библиотечной функцией в качестве значения, а при непредвиденной исключительной ситуации произойдет panic (encoding/json/decode.go:139). Если есть механизм, отлично подходящий для решения какой-то проблемы, почему бы им не пользоваться? Но только это не должно влиять на клиентский код и интерфейс-то все равно должен соответствовать общепринятым в экосистеме рекомендациям.
Наверное, когда дело доходит до panic/recover, то ключевое слово здесь «наверх» (передача управления наверх). Не в вызывающую процедуру, а на верхний уровень библиотеки/обработчика запроса/программы.
Возможно, некоторые не понимают разницу между «предвиденной ошибкой» и «непредвиденной исключительной ситуацией». Путают или смешивают эти понятия, что не мудрено, ибо C++, C#, Java и др. идеологически используют единый подход для работы с ними: throw, try-catch-finally.
Какая же разница между этими понятиями? Простой пример. Допустим, вы хотите прочитать данные из файла. Эта операция может завершиться как успехом, так и может произойти ошибка: файл не существует, недостаточно прав и т.д. Все эти ситуации определены и описаны, они не должны приводить к падению и должны обрабатываться программой: выдать сообщение об отсутствии файла, запросить привилегии и т.д.
Возьмем теперь непредвиденную исключительную ситуацию: разыменование нулевого указателя. Никакой программист в здравом уме не будет делать это нарочно, это может произойти только в результате непредвиденной ошибки в коде. Предусмотреть подобные ошибки нельзя и они могут произойти в любом месте. Что же с ними делать? После такой ошибки практически невозможно восстановить нормальное поведение программы и единственное что можно сделать, это упасть как можно скорее и не усугублять дело, предварительно записав/отправив отладочную информацию (стектрейс, дамп и т.п.) и, по возможности, приведя данные к консистентному виду и освободив ресурсы. И да, я понимаю что в случае с веб-сервером, например, программа не должна падать, но смысл похожий — надо немедленно завершить обработку запроса и вернуть Internal Server Error.
Вернемся к Go. Он идеологически разделяет подход к работе с этими сущностями. Для предвиденных ошибок — это возвращаемое значение, это такой же результат работы процедуры и вы должны его обрабатывать (вы же не игнорируете результат, который возвращает процедура поиска первого вхождения подстроки в строке, даже если это "-1"?). Для непредвиденных исключительных ситуаций — это defer, panic и recover.
Go бескомпромисен, он побуждает, или даже, вынуждает программиста использовать те практики, которые считаются хорошими в мире Go. Если у вас вызывает дискомфорт такое отношение или если для вас хорошие практики отличны от того, что принято в Go, то конечно же у вас будет негативное впечатление от этого ЯП. Но это вопрос вкусов и к техническим деталям не относится. Да, Go местами сильно отличается от мейнстримовых ООП ЯП. Но разве не разумно иметь разный подход к работе с разнородными сущностями и не смешивать их?
Вот смотрите, вы же в C# понимаете отличия struct от object? Где они располагаются, как передаются в качестве параметра? Также, думаю, вам вполне понятны значения модификаторов ref и out? А давайте добавим сюда еще unmanaged код, где можно проводить операции с указателями, stackallock и fixed. Получается что в C# тоже не все просто. Кому-то тоже это может не нравится, а кто-то настолько привык, что даже не замечает. Видимо дело в привычке.
На мой взгляд, интересное видео по теме:
Видел интервью с одним инженером, основателем своего бизнеса в области акустических систем. Там больше говорилось про бизнес-модель. Но и заикался он про кучу имеющихся патентов, в частности говорил что у него есть технологии, позволяющие изготавливать совершенно плоские колонки (толщиной как лист обычного ватмана и практически любой площади) с высочайшим качеством звучания. Еще он говорил, что не развивают это направление только потому, что в представлении обывателя дорогая акустическая система — это массивные, деревянные колонки, а не несколько листочков бумаги на стенах. Если найду видео, кину ссылку.
Несколько месяцев назад наткнулся на аналогичный материал от наших ученых:
Видео длится пол часа, но чтобы понять о чем речь, достаточно посмотреть минут 10.
И вот теперь, в России эти разработки блокируют не нужны, но они обретают новую жизнь за границей.
Всем надо пользоваться с умом. Есть соглашение, об этом написано в статье Effective Go — Errors, что сигнатуры библиотечных методов и функций должны соответствовать определенному формату, а именно, возвращать error, если может иметь место исключительная ситуация. Но внутри кода своей библиотеки вы можете использовать и механизм panic — recover, когда это оправдано (даже для обработки предвиденных ошибок).
Например есть цепочка вызовов функций и исключительная ситуация может возникать где-нибудь на глубине пятого уровня. Чтобы не пробрасывать вручную и не писать в каждой функции проверки типа if err {return err}, уместно использовать panic и просто отловить проброшенное исключение на верхнем уровне. Почему это будет дорого? Ведь .NET и Java используют именно такую модель обработки ошибок. А каком геморрое вы говорите? И почему это будет не Go-way? Ведь если существуют подобные механизмы языка, значит ими можно пользоваться, но и это не значит что их следует пихать везде, где вздумается. Более того, такой способ используют сами разработчики, например в пакете regexp, вот здесь кратко описано что, где и зачем: Effective Go — Recover
1

Information

Rating
Does not participate
Location
Барнаул, Алтайский край, Россия
Date of birth
Registered
Activity