• Как избавиться от длинного имени класса БЭМ?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Необходимо гранулярнее делить на блоки, тогда часть специфичной семантики из названия элемента уедет в новый блок (что укоротит вложенные элементы).

    Текстовая часть напрашивается быть независимым блоком, причем, маркированным списком. Например, структура может быть такой:
    .advantages-card (&--reversed)
        &__description // текстовая часть
        &__picture
    
    .bullet-list
        &__item
        &__item-heading
        &__item-description

    То есть в разметке это будет что-то вроде:
    <div class="advantages-card">
      <ul class="bullet-list advantages-card__description">
        <li class="bullet-list__item">
          <header class="bullet-list__item-heading">User Journey</header>
          <p class="bullet-list__item-description">Description long text</p>
        </il>
      </ul>
      <img class="advantages-card__picture" :src="block.imgURL">
    </div>


    Кроме того, я бы рекомендовал избегать сокращения "description" в "desc", потому что туда идеально укладывается не менее популярный термин "descending".
    Ответ написан
  • Как вести поддержку/документацию монстр-проекта?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Разница должна быть обязательно расширением базовой функциональности (статически - через наследование), либо работать через стандартизированный интерфейс (динамически - API для плагинов).
    Костыли с кучей if лучше заменять полиморфизмом, используя IoC via DI и идею паттерна Стратегия.

    Детальную девелоперскую документацию вести нет смысла, как показывает практика - ее трудно поодерживать. Можно завести вики или даже документы, в общих чертах описывающих работу тех или иных неочевидных узлов.
    С точки зрения логики работы приложения, в сурёзных конторах QA пишут тест-кейсы, которые фиксируют требования. Это бывает полезно, чтобы освежить память. (тест-кейсы не устаревают, ибо каждый релиз прогоняется полный регрессионный цикл)
    Ответ написан
  • В чем отличие новой программы обучения ISTQB 2018 от старой 2011 года?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Глас QA обо всём и сразу:
    Польза сдачи на английском:
    1. + 15 дополнительных минут, это реально поможет на экзамене
    2. некоторые английские термины плохо адаптированны на русский язык, в экзамене будут вопросы из глоссария, которые на англ отличаются 2 словами,но в ISTQB это всё важно, поэтому можно в переводе не сильно адаптированном, ошибиться и потерять баллы на сдаче
    3. демо экзамены уже ДАВНО залиты на сайт ISTQB https://www.istqb.org/downloads/category/2-foundat... и на GASQ https://www.gasq.org/ru/%D1%81%D0%B5%D1%80%D1%82%D...
    4. Прорешать обязательно сэмплы 2018 и 2011 годов, 2018 в первую очередь, осбенно задачи. На экзамене задачи однотипные с семплами
    5. 2011 и 2018 отличаются, что в некоторых терминах, понятиях поменяли места словами, но и это незначительное изменение может сыграть злую шутку на экзамене. Поэтому, если экзамен по 2018 (а уже вроде по 2011 нельзя сдавать в 2020), то читать нужно 2018 книгу
    Ответ написан
  • Entity Framework - не нужен?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    EF вам дает возможность автоматически транслировать экспрешны в диалекты SQL и маппить это на удобные дотнетные сущности. С голым ADO.NET вы будете делать то же самое, но руками.

    куда логичнее было бы, чтобы в объекте Order было поле типа Client, в котором лежит объект компании-получателя, а не условный int с идентификатором.

    Предлагаемая вами схема денормализована, вообще говоря, это не очень хорошо. Но посмотрите на Owned Types для EF Core и на Complex Types для EF6, мне кажется, что это то, что вы хотели бы - "встраивать" один объект в другой, сохраняя всё в единой таблице.

    Да, EF не то, чтобы позволяет сильно отходить от классической реляционной модели, но с ним можно точечно звать raw sql-queries, что даст больше гибкости там, где это необходимо.
    Ответ написан
  • Пoдскажите базовые правила работы с миграциями в asp.net?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Проблемы все те же, что и для ручных миграций, только пишутся они сами и все действия фиксируются в миграциях.

    Как вариант решения: коммитить все миграции, ничего не откатывая. Когда пришла пора мержиться выше (из таск-бранча в стори-бранч, из стори-бранча в фичу, из фичи в девелоп, из девелопа - в релиз/мастер или что у вас там) - приводим в порядок миграции: удаляем промежуточные, делаем одну новую миграцию - финальную и мержим. И так для каждого мержа с изменением схем. Таким образом, у нас каждый мерж в релиз/мастер будет означать максимум одну миграцию, что выглядит вполне адекватно.

    Процесс "приведения в порядок" можно закриптовать, поэтому сильно большой проблемой это не должно быть.
    Ответ написан
  • Как сделать обновление миграции EFCore?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    При каждом изменении моделей и/или их связей, нужно сначала сгенерировать очередную миграцию (Add-Migration), а затем - накатить изменения на схему (Update-Database).

    В CLI будет так:
    dotnet ef migrations add NewMigrationName;
    dotnet ef database update;


    Подробнее читать тут.
    Ответ написан
  • Как на самом деле работают типы данных в js?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Схема в доке v8 говорит что у int всего 2 типа, int32 и unit32, а int8, int16, float32 etc. есть только у массивов.

    Логично, что int не может быть float (=

    В статье про оптимизацию говорилось верно, что Number до некоторого размера может храниться более оптимально. Кусочек кода, который дёргает парсер, когда встречает литерал:

    Handle<Object> Factory::NewNumber(double value, AllocationType allocation) {
      // Materialize as a SMI if possible.
      int32_t int_value;
      if (DoubleToSmiInteger(value, &int_value)) {
        return handle(Smi::FromInt(int_value), isolate());
      }
      return NewHeapNumber(value, allocation);
    }



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

    Неправда потому, что значение иммутабельно в данном случае и будет выделение третьего куска памяти под результат. И одно лишь наличие точки не делает число не целым (что вполне объяснимо, ибо JS-юзер разницы не видит by design).

    По коду сложения чисел видно, что для описаных вами случаев оба объекта приводятся к HeapNumber (aka "большой Number", но не путать c BigInt) и складываются, а ссылка начинает указывать на новый объект (даже в случае укороченной записи, ибо a += b <=> a = a + b).

    Задействованная ветка:
    TF_BUILTIN(Add, AddStubAssembler) { // Операция сложения
      ....
      BIND(&if_left_smi);               // Левый операнд - "маленький" int
      {
        .....
        BIND(&if_right_heapobject);     // Правый операнд - HeapNumber
        {
          ....
          var_left_double.Bind(SmiToFloat64(left));
          var_right_double.Bind(LoadHeapNumberValue(right));
          Goto(&do_double_add);
          .....
        }  // if_right_heapobject
      }    // if_left_smi
    
      ....
    
      BIND(&do_double_add);
      {
        Node* value = Float64Add(var_left_double.value(), var_right_double.value());
        Return(AllocateHeapNumberWithValue(value));
      }
    }
    Ответ написан
  • Верстка по BEM. Файловая структура. Как присваивать классы независимому блоку?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Вообще-то блоки независимы именно потому, что они должны выглядеть одинаково в разных частях страницы. Если блок должен иметь какой-то особый внешний вид "по месту", то это должны быть разные блоки. БЭМ не допускает селекторы вида: .блок1 .блок2 {...}

    Если сам блок допускает какие-то режимы своего отображения (скажем, раскрывающееся меню может раскрываться в разные стороны), то это может задаваться с помощью БЭМ-модификаторов блока, но не снаружи.
    Ответ написан
  • Что учить дальше после основ C#?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Базы данных и СУБД, архитектурные паттерны, платформу .NET вглубь. Дальше - в зависимости от интересующей специфики.
    Ответ написан
  • Добавление записей в доп. таблицу, имеющей связь с основной таблицей?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    var newPerson = new People {
       //Id = model.Id,
       Name = model.Name,
       Treatment = new Treatment {
            //...
       },
    };
    
    dbCcontext.People.Add(newPerson);
    dbCcontext.SaveChanges();
    Ответ написан
  • Как залогинится на сайт с помощью Python Selenium, сайт каждый раз меняет id, name, xpath, selector?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Судя по коду фронта, этот сайт работает на Wt, а потому сам занимается маппингом id/name для элементов формы, прегенерируя их на бэкенде. Вам необходимо искать по классам, по такому принципу:
    //div[contains(@class, 'loginWindow')]//input[contains(@class, 'line-height-wide')][1]

    Обратите внимание, что используется предикатная функция contains, т.е. ищет подстроку и могут появиться лишние совпадения, вроде loginWindow__title. Есть еще функции starts-with и ends-with и остальные, не менее полезные штуки. Гуглите в сторону XPath. Два слэша означает: "где-то среди детей на любой глубине"

    Я напишу на C#, вы адаптируйте под свой питон:

    const string loginFormPath = "//div[contains(@class, 'loginWindow')]";
    const string inputFieldSelector = "input[contains(@class, 'line-height-wide')]";
    
    const string nameFieldPath = loginFormPath + "//" + inputFieldSelector + "[1]";
    const string passwordFieldPath = loginFormPath + "//" + inputFieldSelector + "[2]";


    Либо аналогичное с CSS-селекторами
    const string loginFormPath = ".loginWindow";
    const string inputFieldSelector = "input.line-height-wide";
    
    const string nameFieldPath = loginFormPath + " " + inputFieldSelector + ":nth-child(1)";
    const string passwordFieldPath = loginFormPath + " " + inputFieldSelector + ":nth-child(2)";


    ЗЫ Самая популярная новичковая проблема: если когда-нибудь в процессе работы встретите iframe, то на него необходимо явно переключиться, селекторы через эту границу работать не будут.
    Ответ написан
  • Функция для проверки значений JS. Есть ли стандартная или как правильно свою написать?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Если нас помимо undefined не устраивает null, 0 и "" (что неявно приводится к false), то обычно применяют такой паттерн:
    const value = currentValue || defaultValue;
    Например:
    const productCategory = categoryInput.value || "N/A";
    Ответ написан
  • Как обучить тестировщика?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Как нам действовать?

    Работа по блату, курсы с практикой (и/или возможным трудоустройством), литература по подготовке к проф.сертификации.

    146% понадобится английский Intermediate+.

    И никакого эникейства или техподдержки! Даже совмещением! Нужен только профильный опыт.
    Ответ написан
  • Что с точностью float?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Получается float не надёжен?

    Смотря кому, когда и как. Всё зависит от того, какая точность важна в конкретной задаче. Деньги на нем не считают, но для большого круга задач этой точности вполне хватает, а эффективность кода с double гораздо выше, чем decimal.

    float нужен лишь затем, что этот тип занимает вдвое меньше памяти, чем double. Где-то это может сыграть решающую роль.

    ЗЫ В конкретной ситуации вас спасёт Math.Round
    Ответ написан
  • Как правильно передавать класс компоненту?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    В идеале компонентного подхода классы извне передавать не нужно. Компонент сам знает, как ему нужно отображаться. Чтобы его спозиционировать - оборачиваем и воздействуем на обёртку.

    export function css(...classNames) {
        let result = [];
        for(let i=0; i<classNames.length; ++i) {
            if (Array.isArray(classNames[i])) {
                result = result.concat(classNames[i]);
            } else {
                result.push(classNames[i]);
            }
        }
        return result.join(' ');
    }

    import * as React from 'react';
    import {css} from '../utils.js';
    
    import './MyComponent.scss';
    const BLOCK_CSS = 'my-component';
    const BLOCK_MODIFIED_CSS = BLOCK_CSS + '--modifier';
    const ELEMENT_CSS = BLOCK_CSS + '__element';
    
    
    export class MyComponent: React.Component {
        ...
        public render() {
            const blockStateCss = [BLOCK_CSS];
            
            if (this.state.modificationNeeded) {
                blockStateCss.push(BLOCK_MODIFIED_CSS);
            }
    
            return (
                <div className={css(blockStateCss)}>
                    <div className={css(ELEMENT_CSS)}>
                        ...
                    </div>
                </div>
            );
        }
    }
    Ответ написан
  • Какую архитектуру выбрать для программы на Qt?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Я бы посоветовал:
    • Не делать главный класс окна божественным объектом. Его нужно разделить на контролы-модули с их собственной кастомной логикой, валидацией и прочим, а связать между собой - сигналами и слотами.
    • Отделять бизнес-логику от интерфейса. Для чего использовать внедрение зависимостей и абстрактные (чисто виртуальные) классы для декларирования API.


    Допустим, парсером парсится конфиг в объект настроек, эти настройки передаются в фабрику, которая с оглядкой на них будет выдавать правильно сконфигурированные объекты по запросу. Ссылка на фабрику передается в класс окна, откуда добываются зависимости и передаются дальше контролам по ссылке. Контролы, получая зависимость (допустим, через конструктор), привязываются к ней через сигналы/слоты или непосредственно вызывают виртуальные методы.

    Итог: интерфейс декомпозирован, о бизнес-логике он не знает ничего, кроме предоставленного абстрактного API. Контролы знают друг о друге только в рамках сигналов/слотов между собой и списка зависимостей своих детей. Бизнес-логика не знает ничего об интерфейсе.
    Ответ написан
  • Как правильно написать этот код/как оптимизировать это "код"?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Первое, что нужно сделать - определить доменные сущности, с которыми будет работать программа. В вашем случае это:
    • тест, который состоит из вопросов;
    • вопрос, который состоит из текста и ответов (упорядоченные по индексам);
    • ответ, котрый состоит из текста и признака правильности.
    Код
    public class Test
    {
        public IList<Question> Questions { get; set; }
    }
    
    public class Question
    {
        public string Text { get; set; }
        public IList<Answer> Answers { get; set; }
    }
    
    public class Answer
    {
        public string Text { get; set; }
        public bool IsRight { get; set; }
    }



    Необходимо декомпозировать задачу на атомарные подзадачи. Каждую подзадачу можно унести в отдельную функцию, чтобы ограничить сферы ответственности кода (в духе SOLID в общем и SRP в частности), чтобы обезопасить себя от "эффекта бабочки", когда небольшое изменение отдого кусочка кода в одной части приложения рушит остальную работу в другой части. Да и читать код будет проще, не путаясь в деталях реализации из огромной портянки неразрывного кода с неочевидными связями переменных. Получается примерно так:

    1. логика "главного меню";
    2. логика теста:
      1. создание контента теста;
      2. прохождение теста:
        1. распечатка вопроса;
        2. получение ввода пользователя;
        3. проверка ответа;
        4. подсчет и вывод статистики.



    Затем необходимо сформировать структуру теста. Для этого можно создать функцию-фабрику, которая возвращает новый объект теста (данные хардкодятся как здесь, читаются из базы, ходят за ними в интернет... whatever, нас интересует само тестирование):
    Код
    public static Test CreateTest()
    {
        return new Test
        {
            Questions = new List<Question>
            {
                new Question
                {
                    Text = "Какая команда выводит текст в консоль?",
                    Answers = new List<Answer>
                    {
                        new Answer { Text = "if/else" },
                        new Answer { Text = "System.out.println();" },
                        new Answer { Text = "Console.WriteLine();", IsRight = true },
                        new Answer { Text = "int x = 10;" }
                    }
                },
                new Question
                {
                    Text = "Какой тип из списка целочисленный?",
                    Answers = new List<Answer>
                    {
                        new Answer { Text = "char" },
                        new Answer { Text = "int", IsRight = true },
                        new Answer { Text = "float" },
                        new Answer { Text = "double" }
                    }
                }
                //,...
            }
        };
    }


    Пусть программа, как у вас, приветствует пользователя, а затем, пока он не введет какой-то из двух корректных выводов (регистронезависимо (...IngoreCase)), будет показывать приглашение с подсказкой. Если Go - вызываем создание теста и заходим в игру. Если игра завершилась или пользователь изначально ввел Exit - печатаем знаменитое Press any key... и выходим из программы.
    Код
    public static void Main()
    {
        Console.WriteLine("Всем привет! И это моя первая мини-программа для проверки ваших знаний языка программирования C#");
        string input;
        
        do
        {
            Console.WriteLine("Если готовы пройти тест напишите Go, а если хотите выйти напишите Exit.");
            input = Console.ReadLine();
    
            if (input.Equals("Go", StringComparison.InvariantCultureIgnoreCase))
            {
                var test = CreateTest();
                PlayGame(test); // <----- Логика тестирования находится здесь
            }
        } while (   !input.Equals("Go", StringComparison.InvariantCultureIgnoreCase)
                 && !input.Equals("Exit", StringComparison.InvariantCultureIgnoreCase));
    
        Console.WriteLine("Для выхода нажмите любую клавишу...");
        Console.ReadKey();
    }


    Для игры нам потребуется массив с номерами "проваленных" вопросов (которые пригодятся для статистики в конце - CalculateResult). Мы обходим все вопросы из теста, сначала выводя их текст с вариантами на экран (PrintQuestion), затем спрашивая юзера его выбор (GetUserChoice). Затем ответ проверяется (IsCorrectAnswer), если он неверен - добавляем номер ответа (i - это индекс массива с нуля, поэтому +1) в массив ошибок. После всех вопросов - подсчитываем и выводим статистику.
    Код
    private static void PlayGame(Test test)
    {
        var mistakes = new List<int>();
        for (int i = 0; i < test.Questions.Count; ++i)
        {
            var question = test.Questions[i];
            PrintQuestion(question, i);
    
            int choice = GetUserChoice(question.Answers.Count);
    
            if (!IsCorrectAnswer(choice, question.Answers))
            {
                mistakes.Add(i + 1);
            }
        }
    
        CalculateResults(test.Questions.Count, mistakes);
    }


    Вопрос распечатывается элементарно: сначала его текст, затем варианты ответов, перед которыми ставиться табуляция (\t), чтобы сформировался отступ списка:

    Код
    private static void PrintQuestion(Question question, int questionIndex)
    {
        Console.WriteLine($"{questionIndex + 1}. {question.Text}");
    
        for (int i = 0; i < question.Answers.Count; ++i)
        {
            Console.WriteLine($"\t{i + 1}. {question.Answers[i].Text}");
        }
    }


    Получение варинта юзера делается циклом аналогичным вводу Go/Exit в начале. Его завершением управляет переменная isCorrectInput, которая ставится равной true только в самом конце, когда значение успешно спарсится и пройдет проверку на валидность номера ответа (от 1 до максимального номера ответа):
    Код
    private static int GetUserChoice(int answersCount)
    {
        bool isCorrectInput = false;
        int choice = -1;
        do
        {
            try
            {
                Console.WriteLine("Введите номер ответа:");
                string input = Console.ReadLine();
                choice = Convert.ToInt32(input);
                if (choice >= 1 && choice <= answersCount) isCorrectInput = true;
            }
            catch (Exception ex)
            {
                Console.WriteLine("Неправильный формат ввода");
            }
        } while (!isCorrectInput);
    
        return choice;
    }


    Для проверки правильности сначала выделяем правильные ответы вопроса (rightAnswers), а потом сравниваем их порядковые индексы с выбранным пользователем индексом. Если хоть один ответ совпал (да, их может быть несколько) - вернется true:
    Код
    private static bool IsCorrectAnswer(int choice, IList<Answer> answers)
    {
        var rightAnswers = answers.Where(x => x.IsRight);
        int chosenAnswerIndex = choice - 1;
        return rightAnswers.Any(x => answers.IndexOf(x) == chosenAnswerIndex);
    }


    Ну и вишенка - распечатка результатов. Если ошибок нет - все гуд, 100% решений. Если ошибки есть, то мы решаем простую пропорцию, вычисляя сколько процентов ошибочных ответов было дано, а затем вычитаем их из 100%, получая процент успеха.
    Код
    private static void CalculateResults(int questionsCount, IList<int> mistakes)
    {
        if (mistakes.Count == 0)
        {
            Console.WriteLine("Вы на все вопросы ответили правильно! Тест пройден на 100%! ");
        }
        else
        {
            string invalidQuestionsString = string.Join(", ", mistakes.Select(x => $"#{x}"));
            int progressPercentage = 100 - (int)Math.Ceiling(100 * (double)mistakes.Count / questionsCount);
            Console.WriteLine($"Среди ваших ответов есть неправильные ({mistakes.Count}: {invalidQuestionsString})). Тест пройден на {progressPercentage}% ");
        }
    }



    ЗЫ Вы можете поиграть с кодом, попробовать добавить больше вопросов, принудительное прерывание после первого неправильного ответа, обработку множественных корректных ответов (добавлять или баллы за частичное угадывание), разнообразить типы вопросов и прочее.

    Полный листинг на ideone
    Ответ написан
  • LINQ или foreach?

    @Free_ze
    Пишу комментарии в комментарии, а не в ответы
    Скорость отработки запросов на LINQ существенно (в разы) ниже, чем прямой перебор коллекций, однако мы, разработчики, внедряем всякие умные Parallel LINQ или начинаем заниматься разделением потоков вместо того, чтобы не писать LINQ-запрос, а просто перебрать коллекцию

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

    однако там даже ORM-подхода нет

    Не от лучшей жизни же. Да и сейчас, если дотнет брать, достаточно популярен тот же Dapper. Что так же не мешает совмещать EF с Dapper в одном приложении. Возможно, вам не попадались задачи, где игра стоила бы свеч.

    код на LINQ и прочих “упростителях жизни” проще тестировать

    Напротив, функциональные цепочки вызовов в отладке сложнее императивного кода.

    ЗЫ Разработчику нужно обязательно:
    1. знать, как работают его "улучшайзеры" на уровне имплементации, чтобы прогнозировать сложность алгоритмов;
    2. оценивать требования по производительности для отдельных участков ПО;
    3. грамотно проектировать архитектуру, позволяющую проводить рефакторинг в изоляции;
    4. НЕ заниматься микрооптимизациями и НЕ писать велосипеды без нужды.


    Озвученный вопрос - сугубо технический. А все эти: "Воткнуть еще один сервер, они ж дешевые!" - означает факап бизнеса, который изначально сэкономил на компетентных специалистах, а теперь вынужден деньгами затыкать дыры из-за неверных технических решений. Такие конторы стоит обходить стороной, а поддерживающих идею коллег - гнать тряпками и насмехаться.
    Ответ написан