• Зачем нужна инкапсуляция в ООП?

    Starina_js
    @Starina_js
    full-stack web dev
    Да, есть такая проблема, путаница в понятиях, плюс разные языки программирования по своему трактуют и реализуют такой механизм.

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

    Когда на проекте работают сотни программистов, хочешь ты или нет, будет создаваться система контрактов или договоренностей между людьми. Одна из таких — безопасность от случайных воздействий или устойчивость. Возможность скрыть или защитить данные от внешних воздействий. Хотя это не только, еще про объединение данных и функций работы с данными.

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

    Пример: бизнес торопиться вывести новую функцию на рынок. Чем быстрее выведет, тем быстрее начнет зарабатывать. Бизнес просит программистов делать это побыстрее. Программисты пыхтят, у них высокая нагрузка им некогда разбираться с чужим кодом. Видят нужное им свойство, начинают с ним работать и изменяют под себя.
    Вроде все ок, и программа не упала и все тесты прошли, начали релизить проект. Прошла неделя и пользователь как-то нестандартно использовал новую функциональность и вместо 100 руб, списался 1 миллион. Беда косяк, а бывают и серьезней последствия. Случилось это потому, что кто-то изменил маленькое свойство в чужом коде.

    Поэтому программисты решили договориться — объединяйте и важное прячьте, делайте недоступными для воздействия, хотите поменять коэф — давайте правильный и безопасный метод.

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

    Как-то так)
    Ответ написан
    Комментировать
  • В чем разница между List и LinkedList при объявлении списка?

    sergey-gornostaev
    @sergey-gornostaev Куратор тега Java
    Седой и строгий
    Разница в соблюдении LSP и ISP.
    Ответ написан
    3 комментария
  • Какую книжку выбрать для изучения JS?

    MarcusAurelius
    @MarcusAurelius
    автор Impress Application Server для Node.js
    Комментировать
  • Как называется такой вызов функции?

    iMedved2009
    @iMedved2009
    Не люблю людей
    Method Chaining

    Каждый метод должен возвращать обьект.
    Ответ написан
    Комментировать
  • Правильно ли реализован класс для работы с базой данных по принципу SOLID?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Во-первых, это никакой не DatabaseManager , а CRUDManager. Работа с БД далеко не ограничивается этими 4 примитивными функциями.

    Отсюда мы делаем логичный вывод, что соединение с БД никаким местом не должно создаваться в конструкторе менеджера крудов. А должно точно так же передаваться в него в качестве зависимости. Это может быть либо ванильная ПДО, либо инстанс реального MySQLDatabase (но поскольку мы пока не знаем, как он должен выглядеть, то лучше остановиться на PDO).

    Сам по себе DatabaseManager выглядит избыточным. Непонятно, зачем он нужен, если любой потребитель DatabaseManager-а может просто написать
    public function __construct(CRUDInterface $crud) {
    }

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

    В-четвёртых, хоть это и не относится напрямую к теме SOLID, но для меня является очень важным: собственно, реализация методов CRUD-а. Что в них передаётся? Откуда берутся названия таблиц, полей? Передаются в параметрах методов? Это прямая дорога к SQL инъекции, не говоря уже о нарушении инкапсуляции. Поэтому, отвечая на вопрос "Как вы реализуете работу с базой данных", лично я всё больше в последнее время от развесистых ORM-ов склоняюсь к простым TableGateway-ам. Да, кода писать больше, но он строже и понятнее. И не встаёт колом в нестандартных ситуациях. Тем более что приведённый пример кода как раз очень и похож на этот паттерн. То есть
    abstract class MysqlTableGateway implements CrudInterface
    {
        protected $db;
        protected $table;
        protected $fields;
        protected $primary = 'id';
    
        public function __construct(\PDO $db)
        {
            $this->db = $db;
        }
        public function read($id): ?array
        {
            $stmt = $this->db->prepare("SELECT * FROM `$this->table` WHERE `$this->primary`=?");
            $stmt->execute([$id]);
            return $stmt->fetch();
        }
         // ну и так далее
    }

    И дальше уже классы по работе с отдельными табличками наследовать от него,
    final class UserGateway extends MysqlTableGateway {
        protected $table = 'users';
        protected $fields = ['email', 'password','phone'];
    }

    Соответственно, если мы захотим перейти с мускуля на какой-нибудь редис с джейсоном внутре, то надо будет создать новый абстрактный класс с тем же интерфейсом, и от него отнаследовать реализации. Соответственно, в интерфейсе надо нормально прописать входные и выходные параметры:
    interface CRUDInterface {
        public function create(array $data):int;
        public function read(int $id):?array;
        public function update(array $data);
        public function delete(int $id);
    }

    Другое дело, что в реальности такой шалтай-болтай будет сделать довольно сложно, поскольку классы для работы с отдельными таблицами будут расширяться запросами, специфичными для данной таблицы - то есть все их придется дописывать во все драйверы. То есть в реальности с D будут проблемы. Но чисто с теоретической точки зрения примерно вот так оно будет выглядеть.
    Ответ написан
    4 комментария
  • В каких ЯП мало или вообще нет фреймворков?

    ThunderCat
    @ThunderCat
    {PHP, MySql, HTML, JS, CSS} developer
    В результате всего этого ты вроде и работаешь и вроде как бы опыта набираешься, а по факту знания весьма поверхностные, зачастую после полугода вообще забываются некоторые вещи.
    На самом деле - и да и нет.
    Во первых - 90% фреймворков похожи как родные братья, отличие в десятке настроек и чуть разном подходе к передаче параметров, ну +- десяток нюансов. Вникнуть в них вполне реально за приемлемое время.

    Во вторых - ну вот выучили лару и нет желания колупаться в условном уйй - ищите вакансии под нее, че париться, их мульён же, где-то да найдется под вас.

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

    Ну и анек в тему:
    HR:
    - Вы нам не подходите. Нам нужны молодые, амбициозные, способные творчески расти!
    Пожилой прогер:
    - Запишите мой телефон. Когда выяснится, что у вас все амбициозно растут, а работать некому - позвоните!
    Ответ написан
    7 комментариев
  • Как скрыть openVPN?

    @Herest
    Сервисы определяют использование VPN по MTU. Стандартный размер пакета составляет 1500 байт, но при использовании туннельных протоколов, типа PPTP, L2TP, IPsec, OpenVPN, WireGuard и т.д., его размер всегда будет меньше. И проблема в том, что у каждого протокола этот MTU уникальный, поэтому сопоставить ваш MTU с тем, который по умолчанию используется в популярных протоколах, не составит большого труда. Это называется VPN Fingerprint. Вы можете сами в этом убедиться, посмотрев на вкладку с информацией о системе на сайте proiptest.com. Поэтому единственный выход - это изменить MTU сервера OpenVPN на другое значение, например 1420.

    Вспомнил, что ещё в арсенале сервисов есть проверка на двусторонний пинг, когда существенная разница во времени прохождения ICMP пакетов от вас к сервису и от сервиса к вашему удалённому роутеру палит ваш VPN. Выход здесь тоже только один - это запретить отвечать удаленному роутеру на пинг запросы из интернета.
    Ответ написан
    2 комментария
  • В чем ошибка моего кода?

    FanatPHP
    @FanatPHP
    Чебуратор тега РНР
    Самое время познакомиться с темной стороной программирования.
    Начинающие вайтишники искренне думают, что программист - это типа такой художник. Берет мольберт, поллитру, кисти и начинает ВАЯТЬ. Потом отходит на шаг, любуется делом рук своих, и снова. Ваять. А потом сразу заказчику, за большие деньги.

    Так вот, в реальности это всё не так.
    Большую часть времени программист не пишет код.
    А пытается разобраться, почему он не работает.

    Так что мы будем сейчас учиться это делать.
    Тем более, что это в принципе несложно.
    Главное не думать, что чем-то поможет сидеть и тупить в свой кодик. И приглашать других людей потупить в него тоже бессмысленно. Потому что причина может быть совсем не в нем. но даже если проблема и в коде, то искать её всё равно надо по-другому.
    В код не надо тупить. Его надо ЗАПУСКАТЬ.
    И выводить промежуточные результаты. Проверять его работу.
    Заранее выяснить, какие должны быть значения у переменных, и проверять их на каждом этапе.
    Где не совпадут - там и проблема.
    В идеале IDE сама покажет содержание всех переменных при трассировке, но если пишешь код в блокнотике, то даже тупо писать var_dump($bar1,$var2,$var3...); и смотреть что там лежит.
    Условия проверять еще проще, тупо echo 'зашли в условие if (!empty($user))';
    И если лежит не то, или эхо не выводится - вот тогда уже смотреть в код и думать, почему так получилось.

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

    Кроме того
    Разумеется, отладка невозможна без сообщений об ошибках.
    В половине случаев РНР человеческим голосом сообщает в чем проблема.
    Поэтому всегда, в любом окружении должно стоять error_reporting(E_ALL);
    плюс на домашнем компике полезно прописать ini_set('display_errors', 1); чтобы сразу видеть ошибки на экране.
    На боевом сервере разумеется поставить 0 вместо 1, и добавить ini_set('log_errors', 1);

    У меня только один вопрос.
    Какой смысл вообще делать парольную защиту, если любой придурок сможет спокойно авторизоваться через SQL инъекцию?
    Ответ написан
    9 комментариев
  • Правильно ли я понял принцип инверсии зависимостей?

    @Akela_wolf
    Extreme Programmer
    Главная идея принципа инверсии зависимостей "детали зависят от абстракций, но не абстракции от деталей".
    В приведенном вами примере класс Main зависит от всего: от интерфейса INumberOperation и от обоих классов NumberOperation1, NumberOperation2. То есть тут принцип инверсии зависимостей вообще не работает. Никак.

    Проявляется же он в следующем примере. Пусть у меня есть некая абстрактная логика "прочитай число, выполни над ним операцию, запиши результат". Эта абстрактная логика (потому она и абстрактная) ничего не должна знать ни откуда она читает число, ни какую операцию над ним выполняет, ни куда и как записывает результат. Таким образом, у нас есть модуль, состоящий из
    interface NumberInput {
      int read();
    }
    interface NumberProcessor {
      int process(int a);
    }
    interface NumberOutput {
      void write(int a);
    }
    class Processor {
      private final NumberInput input;  
      private final NumberProcessor processor;
      private final NumberOutput output;
    
      public Processor(NumberInput input, NumberProcessor processor, NumberOutput output) {
        this.input = input;
        this.processor = processor;
        this.output = output;
      }
    
      void process() {
        output.write(processor.process(input.read()));
      }
    }

    Все. Модуль получился очень абстрактным и ни от кого никак не зависящим.
    Затем мы можем сделать реализации этих интерфейсов - они будут зависеть от нашего модуля логики (так как ссылаются на интерфейсы). И это полностью соответствует принципу инверсии зависимостей - детали зависят от абстракций.

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

    Этот принцип очень хорошо объяснен в книге Р.Мартина "Чистая архитектура", по крайней мере у меня все встало на свои места именно после прочтения этой книги.
    Ответ написан
    1 комментарий
  • Что попадает в фазу I/O коллбэки в node?

    MvcBox
    @MvcBox
    Software Developer [C/C++/JS(for Node.js)/etc]
    Фаза pending callbacks - это почти все коллбэки, за исключением событий close, таймеров и setImmediate()
    Фаза poll - самая интересная и сложная фаза, подготовка к которой начинается еще в предыдущей фазе, а также производится проверка состояния других очередей. Если кратко, то здесь происходит работа с "внешними источниками" (принимаются входящие соединения, etc), работа с многопотоком (thread pool), работа с файловыми дескрипторами, ect.

    Библиотека libuv постоянно развивается, поведение некоторых ее частей может меняться от версии к версии.
    Поэтому самую актуальную информацию Вы сможете получить только ознакомившись с исходниками.
    Ответ написан
    7 комментариев
  • Почему docker контейнеры не видят друг друга по имени?

    @krinbin Автор вопроса
    Решение было найдено.
    Оставлю для потомков.
    Все же podman != docker
    Поэтому для решения задачи, необходимо поставить dnsname (входит в состав podman-plugins или собрать) и dnsmasq.
    И далее по тексту.

    Я так же поставил podman-compose, но толку не было :)

    Всем спасибо за внимание.
    Ответ написан
    Комментировать
  • Насколько актуален MVC в Golang для написания сайтов средней сложности?

    alfss
    @alfss
    https://career.habr.com/alfss
    Буду продвигать в массы

    https://github.com/alfssobsd/notes/blob/master/gol...
    https://github.com/alfssobsd/clean-arch-golang-bes...

    В текущей компании делаем так , аналогично в Java , Kotlin, нам нравится .
    Ответ написан
    Комментировать
  • Docker и CI/CD: как все-таки происходит этот магический деплой?

    gbg
    @gbg
    Любые ответы на любые вопросы
    Общее в CI/CD - это идея, данный набор практик закрывает вопросы 2 и 3 из теста Спольски

    То есть, в данном случае, важна идея, а не конкретный путь к ее реализации (делать это через Доскер или через развертываение VM, использовать для этого вязанку батников или монстра вроде TeamCity - это уже частности - важно, что нажатие одной кнопки приводит к появлению где-то полностью собранного с нуля и развернутого актуального билда Продукта)
    Ответ написан
    Комментировать
  • Книги для изучения root, Ip, api?

    Zoominger
    @Zoominger Куратор тега IT-образование
    System Integrator
    Начните с элементарного гугления, термины, которые вы перечислили, довольно разнородные (относятся к разным сферам).
    Ответ написан
    Комментировать
  • Почему не могу подключиться к MySQL?

    sptm
    @sptm
    software developer / DevOps engineer
    Для пользователя root по умолчанию включена авторизация через unix socket (auth_socket). Нужно создать пользователя с авторизацией по паролю или сменить тип авторизации для root (как-то так).
    Ответ написан
    Комментировать
  • Как работает глобальный Интернет?

    saboteur_kiev
    @saboteur_kiev Куратор тега Компьютерные сети
    software engineer
    Ваш провайдер имеет аплинки к другим провайдерам (зачастую к трансмагистральным провайдерам, которые лично владеют коммуникационными каналами на далекие расстояния - например провайдер сам является международным и у него есть оптические каналы проложенные к его датацентрам в других странах, либо у двух крупных провайдеров есть какой-то канал, который они вместе построили и проложили под землей или через океан. Там вообще могут быть коммуникации купленные вскладчину несколькими участниками.

    Стоимость прокладки таких каналов стоят огромных денег. Это и непосредственно сами работы и огромное количество разрешений на эти работы, которые должны быть согласованы со всеми владельцами и странами. Поэтому окупается это так, что между провайдерами всегда идут какого-то рода взаиморасчеты за передачу траффика. Кто генерит контент, то собственно больше и получает, кто скачивает - тот платит.
    Но стоимость этих взаиморасчетов может быть разная на разном уровне. Где-то определенный объем входит в абонплату. Где-то трафик ночью дешевле. Где-то можно сделать дешевле но медленнее. Условия разные и гибкие.
    Поэтому в свитчах провайдеров всегда настроены довольно сложные правила и для минимизации этой цены, и для обеспечения отказоустойчивости (если какой-то маршрут нарушен, автоматом может пустить по другому каналу, возможно более медленному). Плюс ошибки админов.

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

    Также из всей сложной схемы и могут возникать такие ситуации, когда, например трафик из Украины в Россию, дешевле будет пускать не напрямую, а через Франкфурт, хотя казалось бы напрямую будет короче.

    Так и живем.

    P.S. Ах да, еще - пинг и скорость это разные показатели, не всегда релевантно относящиеся друг с другом, но в отдельных моментах ширина канала может аффектить пинг. Если посчитать скорость света с минимальной затратой на проход пакета через маршрутизаторы, можно сказать что самый большой пинг в пределах Земли должен быть около 400 мс между точками-антиподами. Но тут естественно нужно учитывать между этими точками только оптика, или что-то еще.

    P.P.S. хорошая статья на Хабре от amarao https://habr.com/ru/post/186282/
    Ответ написан
    7 комментариев
  • Идеальная структура каталогов проекта?

    sergey-gornostaev
    @sergey-gornostaev
    Седой и строгий
    Не существует.
    Ответ написан
    Комментировать
  • Отделение бизнес логики от фреймворка Symfony?

    Maksclub
    @Maksclub Куратор тега PHP
    maksfedorov.ru
    Генератор — просто инструмент для помощи, по итогу сущности чисты, не считая аннотаций/атрибутов для маппинга в ORM, но это просто мета-информации и завязка не существенна (не считая маленького компромисса с ArrayCollection). То есть если вы выберите др ORM, то эти аннотации вам не помешают никак, просто лишние заюзанные классы аннотаций

    Имея сущности доктрины — у нас не связанный от фреймворка код, пишите спокойно бизнесуху, не обращая внимания на то, как оно потом маппится. То есть практически все по каннонам

    Чтобы отделить репозиторий от домена — просто в домене делайте интерфейс, а вот реализация этого репозитория будет в Infrastrucure Layer, но это избыточно... риск минимальный, если сделаете не совсем по канону, а именно риск стоит как основной аргумент такового отделения (не просто же вы словам следуете, а причину понимаете?)
    Разработка строится на компромисах, если смените доктрину на др ORM — так и так писать новые репозиторий, вероятность низкая и многие например не делают такие интерфейсы — слишком усложнит код...
    Вам надо будет просто репозиторий в маппинге ORM\Repository заменить в таком случае

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

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

    617588c41bdde421641847.png
    Ответ написан
    8 комментариев
  • В чём смысл передавать array_reverse в foreach без явной необходимости обратного порядка элементов?

    ipatiev
    @ipatiev Куратор тега PHP
    Потомок старинного рода Ипатьевых-Колотитьевых
    Для ответов на подобные вопросы всегда полезно использовать логику.
    Например:
    Если необходимости в обратном порядке элементов нет, то array_reverse не нужна.
    Если array_reverse используется - значит, скорее всего, необходимость есть, но вы её просто не видите.

    Это универсальный способ, который позволяет ответить на можество подобных вопросов.

    "оставить исходный массив нетронутым" причиной не является.
    Ответ написан
  • Когда применяем паттерн Стратегия, а когда Декоратор?

    Maksclub
    @Maksclub
    maksfedorov.ru
    Стратегия = полиморфизм, то есть мы завязаны на некий интерфейс, а какая реализация — нам не важно. Это история про зависимости. Ну например почтальон отдает пенсию бабушкам (любым, какой бабушке именно — зависит от стратегии, КОТОРАЯ НЕ СВЯЗАНА с модификацией конкретной бабушки:)

    Декоратор, это про добавить функционал в рамках одного интерфейса, тут вообще не рассматривается вопрос каких-либо отношений (к примеру бабушки и почтальона), тут рассматривается — бабушка в шубе или бабушка с загаром или бабушка на коляске, все та же бабушка, но "обернутая" неким поведением :) Главное что бабушка всегда остается быть той самой для всех бабушкой. То есть это не противопоставление — ни в начале ни в середине мы не завязываемся на дополнительное поведение бабушки у почтальона. Бабушка и все, а какая именно — зависит от стратегии разноса (например по названию улицы). Если выйдет к нему "декорированная" бабушка-качок — пенсию он даст ей также, как и не качку, тк она для него всего лишь некий субъект/абстракция, главное чтобы возраст и ФИО сошлись.

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

    Кое-где не корректные аналогии, и в аналогии стратегия есть бабушка, но в целом для понимания норм и не критично :)
    Ответ написан
    Комментировать