• Как привести переменную к переменному типу?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    В других языках ЭТО реализуется так, как этого требует система типов конкретного языка, с помошью плюшек, предоставляемых самим языком и/или его стандартной библиотекой :) Подробности (например): раз, два, три. На практике это может быть что угодно - от велосипедной эксплуатации автобоксинга или перегрузки операторов / сигнатур методов, через рефлексию, дженерики, темплейты (в плюсах), или оберточные типы (как в Java) и вплоть до реальной компиляции "на лету" в каком-нить Groovy. Последнее, семантически - тот же eval, чреватый теми же побочными эффектами.

    Тут фантазия разработчика, владеющего конкретным языком, ограничена, разве что, здравым смыслом, который, в первую очередь, призывает задуматься, а нафига это вообще нужно :) И, что характерно: в подавляющем большинстве случаев (если, конечно, мы не пишем како-нибудь интерпретатор или полиморфный вирус) эти размышления приводят к тому, что все решается проще и надежнее именно статической типизацией... т.е. да - где-то таки придется написать switch case :)
    Ответ написан
    Комментировать
  • Что за ility testing?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Гугл, в данном случае, как почти всегда, прав )) Речь идет о т.н. нефункциональных требованиях, т.е. таких, которые нельзя вот так вот взять, проверить тестом, и убедиться, что "оно работает, как заказывали". Это суть требования качественного характера, предъявляемые к архитектуре в целом, или к отдельным группам функциональных и/или системных требований. Например: "горизонтальное масштабирования хранилища данных не должно влиять на время реакции системы для операций регистрации новых и удаления ранее зарегистрированных пользователей", или, "система должна быть с минимальными затратами переносима на 64-битную архитектуру"... или еще круче: "разрабатываемая система должна обеспечивать легкость сопровождения кода новыми разработчиками".

    Термин "Ility" происходит из (ныне устаревшего) ISO/IEC-9126, разросшегося в целое семейство стандартов ISO/IEC-250*, которые перечисляют и классифицируют все мыслимые и немыслимые аспекты качества ПО и даже определяют методику их оценки (SQuaRE)... правда, эта методика настолько туманна, что после ее прочтения растет число самоубийств остается больше вопросов, чем было до него )) В самой же классификации, большинство аспектов верхнего уровня заканчиваются на "ility" - Testability, Reliability, Portability и т.д. - oтсюда и название.

    Однако, если есть требование, QA должен хоть извертеться на пупке, но найти возможность проверить/подтвердить, выполнено оно или нет... и такие возможности действительно есть. Только вот "тестированием" назвать их можно весьма условно. Скорее, речь идет о методиках анализа кажущихся на первый взгляд субъективными, характеристик качества ПО на основе его объективных характеристик - от мнений независимых экспертов, проводящих аудит (архитектуры, процессов разработки и системы управления качеством на предприятии), и вплоть до результатов структурного анализа архитектуры и статического/динамического анализа кода (в качестве примера последнего подхода: SQALE).
    Ответ написан
    1 комментарий
  • Как канонично организовать структуру таблиц в базе данных MySQL?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Вот Вам пример сильно нормализованной модели под заявленные хотелки (насколько я их понял).
    e480bdfe4acd4f2c9887ed7f6970beb6.png
    SET FOREIGN_KEY_CHECKS=0;
    
    CREATE TABLE department
    (
    	id INT NOT NULL,
    	name VARCHAR(50) NOT NULL,
    	PRIMARY KEY (id)
    ) 
    ;
    CREATE TABLE department_property
    (
    	department_id INT NOT NULL,
    	property_id INT NOT NULL,
    	UNIQUE KEY UQ_department_property_department_id_property_id(department_id, property_id)
    ) 
    ;
    CREATE TABLE employment
    (
    	id INT NOT NULL,
    	worker_id INT NOT NULL,
    	plant_id INT NOT NULL,
    	PRIMARY KEY (id),
    	UNIQUE KEY UQ_employment_worker_id_plant_id(worker_id, plant_id)
    ) 
    ;
    CREATE TABLE employment_department
    (
    	employment_id INT NOT NULL,
    	department_id INT NOT NULL,
    	UNIQUE KEY UQ_employment_department_employment_id_department_id(employment_id, department_id)
    ) 
    ;
    CREATE TABLE plant
    (
    	id INT NOT NULL,
    	name VARCHAR(50) NOT NULL,
    	PRIMARY KEY (id)
    ) 
    ;
    CREATE TABLE property
    (
    	id INT NOT NULL,
    	name VARCHAR(50) NOT NULL,
    	value VARCHAR(50) NOT NULL,
    	PRIMARY KEY (id)
    ) 
    ;
    CREATE TABLE worker
    (
    	id INT NOT NULL,
    	name VARCHAR(50) NOT NULL,
    	PRIMARY KEY (id)
    ) 
    ;
    SET FOREIGN_KEY_CHECKS=1;

    Но, как уже сказали, нормализация не является самоцелью! С запросами к этой модели у программиста уже возникнет некоторая Камасутра, а будут ли они оптимальны по перформансу - вообще отдельная тема. Так что, не устану повторять: оптимизация структуры БД заключается не в достижении максимально возможной NF, а в том, чтоб на ней оптимально выполнялись именно те запросы, которые на ней должны выполняться ))
    Ответ написан
    5 комментариев
  • Что такое end-to-end тестирование?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Понятие еnd-to-end обозначает всего-навсего классификацию тестов по уровню, на котором тестируется система, и, само по себе, ничего не говорит ни о том, какие конкретно должны быть эти тесты, ни о том, какую роль они играют в общей стратегии обеспечения/проверки качества и, также, не является методикой тестирования. (Методика - это совсем другое понятие.)

    Для понимания сути этого понятия хорошо сравнить его с модульным ("нижний" уровень) и интеграционным ("средний") тестированием на каком-нибудь конкретном примере. Давайте рассмотрим некий сферический webshop в вакууме. Предположим, в нем есть 50 классов и для большинства из них написаны модульные тесты. Они проверяют исключительно функционал конкретного модуля (чаще всего, класса), т.е. тот, что зависит только от самого модуля и ни от чего чего более. Потом есть интеграционные тесты. Они проверяют корректность работы отдельных "модулей", если их собрать вместе согласно архитектурe. Например, работает ли правильно "Корзина", состоящая, в свою очередь, из 10 классов (предварительно проверенных модульными тестами), или "Корзина", подключенная к "Вебморде" и т.д. Где-то повыше в этой иерархии есть такие интеграционные тесты, которые проверяют конкретный функционал всей системы. Например, отправляется ли юзеру мейлом копия оплаченного заказа...

    И вот тут начинается самое интересное для понимания того, что такое end-to-end тестирование! Можно представить себе тест, проверяющий, что соответствующий мейл генерируется и сбрасывается SMTP серверу. Если SMTP сервер не рассматривать, как часть разрабатываемой системы, то этот тест вполне можно назвать end-to-end тестом (послали кучку HTTP запросов через "Вебморду" и проверили сброс мыла на SMTP - все зашибись!). Однако, если настройки и эксплуатация SMTP сервера - часть проекта (например, заказана разработка webshop "под ключ"), может оказаться, что это мыло будет отфильтровано каким-нибудь спам-фильтром, превысит лимит почтового ящика пользователя... короче, не дойдет до него. Тогда этот же самый тест уже нельзя считать end-to-end, а нужно бы было написать тест, проверяющий приход мыла в POP3/IMAP ящик. (Опять же, если это действительно нужно! Ибо, в зависимости от конкретных функциональных и нефункциональных требований, архитектор и QA инженер вполне могут найти возможность обеспечить адекватный контроль качества и без такого теста.)

    Таким образом, end-to-end тесты, это такие интеграционные тесты, которые воздействуют на систему через ее самые внешние интерфейсы и проверяют ожидаемую реакцию системы через эти же интерфейсы. Почему именно интеграционные? Потому, что это единственное, что можно о них сказать наверняка: они по определению не могут быть модульными тестами. А все остальное: являются ли они одновременно приемочными, нагрузочными или еще какими - зависит только от общих плана/стратегии тестирования и той роли, которые эти тесты в них играют.
    Ответ написан
    Комментировать
  • Привязана ли команда SUM (mysql) к оператору SELECT FROM?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Хорошо, что проблема разрешилась, но - просто для ясности... Оператор, точнее, аггрегирующая функция SUM() связанa с указанным полем того множества, из которого производится SELECT, и суммирует значения этого поля для записей, прошедших все фильтры (WHERE и HAVING). В простейшем случае это одна таблица (тогда подразумевается GROUP BY по первичному ключу, т.е., эффективно, вообще никакого), но может быть и JOIN, и подзапрос и.д. и тогда уже, конечно, нужно явно указывать GROUP BY, который будет относиться к одному или нескольким полям ЭТОГО ЖЕ множества. Проще говоря, аггрегирующие функции относятся к множеству, полученному в результате группировки и применяются к тем записям, которые были "свернуты" этой группировкой.
    Ответ написан
    Комментировать
  • «Vanilla» C ,что это?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    В общем, это означает "чистый C" (только хардкор, без плюсов и прочих плюшек). Часто под этим понимается ANSI C, но конкретное, возможно, даже еще более суровое толкование, может зависеть от контекста... например, MISRA C:1998 ))
    Ответ написан
    Комментировать
  • UML Class diagramms - как показать, что некоторые классы лежат в другой директории?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Разумеется, с помощью элемента package! В UML это абстракция, в принципе, для группировки чего угодно, но она, в частности, соответствует 1:1 семантике package в Java, которая, в свою очередь, соответствует тому, в каких папках файловой системы лежат классы ))
    Ответ написан
    Комментировать
  • Как в C++ распределяется память?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Смотря что именно подразумевается под "создать массив" )) Вот хорошая шпаргалка на тему, что это может означать и где он в результате окажется:

    int arr1[100000]; // BSS
    vector<int> arr2; // HEAP
     
    struct DumbStruct {
        int someArr[10000];
    };
     
    int main () {
        int arr3[100000]; // STACK
        vector<int> arr4; // HEAP
        int* arr5 = new int[100000]; // HEAP
        int* arr6 = (int*) malloc(100000 * sizeof(int)); // HEAP
        static int arr7[100000]; // BSS
        DumbStruct struct; // STACK
        DumbStruct* struct2 = new DumbStruct(); // HEAP
        vector<DumbStruct> structarr; // HEAP
        int n;
        scanf("%d", &n);
        int arr8[n]; // STACK (assuming C99 -- this does not compile in C++)
    }


    Если речь именно о динамическом выделении памяти и именно о С++, то есть два варианта: malloc()/free() и new[]/delete[]. Теоретически в случае фрагментации памяти (когда ее, в принципе, достаточно, но не "одним куском") ни тот, ни другой не "заполнит" никаких "свободных" ячеек. malloc() вернет NULL, a new[] кинет std::bad_alloc.

    А практически, конечно, возможно, что угодно... т.к. 1. new - это оператор, и значит, его можно переопределить, 2. с помошью std::set_new_handler ему можно подсунуть свой аллокатор, а также 3. полезет ли new[] вообще к malloc(), строго говоря, не гарантируется и, наконец 4. реализация malloc(), вообще-то, зависит от ОС, а они нынче умные и могут прореагировать как угодно - вплоть до убийства затребовавшего память процесса OOM менеджером.

    Так что, наиболее точный ответ на вопрос: возможны варианты (хорошие и разные)... Однако, если суть вопроса в том, а не получится ли при этом случайно массив, который не массив, то ответ - нет, не получится. Массив, это семантическая конструкция языка. В С++ программист может полагаться на то, что память в массиве непрерывна.
    Ответ написан
    5 комментариев
  • Websocket: прием сообщения и запись его части в отдельную переменную. Как осуществить?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Как сделать, чтобы получаемые символы на определенных местах записывались в, например, заранее оглашенные переменные?


    Для этого строку нужно разобрать, выделить интересующие подстроки и положить их в нужные переменные самостоятельно. Никакой магии, которая сделает это за Вас, нет. В этом смысле нет никакой разницы, откуда пришла строка: из вебсокета, с консоли или еще откуда-то :)

    Сделать это можно регулярками, циклами или любым другим замысловатым способом, но, прежде, чем это делать, нужно понять, что именно делать. А тут возможны варианты.

    1. Строка именно такая, как описано в вопросе, и изменить ее нет возможности, т.к. ее посылает "чужой" код.
    Тогда нужно описать структуру разделителей интересующих частей регулярным выражением (если она вообще регулярна) и выделять их регуляркой, например, так:
    string[] tokens = Regex.Split("Change position: F1-F2", @"(: )|(-)", RegexOptions.ExplicitCapture);
    
    if( tokens.Length == 3 && string.Equals(tokens[0], "Change position") )
    {
      curPos = tokens[1];
      newPos = tokens[2];
    } else {
      // прислали какую-то непонятную хрень...
    }

    Поиграться, потестировать регулярки самостоятельно и посмотреть, что они могут разобрать, а что нет, можно, например, вот тут.

    2. Строку можно определить самостоятельно.
    Тогда лучше придумать протокол (конкретнее, формат строки), который было бы легко разбирать. Например:

    event:cur-position:new-position

    т.е. для данного примера посылать:

    "Change position:F1:F2"

    Такую строку можно разобрать элементарным
    string[] tokens = "Change position:F1:F2".Split(':');
    
    if( tokens.Length == 3 && string.Equals(tokens[0], "Change position") )
    {
      curPos = tokens[1];
      newPos = tokens[2];
    } else {
      // прислали какую-то непонятную хрень...
    }


    И в том, и в другом случае нужно помнить, что к вебсокету, в принципе, может подключиться не только "наш" клиент, но и вообще любой проказник и послать туда что-то, чего мы совсем не ожидаем. Соответственно, перед тем, как тупо разбирать, строку хорошо бы еще проверить на корректность и, по крайней мере, обработать возможные ошибки при разборе (этого в приведенном примере кода нет - задание для самостоятельного изучения).
    Ответ написан
    1 комментарий
  • Как декодировать римскую запись числа?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Так же, как любyю другую непозиционную систему счисления... как-то вот так. Читайте, разбирайтесь, пишите свою программку ;)
    Ответ написан
    Комментировать
  • Что такое "call back interface"?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Если речь о Java и интерфейсах, то пример - как-то так:

    interface IObserver {
      void notify(Event e); //I'm a callback  ;)
      ...
    }
    
    interface IObservable {
      void register(IObserver o);
      void unregister(IObserver o);
      ...
    }


    Это не столько свойство интерфейса, сколько паттерн (идея, шаблон) на тему того, как можно в программе организовать взаимодействие (обычно, асинхронное) между двумя сущностями. В данном конкретном примере callback - это метод notify() в первом интерфейсе.

    В простейшем случае экземпляр класса, имплементирующего этот интерфейс (наблюдатель) сначала должен зарегистрироваться у какого-нибудь экземпляра, имплементирующего второй (наблюдаемого). После этого, всякий раз, когда в наблюдаемом будет происходить что-то, интересующее наблюдателя, наблюдаемое будет вызывать метод notify() экземпляра наблюдателя (и, например, передавать в него информацию о произошедших изменениях, в виде Event... но это - уже частности, не существенные для понимания сути callback). И так - до тех пор, пока наблюдатель не "попрощается" с наблюдаемым, вызвав его метод unregister(). Кроме того, кто именно "познакомит" одного с другим, тоже не существенно: это может быть сам наблюдатель, или кто-то еще (например, какая-нибудь фабрика, брокер, менеджер и т.д.) Если говорить о "свойствах интерфейса", то суть паттерна состоит в договоренности: "я знаю, что у тебя есть метод, который я могу и буду вызывать, когда сочту нужным, и ты будешь знать, что этот вызов означает". Все остальное (в каком потоке вызывать, что конкретно должен делать этот метод и т.д.) обычно тоже регламентируется, но это, опять же, уже частности.

    Для сравнения и отграничения понятия callback (от банальной инкапсуляции и IoC) можно привести пример
    java.lang.Comparable<T> {
      int compareTo(T o);
    }

    Все, что обещает этот интерфейс/метод: "если ты меня вызовешь и передашь мне ссылку на экземпляр такого же класса, как я сам, я сравню себя (по некоему, только мне известному алгоритму) с этим экземпляром и верну результат". Кто его будет вызывать, когда, зачем, что он станет делать с результатом... все это самому имплементирующему классу не важно. Это, хотя и очень похоже, но, строго говоря, не укладывается в понятие callback, т.к. вызываемому методу и всему экземпляру класса, в общем, нет никакого дела до того а. кто его вызовет и б. что это будет означать. А понятие callback подразумевает, что предоставляющий этот метод класс а. знает, кто и зачем его вызовет и б. непосредственно заинтересован в этом.

    Ну и, для полноты картины, нужно также упомянуть, что в разных язакых есть много разных способов организации callback. В C# есть делегаты и события, в C можно передать указатель на функцию и т.д. В Java для этого удобно пользоваться интерфейсами. Но это ничего не меняет в самом понятии callback, заключаюшемся в том, что кто-то осознанно предоставляет другому определенный метод, а этот другой этот метод вызывает, и оба знают, что означает сам факт вызова.
    Ответ написан
    Комментировать
  • Как в азбуке Морзе происходит разделение между символами?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Скорость передачи считается относительно длительности звучания точки. Длительность всех остальных элементов по такой схеме:

    "–" = 3 * "."
    пауза между элементами буквы = 1 * "."
    пауза между буквами в слове  = 3 * "."  ( = 1 * "–")
    пауза между словами          = 7 * "."


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

    Подробности, как всегда в ·–– ·· –·– ·· ·––· · –·· ·· ·–
    Ответ написан
    3 комментария
  • Как правильно выбрать путь изучения?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Вам нужно для начала точнее определиться с собственными целями... Если цель пока просто поиграться с железом, чтоб понять, нравится или нет, то самое правильное будет спокойно продолжить получать фундаментальное образование по программированию и делать какие-нибудь DIY проекты. Если же уже понятно, что это "дело всей жизни", то придется еще много чего учить, чего на телематике наверняка не будет (по крайней мере, в нужном объеме) и без чего в низкоуровневом программировании и, особенно, робототехнике делать просто нечего. Это прежде всего физика, электротехника, схемотехника, общие принципы конструирования и разработки систем, плюс, разумеется, технологии производства (тупо уметь паять), измерений (тупо уметь пользоваться осцилографом или LA), стандартизация и т.д. и т.п. вплоть до того же всеми горячо любимого сопромата. Из программирования и математики помимо чисто фундаментальных знаний (типа алгоритмов и структур данных, которые можно учить на любом языке) тоже понадобится много конкретного, например, DSP, криптография, ассемблер, устройство компиляторов, протоколы... английский (как минимум "технический") тут даже смешно упоминать. Одним словом, будьте готовы к тому, что в таком случае Вам фактически понадобятся ДВА образования.

    С чего начинать, в каком порядке и как их получать (еще куда-то поступать или учить недостающее самостоятельно) - особой роли не играет. В результате придете к тому же самому. Но хотя бы одно "официальное" образование обязательно нужно... не потому, что там чему-то конкретному научат, а потому, что там дадут возможность научиться учиться.

    А вот про "всю жизнь писать код" реально улыбнуло... Как только почувствуете, что устали писать код, паять, измерять, конструировать и, главное, изучать новое - так сразу же и уходите из профессии, ибо дальше в ней делать просто нечего! :)
    Ответ написан
    2 комментария
  • Хак предотвращающий повторную вставку данных в PostgreSQL?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Что конкретно значит "помог бы"? Однозначного хака, который сам собой все решит, скорее всего нет. Самый грязный хак, какой можно навскидку придумать - в БД добавить constraint на уникальность комбинации всех вставляемых полей, типа:
    CREATE TABLE example (
        a integer,
        b integer,
        c integer,
        UNIQUE (a, c)
    );

    Подробности: Раз - Два.

    Это исключит на уровне БД саму возможность повторной вставки, однако, сильно просадит перформанс вставок вообще (и, заодно, может полoмать другие кривые места, если такие есть). "Поможет" ли это - вопрос неоднозначный, т.к., если проблема действительно в повторной вставке, в коде наверняка где-то (скорее всего, в недрах NpgsqlCommand), всплывет Exception (constraint violation). Возможно, это поможет найти кривое место :) Заодно это поможет выяснить, нет ли в БД уже дубликатов, т.к. если они есть, добавить constraint просто не удастся до тех пор, пока их не вычистить.

    А кроме того, "в БД оказывается два экземпляра данных" cамо по себе требует уточнения. Если Вы смотрите в БД какой-нибудь независимой тулзой, то это одно, а если с помощю самого "кривого проекта", то проблема запросто может быть на самом деле не в БД, a в запросе, которым он выбирает данные из БД, или вообще в архитектуре (когда второй экземпляр появляется не из БД, а из какого-нибудь внутреннего списка, куда он тупо вставляется). В таком случае добавление constraint ничего не даст, но, опять же, этим и поможет локализовать проблему.

    UPD: В любом случае, самое правильное - с помощью этого (или подобного) хака найти проблему, после чего хак убрать! :)

    UPD2: Судя по тому, как передаются значения полей для вставки (в конструктор), этот "проект" - Эльдорадо для любителей SQL Injection. Так что, не удивляйтесь, когда в один не самый прекрасный день хакеры пришлют вам поздравительную открытку :)
    Ответ написан
    Комментировать
  • Почему выводить логи через System.out.print плохо?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Вывод через System.out - это не "плохо", а всего-навсего "плохо чаще чем хорошо". Если кто-то дает такой совет, то имеет ввиду при этом "по сравнению с использованием логгирующего фреймворка". На самом деле оба подхода имеют свои достоинства и недостатки, просто, как правило, для более-менее сложных и, особенно, чувствительных к производительности систем преимущества использования фреймворков в подавляющем большинстве случаев очевидно перевешивают недостатки.

    Производительность.
    Если делать вывод через System.out, он всегда будет синхронным, т.е. поток будет ждать завершения операции. И изменить это потом нельзя, не трогая код. Фреймворк же может буфферизировать вывод, сортировать в очереди и т.д. и, почти всегда по дефолту будет делать вывод в другом потоке. Разумеется, все это не бесплатно (память, потоки), но, по мере роста сложности системы, накладные расходы очень быстро компенсируются общим приростом производительности. Если же говорить только о, непосредственно, IO вывода в консоль, то абсолютные накладные расходы от использования фреймворка (для большинства случаев) пренебрежимо малы.

    Удобство использования в разных сценариях.
    Если код, например, изначально был под десктоп, а потом его понадобилось перенести на сервере, то вывод System.out, конечно, можно перенаправлять в файлы. Но это придется делать довольно отвратительными внешними костылями (ротация файлов, разные блокировки файлов на Win/Linux). Фреймворк, как правило, предоставляет из коробки решения не только этих очевидных проблем, но и кучу плюшек (типа вывода на серверы логов, в сокеты или даже SNMP), причем, делает это эффективнее среднестатистического костылестроителя и позволяет легко переключаться "на лету". Опять же, не бесплатно. Цена: одна лишняя (по сравнению с System.out, который будет работать всегда и везде) зависимость - от самого фреймворка, (а, реально, еще и ворох транзитивных). Однако, для систем с большим количеством других зависимостей этот недостаток практически незаметен.
    Другой екстремальный сценарий: старый, давно проверенный и железобетонно-надежный серверный код, логгинг которого последние три года отправлялся в null, вдруг понадобилось использовать... в консольном приложении. А там System.out на System.out-е :) Та-дам!

    Гибкость/Масштабируемость.
    Фреймворки предоставляют наборы абстакций (аппендеры, очереди, категории, фильтры и т.д.), позволяющих, не трогая рабочий код, легко переконфигурировать логику вывода логов. Представьте на минутку кривизну костыля, который придется строить вокруг System.out, чтоб, например, временно дублировать в отдельный файл записи, выданные кодом из одного определенного пакета или класса, продублировать вывод на второй сервер логов или, сколько кода придется перелопатить, чтоб покрасить в красный цвет записи, содержащие слово CRITICAL... (а если одновременно под ANSI и под HTML?) :) И какова при этом будет вероятность нечаяно сломать этот костыль или этот код :)

    Читаемость/Сопровождаемость кода.
    Хоршие фреймворки обычно хорошо спроектированы и, при правильном использовании, позволяют безболезненно обновляться, расширять функционал или даже вообще заменяться на другие с минимальными затратами. И все это - совершенно прозрачно с т.з. программиста, которому достаточно просто вместо "System.out.println(" писать, например, "Logger.log(", и можно перестать нервничать по поводу логов и начать жить :)

    Недостатки, собственно, вытекают из преимуществ. Мне, лично, приходят в голову только два с половиной сценария, в которых я бы точно не стал брать фреймворк: 1. написание очень маленькой, тривиальной программы, 1.5. написание иллюстрирующего кода (ответ на Тостере, пример использования чего-то в документации или в багрепорте и т.д.) и 2. написание бенчмарка (чтоб не грузить JVM "неизвестно чем").

    Так что, собственно, абсолютно универсального совета "не использовать" нет - есть здравый смысл и конкретные потребности в конкретных случаях.
    Ответ написан
    Комментировать
  • Как исправить конкретный cлучай со stackoverflow? Как понять java?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Эти классы зависят друг от друга, т.к. в одном создается экземпляр другого, и наоборот. Само по себе это не страшно, но у Вас это происходит и там, и там прямо в инициализации (то место, где объявляются поля и им тут же присваивается новый экземпляр другого класса).
    public class Lois {
      Pitter husbend = new Pitter();
      ...
    
    public class Pitter {
      Lois wife = new Lois();
    ...

    Инициализация выполняется всякий раз, когда создается экземпляр этого класса:
    ...  = new Lois();
     //or
        ... = new Pitter();

    причем, совершенно неважно, где и почему программа пытается его создавать.

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

    Что такое переполнение стека и почему оно тут происходит? Любое создание объекта (экземпляра класса) "внутри" означает просто-напросто "вызов метода" (статической инициализации, затем конструктора; только после возврата из конструктора объект считается созданным и ссылка на экземпляр может быть чему-то там присвоена). А "вызов метода" означает (немного упрощенно) не что иное, как "положить на стек точку возврата, передаваемые параметры метода и перейти по адресу метода". Метод забирает со стека параметры и точку возврата, в конце кладет на стек свой результат (если он есть), и совершает переход по точке возврата. Если за ней в программе присвоение (как в ваших классах), результат забирается со стека и "присваивается". Таким образом, при нормальном вызове стек остается "чистым". А если вызов уходит в бесконечную цепочку других вызовов, как у Вас, то до очистки дело никогда не доходит, и стек рано или поздно заканчивается. Вот тогда и прилетает StackOverflowError.

    Упрощено суть того, что в Вас происходит, можно проиллюстрировать вот так:
    public class Main {
    
    	public static void main(String[] args) {
    		// You can run JVM with:
    		//	-XX:MaxJavaStackTraceDepth=-1 (unlimited number of stack trace frames)
    		//  -Xss4m (play around with stack size)
    		//
    		try {
    			first(); // causes stack overflow!
    		} catch (StackOverflowError e) {
    			System.err.println("Depth (frames):" + e.getStackTrace().length);
    		}
    	}
    	
    	static int first(){
    		return second();
    	}
    
    	static int second(){
    		return first();
    	}
    }

    Можете запустить и посмотреть, что будет :)

    Чтоб этого не происходило, нужно переделать код, например, вот так:
    public class Lois {
    
    	Pitter myHusband = new Pitter(this);
    	
    	public Lois(Pitter whoIsMyHusband){
    		myHusband = whoIsMyHusband;
    	}
    	
    	//...
    }
    
    public class Pitter {
    	
    	Lois myWife = new Lois(this);
    	
    	public Pitter(Lois whoIsMyWife) {
    		myWife = whoIsMyWife;
    	}
    	
    	// ...
    }
    Ответ написан
  • Как разобраться в этой терминологии?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Благодаря тому, что программирование, это не идеология, не мифология, а вполне себе прекладная научная дисциплина, вся терминология в нем изначально предельно точная и информативная. В любом термине (кроме некоторых маркетинговых мемов) обычно содержится почти полный ответ на вопрос: "а что это такое", для понимания которого не нужно ничего запоминать - достаточно анализировать слова и синтезировать смысл абстракции по аналогии с предметами реального мира :) Однако, есть два момента.

    Первый: терминология исторически англоязычная и всяческие попытки переводов чаще вредят, чем помогают понять суть. (Мне, например, пришлось напрячь Гугл, чтоб удостовериться, что под "шлюзом" Вы действительно подразумеваете "pattern gateway", а не что-то там еще).
    Второй: термины часто обозначают не конкретные сущности, а абстракции. (Если вдруг значения последних двух слов понятны только на уровне смутного ощущения, разберитесь, что они конкретно означают... абстракция - это не "нечто туманное и заумное", а совсем другое :) ) Соответственно, для толкования этих терминов нужно использовать абстрактное мышление. Как?

    Например, для того же шлюза... шлюз бывает между двумя реками с разным уровнем... да, там есть выше - ниже. Однако, бывает между двумя отсеками космического корабля (хорошо - там все еще может быть разное давление), а бывает между толпой пассажиров в аэропорту и выходом к самолету (давление толпы?)... или, между локальной сетью и внешней, и т.д. Если задуматься, почему все это называют таким словом, легко понять, что суть понятия не столько в перепаде (уровней, давлений и т.д.), а, наверное, в том, что "это такое нечто, только через которое можно попасть/выйти из одного в другое". .. оно же, кстати, соответствует буквальному переводу слова "gateway" - "выход наружу"...

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

    Далее... как его "сделать в коде"? А фиг его знает! Как удобнее для проектируемой системы, так и делайте. Может, это будет один класс, может микросервис, а может вообще железяка на FPGA. Паттерн не дает никаких готовых рецептов (типа, взять три грузовика бетона, выкопать яму глубиной 5 метров и пр.) - он просто говорит, что чем всем частям системы "лазить наружу" как попало, лучше это дело сконцентрировать в одном месте. И, кстати, патерн не является "современным стандартом" и, вообще, стандартом! Это просто обобщенный опыт поколений разработчиков систем. Не более, чем соображение мыть руки перед едой... если зачем-то нужно, можно и не мыть, но если нет какой-то осознанной мотивации, то мыть в большинстве случаев - лучше :)

    Идем дальше... интерфейс. Что это такое? Формочка с кнопочками и чекбоксиками? А может, разъем USB? А может, код на каком-нибудь ЯП? Что между всем этим общего? Да очень просто! Интерфейс, это набор правил и соглашений о том, как пользоваться какими-то функциями того, что предоставляет этот интерфейс :)

    Вот и все! В ИТ-шной терминологии нет никакой магии и никакого сакрального смысла. Для понимания достаточно знать ТОЧНОЕ значение довольно небольшого количества слов и для любого непонятного термина всегда задавать два простых вопроса: "что это значит" и "почему", и находить на них ответы. И все туманное и странное быстро становится простым и понятным :)
    Ответ написан
    2 комментария
  • Что должен уметь менеджер проекта (продукта)?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Не обижайтесь, но, судя по задаваемым вопросам, Вам еще вообще рано думать о таких позициях. Трудоустройство - это, как бы, одно (тут может и повезти), но вот успешная работа - совсем другое. А для этого Вам нужно еще набираться и набираться опыта. По вопросам:

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

    Менеджер продукта отвечает за то, чтоб не просто что-то там разрабатывалось, но еще это что-то и продать, и, самое главное, получить прибыль. Его задачи - сделать продукт конкурентоспособным, востребованным на рынке, но сделать это максимально эффективно и в нужные сроки. Для этого он общается с потребителем (изучает его потребности), с конкурентами (изучает их сильные и слабые места), следит за трендами на рынке и в технологиях, за ценами, за патентами и торговыми марками, за стандартами и регулирующим законодательством, тусует на выставках, находит партнеров, составляет и заключает с ними договора... короче, он полностью определяет стратегию продукта, т.е. ЧТО нужно разрабатывать.

    Разумеется, оба работают в тесном контакте друг с другом и, как правило, без формального "разграничения должностных обязанностей", знают продукт не хуже (а иногда и лучше) любого рядового разработчика и, в конечном итоге, совместно определяют успех или провал всей затеи... за что и получают либо лавры победителей и премии, либо пинка под зад. Иногда, в небольших проектах все это может выполнять один человек.

    2. Для трудоустройства - как повезет, а вот для работы нужно обладать солидными знаниями и опытом во всех этих областях плюс, в идеале, глубокими знаниями технической стороны вопроса... как минимум, знать, чем отличается интерфейс от абстрактного класса ;)

    3. Если это важно для продукта, то ОЧЕНЬ пригодятся, а если нет, то придется разбираться с теми технологиями, которые важны.

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

    5. Самая смешная часть вопроса... сначала покажите на деле, что способны заработать денег для фирмы, а потом уже задумывайтесь над тем, сколько просить за этот свой навык :)
    Ответ написан
    4 комментария
  • Не поможете разобраться с наследованием в Java?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    В том, что вы написали, нет никакого смысла (даже если это и компилируется)! Если я правильно понял смысл затеи, что из написанного ОЧЕНЬ непросто, то должно быть как-то так:
    public abstract class Pet {
    	String name;
    	int age;
    	int weight;
    	int cost;
    }
    ...
    public class Dog extends Pet {
    	public Dog(String name, int age){
    		this.name = name;
    		this.age = age;
    	}
    }
    ...
    public class Main {
    
    	public static void main(String[] args) {
    		Pet tuzik = new Dog("Tuzik", 5);
    		System.out.print(tuzik.age);
    	}
    
    }

    Это наиболее близко к написанному и будет работать, как хочется, но это все еще УЖАСНО с т.з. ООП, так что рассматривайте это только как иллюстрацию, а не как пример к подражанию.

    Обратите внимание на "public Dog(String name, int age){..." - это конструктор. У него нет типа. Он вызывается, когда класс инстанциируют (... = new Dog("Tuzik", 5);) и возвращает экземпляр класса (объект). Только после этого поля объекта проинициализированны какими-то значениями. До того в них в данном примере ничего нет (точнее, там сразу после создания объекта и до отработки его конструктора везде 0 и null в name).
    Если что-то непонятно, спрашивайте, но учтите, что пока вы не разберетесь с этим, продолжать обсуждать подробности (например, почему класс Pet абстрактный) бессмысленно.
    Ответ написан
    6 комментариев
  • Что сделать, что бы не нарушался принцип инверсии зависимостей?

    pi314
    @pi314
    Президент Солнечной системы и окрестностей
    Примерно так:
    interface IBankAccount
    {
     public void AddFunds(double value);
     public void RemoveFunds(double value);
    }
    ...
    class BankAccount implements IBankAccount
    ...
    class TransferManager
    {
     public TransferManager(IBankAccount Source, IBankAccount Destination, double Value) {
    ...

    ...и т.д. Везде заменить прямую зависимость от класса на зависимость от интерфейса, ибо:

    - Модули верхних уровней не должны зависеть от модулей нижних уровней. Оба типа модулей должны зависеть от абстракций.
    - Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций.

    (Класс - это модуль, а интерфейс - это абстракция.)
    Ответ написан
    Комментировать