gzhegow
@gzhegow
aka "ОбнимиБизнесмена"

Как разобрать зависимости без лямбда-замыканий, какая архитектурная ошибка допущена?

Class ValidatorComponent
{
  const ERR_BAD_PATH = 1;
  const ERR_BAD_ARGUMENT = 2;

  private $path;

  public function __construct(PathComponent $path)
  {
    $this->path = $path;
  }

  public function someMethod()
  {
    return true;
  }

  public function isPath($path) : int
  {
    return $this->path->isPath($path) ? 0 : self::ERR_BAD_PATH;
  }

  public function isCorrectArgument($argument)
  {
    return $this->someMethod($argument) ? 0 : self::ERR_BAD_ARGUMENT;
  }
}


Class PathComponent
{
  private $validator;

  public function __construct(ValidatorComponent $validator)
  {
    $this->validator = $validator;
  }

  public function isPath($path)
  {
    return true;
  }

  public function someAction($argument)
  {
    if (! $this->validator->isCorrectArgument($argument)) throw new InvalidArgumentException($argument);
    // do something
  }
}


Получается что модули взаимозависимы.

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

Происходит "отложенное создание обьекта", которое можно объявить
- колбэком (в конфиге инжектора) - сначала создаем первый, кидаем в него второй ещё не готовый, потом допиливаем первый, а поскольку по ссылке - то будет работать
- методом (зашить контейнер прямо в модуль и запросить явно $this->container->get())
- фабрикой (кинуть на вход фабрику которая в нужный момент выполнит обращение в контейнер $this->factory->create() а под капотом все равно $this->container->get())
и т.д.

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

ps. есть глупая догадка типа я попытался объединить в одном модуле "опрос системы" и "изменение системы", дескать нужно ввести класс который будет делать someAction() и ему нужен валидатор, и класс который содержит проверки - которому требуется модуль, вместо того чтобы избавиться от циклической зависимости 1 к 1, ввести третьего и тогда зависимость будет не взаимная
  • Вопрос задан
  • 170 просмотров
Решения вопроса 2
index0h
@index0h
PHP, Golang. https://github.com/index0h
Может вам ValueObject стоит заюзать? В примерно таком виде:

class Path
{
    private $value;

    public __construct($path string)
    {
        // validate path here
       $this->value = $path;
    }

    public function getValue(): string
    {
        return $this->value;
    }
}


Дальше уже в этой обертке по вашему проекту может летать проверенный и корректный path.
Обычно чем раньше найдены проблемные данные - тем раньше стоит бросить исключение.

Я понимаю, что в моем случае - не про отложенность, может вы более подробно опишете, зачем оно вам надо?
Ответ написан
gzhegow
@gzhegow Автор вопроса
aka "ОбнимиБизнесмена"
Чуть позже обдумав - понял, что правило простое "если А зависит от Б, а Б зависит от А, нужно сделать В, от которого зависит и А и Б".

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

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

устарело

По проблеме с циклическими зависимостями найдено решение. Кому интересно напишите, расскажу весь путь как шли и почему так. Спасибо Андрей Ковалёв, Алексей Пастушенко, еще один Алексей, Александру Захаренко, Human с t.me@oop_ru канала, Александру Markwhide и Ярославу Кравцову, и еще двум парням в телеге

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

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

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

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

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

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

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы