Почему целесообразнее не использовать new для работы с классами, а предпочесть DI?

Ссылка скопирована
PHP
1 ответ

на проекте, куда попал, принято каждый сервис вызывать внутри функций других классов в виде
(new MyService())->getData() - к примеру.

Но у меня было убеждение, что тогда уж лучше в конструктор класса прокинуть его:
private readonly MyService $service;

или прям в метде контроллера:

public function store(Request $request,  Myservice $service) {    $newData = $service->getData($request->all()); }

public function store(Request $request, Myservice $service) { $newData = $service->getData($request->all()); }

в общем можете мне с точки зрения целесообразности объяснить как правильнее?

Дополнительно:

Шли годы разработки на ларавел и вдруг Слава открыл для себя DI...

  • JhaoDa, ты хотя бы читаешь содержание поста прежде, чем написать комментарий? там написано, что пришел на проект и везде используют new в этом проекте. а я привык использвоать di. и попросил пояснить за неправильность подхода с использованием new/
  • Слава, у человека, который знает и понимает DI, такого вопроса бы не возникло.
  • Вот почему: PHPunit замокать класс внутри тестируемого класса?
    Бонус-раунд: DI упрощает работу с зависимостями зависимостей. Если ваш MyService принимает какой-нибудь логер в зависимостях, который в свою очередь ожидает какой-нибудь драйвер файловой системы, вы задолбаетесь собирать их через new.

    Правильнее через внедрение зависимости (в частности DI).
    В конструктор нужно передавать интерфейс.
    Простой пример, в зависимости от настроек нам нужно отправить сообщение или через email или просто записать в файл для отладки, тогда

    interface MessageProviderInterface {     public function sendMessage($from, $to, $text); }  class Email implements MessageProviderInterface{} class File implements MessageProviderInterface{}  class NewService {    private MessageProviderInterface $provider;    public function __construct(MessageProviderInterface $provider) {       $this->provider = $provider;   } }  $provider = getenv('ENV') === 'DEBUG' ? new FileProvider() : new EmailProvider(); $service = new NewService($provider);

    interface MessageProviderInterface { public function sendMessage($from, $to, $text); } class Email implements MessageProviderInterface{} class File implements MessageProviderInterface{} class NewService { private MessageProviderInterface $provider; public function __construct(MessageProviderInterface $provider) { $this->provider = $provider; } } $provider = getenv('ENV') === 'DEBUG' ? new FileProvider() : new EmailProvider(); $service = new NewService($provider);

    Это буквы I и D в принципе SOLID.
    DI контейнер просто позволяет проще регистрировать эти зависимости (не только но в основном)

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

      если каждый раз вызывать new то это и по perfomance такое себе, выделение памяти, процесорное время, если это делать часто то есть определенный оверхед (тут конечно надо смотреть на ситуацию, DTO предположим вполне себе нужно создавать каждый раз на разные данные)

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

    Главные аргументы против new.
    1. Невозможно написать unit-тест на класс
    2. Нельзя сделать то что написал devian3000 в своем примере
    3. По коду непонятно от чего зависит класс.
    4. Хорошие контейнеры обеспечивают переиспользование уже созданных классов, что может давать выигрыш в скорости

    Нужно решить такую задачу?

    Опишите проблему, и специалист поможет с настройкой, исправлением ошибки или доработкой сайта. Подберём понятный план работ без лишней переписки.

    Заказать помощь
    Лучший ответ
    1
    Алексей Денисов Ответ

    При работе с классами в PHP, использование Dependency Injection (DI) может быть более предпочтительным подходом, чем создание новых экземпляров классов с помощью оператора `new`. Вот несколько причин, почему DI может быть более целесообразным:

    1. Гибкость и управляемость зависимостей: Используя DI, вы можете легко управлять зависимостями классов, внедряя их извне. Это делает ваш код более гибким и модульным, что в свою очередь облегчает тестирование и поддержку.

    2. Уменьшение связанности: Используя DI, вы уменьшаете связанность между классами, так как классы не создают свои зависимости самостоятельно, а получают их извне. Это позволяет легко заменять зависимости или вносить изменения без необходимости изменения самого класса.

    3. Легкость тестирования: При использовании DI вы можете легко подменять зависимости классов на моки или заглушки во время тестирования, что делает тестирование более простым и эффективным.

    4. Разделение ответственностей: Используя DI, вы четко разделяете ответственности классов, так как каждый класс отвечает только за свою логику, а зависимости внедряются извне. Это способствует созданию более чистого и структурированного кода.

    Пример использования DI в PHP:

    class UserService {
        private $userRepository;
     
        public function __construct(UserRepository $userRepository) {
            $this->userRepository = $userRepository;
        }
     
        public function getUserById($id) {
            return $this->userRepository->findById($id);
        }
    }
     
    class UserRepository {
        public function findById($id) {
            // Логика получения пользователя из базы данных
        }
    }
     
    // Использование DI
    $userRepository = new UserRepository();
    $userService = new UserService($userRepository);
     
    $user = $userService->getUserById(1);

    class UserService { private $userRepository; public function __construct(UserRepository $userRepository) { $this->userRepository = $userRepository; } public function getUserById($id) { return $this->userRepository->findById($id); } } class UserRepository { public function findById($id) { // Логика получения пользователя из базы данных } } // Использование DI $userRepository = new UserRepository(); $userService = new UserService($userRepository); $user = $userService->getUserById(1);

    Таким образом, использование Dependency Injection может улучшить структуру вашего кода, сделать его более гибким и управляемым, а также упростить тестирование.

    Другие ответы (0)

    Пока нет других ответов. Будьте первым, кто поможет автору.

    Ответить на вопрос

    комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *

    Вам также может быть интересно