Ответы пользователя по тегу ООП
  • Как соединить две модели в коллекции?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Коллекция это про пачку.

    Создав класс коллекции компаний и запихнув туда контакты ты сказал примерно так "несколько компаний имеют общие контакты в рамках этой пачки компаний".

    Иногда это имеет смысл, но это такие редкие случаи, что так не надо делать. "независимость" модели это про другое, это про то что контакты являются частью одной задачи из ТЗ, а компании - другой. Но это не мешает одним иметь другое в подчинении, если в тз написано что компания имеет контакты.

    Если ты решил сделать что контакты имеют не только компания или же разные компании могут иметь одни и те же контакты, то это новая таблица company_contact, где указаны все связки компаний с контактами, а не коллекция.
    Ответ написан
    3 комментария
  • Зачем задавать приватный модификатор доступа для свойств класса?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Я когда-то задал тот же самый вопрос и нашел на него ответ.

    Для начала "private/protected/public" это больше история про наследование, чем про "доступ" или про "капсуляцию". Тут просто эти три слова несколькой обязанностей выполняют.

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

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

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

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

    Теперь про свойства.

    "Публичные" свойства как во всех языках нужны, чтобы просто к ним обращаться и вписывать туда значение. Плюсы как бы - обычная переменная, меньше кода писать. Минусы? Обьекты постоянны от функции к функции. Где-то на уровне 10м-20м кто-то всунет в этот обьект то, что ты бы не хотел там видеть и оно всунется, а ты потом офигеешь, что "а че, так можно было чтоли?" - да. То есть публичные свойства стоит использовать только в каких-то может одноразовых объектах, типа DTO/ValueObject паттерн, просто потому что от "защиты" там только число кода добавится, они просто как массивы только с именованными заранее полями. Удобно писать, редактор подсказки показывает. И все равно остается опасность того что выше написано, поэтому как бы предполагается что вот дто он такой, одноразовый, и пишут в него только в начале приложения, разбирая запрос на куски, запихнул что не надо - получай.

    "Защищенные/Приватные" свойства здесь выполняют почти ту же задачу, что и защищенные методы (про наследование). Дополнительная фишка - в защищенное свойство ты можешь методами написать КАК ИМЕННО ты кладешь туда данные или всобачить небольшую проверку. Не стоит увлекаться тысячей проверок - в коде утонешь, но проверку Типа - можно, почему нет. Раньше типизированных свойств не было, так это был вообще единственный способ убедится что в поле лежит нужного класса другой объект. Я вот раньше писал set()/get()/delete()/add()/put() и кучу всего пока не понял, что можно обобщить до двух set(?$items) и add($items), причем set() - вызовет add() и если туда null передать, то оно либо поставит по умолчанию либо очистит, то есть ещё и delete() выполняет когда нужно.

    Ещё кое-что про свойства. Стоит помнить, что в свойства можно закинуть любую информацию, в том числе скаляры. Скаляры это те типы, что копируются каждый раз, когда ты их присваиваешь в другую переменную потом. То есть увлекаться большим числом свойств никогда нельзя, это чревато тем, что при копировании обьектов ты скопируешь все данные внутри, внезапно увеличив потребление ресурсов компьютера. Конечно, на практике так очень редко бывает, что кто-то в свойство положит массив на 100 тысяч свойств, а потом клонирует обьект случайно в цикле десяток раз... Но где минус там и плюс - если объект не копировать - это пачка данных, которые хранятся один раз, а работают потом везде, так сказать общее хранилище.

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

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Коллекция это просто пачка. Массив с цифровыми ключами. Бывает двух типов я для себя назвал - массив и список.
    Массив - ключи строго цифровые и строго по порядку.
    Список - ключи строго цифровые но могут идти не по порядку и некоторые из них могут отсутствовать

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

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

    Почему в Ларавеле коллекции? Потому что им надоело прокидывать по ссылке вероятно и писать методы для работы над массивами. Может не смогли разобраться. Может задолбались. Но под капотом эта их штука спамит коллекциями так что страшно думать. $collection->merge() - мы вроде как с обьектами работаем, а значит должно добавить в нашу коллекцию. Но вместо этого она создает вторую коллекцию, а первую - удаляет, чтобы вести себя так, как это было бы с двумя массивами.

    Вот в Ларавеле получается с этим дичь - они начали юзать обьекты, а потом пытаться реализовать поведение по-старинке, как в массивах. И для чего им теперь эта коллекция кроме как для проверки что она такого-то типа - непонятно.

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

    gzhegow
    @gzhegow Автор вопроса
    aka "ОбнимиБизнесмена"
    Похоже нашел некоторый удобный способ их применять. Опишу здесь, если кому пригодится.

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

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

    Делим возникающие косяки на:
    1. уведомление разработчика, что программа кривая и так не должно было быть
    2. уведомление/перенаправление пользователя
    3. оповещение других модулей в соседних папках о том, что случилось что-то, что они могли бы решить, но в одиночку никак

    В первую группу в основном падают стандартные эксепшены. Пишем мы функцию
    function a() : int
    {
      return 'vasia';
    }

    Функия возвращает не то, что просили. Об этом должен узнать разработчик, который делает на базе вашего кода. Программа не должна запуститься. Удобный способ делать такие проверки, писать следующий код:
    function verifyEmail($email) : ? string
    {
      if (filter_var($email, FILTER_VALIDATE_EMAIL)) return null;
      return __FUNCTION__ . ' fails: ' . $email;
    }
    // ... code
    if ($err = verifyEmail(123)) throw new \UnexpectedValueException($err);

    Мы возвращаем текст ошибки если проверка не прошла, валидируем.
    Так, например, можно сделать проверку входных параметров на собственный тип не занимаясь ООП ересью на зашивание проверки в конструктор и разбивание массива на свойства, каждое из которых сеттер с валидатором и тд. минус файл и куча логики. Эти ошибки бывают что требуют вывода дополнительной информации в трейс. А поскольку эксепшен не принимает данные дополнительным параметром, я рекомендую дописать функционал `public function data(array $data))` и в особых случаях вызывать throw (new MyException($err))->data($data); и тогда в логах можно добавить эту самую $data не прибегая к танцем с бубном. Она врядли понадобится системному администратору или девопсу, но она точно понадобится тому, кто пойдёт ошибку править. Опять же - в эксепшене есть дерево вызовов и там есть аргументы. Но пару раз по причинам мне непонятным - пыха оставляет там не всё, что я ожидаю там прочитать. В той же валидации аргументом прилетает одна строка, если вывести все дерево - лог будет слишком огромный. А если одну строку - то это ничего не скажет. Чтобы понять, почему валидация сломалась часто нужно увидеть весь входной словарь данных.

    Во вторую группу я отнес ошибки HTTP приложения, приложения как штуки способной принимать команды и отвечать на них. Это разные редиректы. Это 500ые коды сделанные вручную. Это отсутствие прав доступа. Всё что по-любому закончится HTTPException или RedirectBack. Программа запуститься, но ошибка может быть вызвана тем, что во внешних условиях что-то идёт не так - нет прав доступа, или этот кусок программы сейчас отключен. То есть это не косяк кода, а сознательно установленный запрет. Эти ошибки чаще всего нужно переводить на другой язык или писать заглушку. Эти ошибки как правило бросаются фреймворком или приложением, и имеют либо неймспейс как у фреймворка, либо что-нибудь вроде \App\HttpException;

    В третью группу я отнёс ошибки, которые требуют внешних зависимостей. Насчет этой группы я пока не уверен, может быть это из-за того что я неправильно разобрался с первыми двумя. Предположим у нас есть модуль авторизации пользователя и есть модуль для работы с JWT-токенами. Вот когда модуль не смог разобрать JWT токен - он должен кинуть ошибку "я отработал и решил, что что-то пошло не так, но дальше - не моя забота". JWT разбор выполняет очень узкую задачу - парсинг токена. Туда не всунешь зависимостью модуль авторизации, это как поставить директора в подчинение работнику. Можно попробовать НЕ-бросать эту ошибку путем того, что инициировать проверку токена в модуле Авторизации, чтобы прямо там её и обработать. Но это странно, потому что Авторизация может быть сделана десятью путями и каждый из них зависимостью хардкодить - не очень хорошо. Именно эти ошибки должны иметь неймспейс, потому что их будут ловить и с ними работать, пытаясь их заглушить. То есть на уровне модуля - это критическая ошибка. А на уровне программы - это предупреждение.

    На текущий момент мне кажется это правильным подходом. Если найду что-то ещё исправлю.
    Ответ написан
    Комментировать
  • Что нужно знать про ООП?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    А я бы добавил что ООП это украшение кода, а не его суть

    Cейчас есть способы платить Амазону и вообще не писать код, создавая апишки в админке с помощью мышки. Все что будет нужно от ПХП - это делать простые скрипты которые передают данные из точки А в точку Б. Там вообще не нужен будет ООП, потому что не будет понятия "цельный проект" в рамках папки с файлами. Цельный проект это будет куча компьютеров, а на этом конкретно есть передача из А в Б. И тут уже PHPшники посмеются)) Они то готовы к такому

    Увидев, что тебе понравился первый ответ (может ты его и искал?), я попробую пояснить его для тех, кому термины ничего не говорят:

    https://qna.habr.com/q/655113#answer_1431141

    думаю сейчас ты увидишь как набегут великие архитекторы, которые давали тебе советы по этим словам и начнут говорить что то не про это, а это не так и это не здесь. вот это еще одно что надо знать про ООП. Ты никогда не услышишь, что ты прав, потому что термины заменили им мозг, а если им сказать об этом - они объединяются в стаи, чтобы завалить тебя стикерами и унижениями.
    Ответ написан
    19 комментариев
  • Как сохранить в переменную объект с другим свойством не изменяя объект?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Рассматривай обьект как ящик с данными.
    Создал ящик. Положил туда что-то. На ящик навесил ярлык с переменной.
    Хочешь другой обьект - это другой ящик создавай новый или копируй (через клонирование) старый. Можешь в переменную положить другой ящик.

    Но помни что если ни одна из переменных не содержит информацию о твоем ящике, то ты его потерял.
    То есть
    $obj = new Obj;
    $obj = new Obj;

    дважды создал ящик и присвоил туда же - в итоге первый ты потерял, на него больше никто не ссылается и он будет удален. То же и со строкой будет если в одну переменную две строки положить - то первая же удалится.

    Создавай вторую. И там играйся со вторым ящиком
    Ответ написан
    Комментировать
  • Где посмотреть\почитать нормальные уроки по ООП в PHP?

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

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

    Дальнейшее "понимание глубины" не несет под собой ничего кроме того что есть авторитет, и есть вот ты. и ты никогда не станешь авторитетом, потому что он гуру и на горе. А ты здесь. И не бывает так, чтобы ты и авторитет. Зато бывает менталитет. Извиняюсь, кого обидел
    Ответ написан
  • Как правильно реализовать реализовать классы Родитель->Потомок?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Вот как я понимал события

    <?php
    
    $events = [];
    
    $on = function (string $name, callable $func) use (&$events)
    {
      $events[ $name ][] = $func;
    };
    
    $fire = function (string $name, $emitter, ...$arguments) use (&$events)
    {
      if (! isset($events[ $name ])) return;
      
      foreach ($events[ $name ] as $func) {
        call_user_func($func, $name, $emitter, ...$arguments);
      }
    };
    
    // когда машина выйдет с завода - поехали!
    $on('car-created', function ($event, $car, ...$comments) { var_dump($car, $comments); });
    
    // ...some application code
    
    // пришло время, тачка готова.
    $fire('car-created', $car, 'faster', 'darling');


    Что тут есть. Есть массив, хранилище, где будут лежать инструкции $func "что сделать", привязанные на ключ $name - "когда сделать".

    Есть функция $on, которая тупо на некое имя записывает в хранилище что делать.
    Есть функция $fire, которая "стреляет" - начинает выполнять всё, что висит на этом `$name`. В параметре эмиттер может быть что угодно, что душе хочется - обычно это виновник торжества, из-за чего событие стрельнуло.

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

    Вот когда понимаешь что события, это вот так вот просто, сверху можешь на них навесить классы, чтобы понять что эвентс лучше хранить в классе "Диспетчер", а типы событий тоже могут быть классами вместо `$name`, тогда в них можно какие-то функции зашить внутрь.

    Пока просто вот так отнесись к этому
    Ответ написан
    Комментировать
  • Можно ли в одном экземпляре класса получить ответ а в другом работать с ранее полученным ответом?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Первый скрипт запускается на сервере и второй там же. Что общего на сервере? Жесткий диск. Клади ответ в файл, потом файл читай.

    Если нужно оттолкнуться от прошлого ответа - можно сделать мультикурл (тогда это будет в одной странице, но больше ждать) - говоришь сначала сделай запросы туда туда и вон туда одновременно, а когда все они закончатся, делай дальше тото
    Ответ написан
    Комментировать
  • Можно ли это переписать на ООП? И как примерно всё это можно распределить по классам?

    gzhegow
    @gzhegow
    aka "ОбнимиБизнесмена"
    Мне очень понравилось, как мне объяснил это Алексей Пастушенко

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

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

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

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

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

    В остальном ООП это усложнение кода, которое стоит того. Но если тебе нужно быренько связать два сайта через один скрипт, то попытка нарисовать карту этой связи выжрет у тебя неделю. А написать функцию, которая это делает - займет минут 10. Поэтому надо быренько сделать - забей. Надо предсказуемость, логируемость, понимание что где происходит, история изменений, или что самое ядерное - откат в обратном порядке от того, как это делалось - придется рисовать карту. Или сдохнуть на проекте
    Ответ написан
    3 комментария