frolover
@frolover

Класс управления зависимостями, как вам реализация?

Набросал класс управления зависимостями в приложении

/**
 * Class DependencyManager
 */
class DependencyManager {

    /**
     * @var array of dependencies
     */
    public $dependencies;

    /**
     * DependencyManager constructor
     */
    public function __construct() {

        $this->dependencies = [

          [
              'class' => 'SomeClass',
              'dependencies' => [
                  'DependentedClass', 'SecondDependentedClass'
              ]
          ]

        ];

    }

    /**
     * @param $class
     * @return null OR Dependencies of class
     */
    public function checkDependencies($class) {

        $dependenciesByClass = null;

        foreach( $this->dependencies as $data ) {

            if($data['class'] === $class) {

                $dependenciesByClass = $data['dependencies'];
                break;
            }

        }

        return $dependenciesByClass;

    }

}

/**
 * Class DependentedClass
 */
class DependentedClass {

    public $variable = 'this text placed in DependentedClass <br />';

}

/**
 * Class SecondDependentedClass
 */
class SecondDependentedClass {

    public $moreVariable = 'this text placed in SecondDependentedClass';

}

/**
 * Class SomeClass
 * class for example
 * It is dependent on other classes
 */
class SomeClass {

    public $varInDependentedClass;
    public $varInSecondDependentedClass;


    public function __construct(DependentedClass $dependentedClass, SecondDependentedClass $secondDependentedClass) {

        $this->varInDependentedClass = $dependentedClass->variable;

        $this->varInSecondDependentedClass = $secondDependentedClass->moreVariable;

    }

}

/**
 * Class Loader
 * This class creates an instance of any class
 */
class Loader {

    public $className; 

    public $dependencyManager;

    /**
     * Loader constructor.
     * @param DependencyManager $dependencyManager
     */
    public function __construct(DependencyManager $dependencyManager) {

        $this->dependencyManager = $dependencyManager;

    }

    /**
     * @return null|object
     * this method loads classes
     */
    public function loadClass() {

        $dependencies = null;
        $arrayObjects = null;
        $reflection = null;
        $instance = null;

        $this->className = 'SomeClass';

        if( !is_null($this->className) ) {

            $dependencies = $this->dependencyManager->checkDependencies($this->className);

            if( !is_null($dependencies) ) {

                for($i = 0; $i < count($dependencies); $i++) {

                    $arrayObjects[$i] = new $dependencies[$i];

                }

                $reflection = new ReflectionClass( $this->className );

                $instance = $reflection->newInstanceArgs($arrayObjects);

            } else {
                $instance = new $this->className;
            }
        }
        return $instance;
    }
}

Usage:
$loader = new Loader( new DependencyManager() );

var_dump( $loader->loadClass() );

/*
 * var_dump() results:
 * object(SomeClass)#6
 *  (2) {
 *  ["varInDependentedClass"] => string(43) "this text placed in DependentedClass"
 *  ["varInSecondDependentedClass"] => string(42) "this text placed in SecondDependentedClass"
 *  }
 */

Очень удобная вещь, теперь можно по частям тестировать приложение.

НО

Что меня смущает:
1. То что нужно вручную собирать массив зависимостей ( метод DependencyManager::__construct() );
2. Что возможно я что то упустил либо нагородил, например формирование массива объектов-зависимостей ( метод Loader::loadClass() )

UPDATE
Еще пришла идея в классы с зависимостями передавать в аргументах интерфейсы, а не объекты.

Прошу заметить что это не полностью рабочий вариант, и прошу дать совет по основной идее работы класса, а не по пропущенным проверкам и тд. Многие моменты максимально облегчены для быстрого понимания
  • Вопрос задан
  • 455 просмотров
Решения вопроса 3
Вы решили написать свой Dependency Injection Container с блекджеком и ***?
Да, прописывать зависимости руками (в конфиге) --- это норм.
Если не хотите этим заниматься, то гуглите по ключевым словам "di container autowiring"
Ответ написан
@shaqster
C# Symfony2|3 Angular Govnokod Developer
Вот же на гитхабе симфонийский DI. Внешних зависимостей нет. Зачем изобретать велосипед? Для самообразования? Лучше изучить стандартизированный проверенный инструмент, чем писать свое на коленке.
Ответ написан
index0h
@index0h
PHP, Golang. https://youtube.com/index0h
// Должен быть приватным
DependencyManager::$dependencies;

// Что это за хардкод?))
DependencyManager::__construct

// 1. Вы не проверяете аргументы, что будет, если я передам в $class
// например new \Exception()? А еще лучше строку "trololo"?
// 2. Нейминг - гуано, вы не проверяете зависимость, а возвращаете.
// 3. Если зависимость не найдена - будьте добры исключение.
DependencyManager::checkDependencies($class)

// Эта переменная не нужна, передавайте ее в метод
Loader::$className

// Эта переменная должна быть приватная, что будет, если туда вставить
// например строку и попытаться обработать?
Loader::$dependencyManager;

// Херня. У вас метод делает какую-то магию.
Loader::loadClass


Из SOLID вы нарушили:
* SRP - Loader выполняет И управление зависимостями и подгрузку. То что вы называете DependencyManager - это конфигурация, но ни как не менеджер зависимостей
* OCP - у вас явно открыты свойства, которые отвечают за внутреннюю логику выполнения. А то вот конфиги из вашей конфигурации зависимостей стоит возвращать через геттер
* ISP - вы в принципе не заморачиваетесь с интерфейсами, а полностью зависите от реализации.

Странно то, что загрузку не делаете рекурсивной, что если у вас у зависимого класса тоже есть зависимость?
Плохо то, что инициализируете сразу все классы, что если их будет много?

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

Отличная идея)) как только в PHP появится инициализация интерфейсов - вернетесь к ней, а пока что забудьте.

Когда наиграетесь - выбрасывайте и переходите на качественные решения, по совету shaqster
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

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

Войти через центр авторизации
Похожие вопросы
ProfitClicks Краснодар
от 80 000 руб.
от 160 000 до 210 000 руб.
IMAGINWEB Москва
от 55 000 до 85 000 руб.
16 июн. 2019, в 19:42
500 руб./за проект
16 июн. 2019, в 18:38
5000 руб./за проект
16 июн. 2019, в 17:14
5000 руб./за проект