Я пишу свою собственную реализацию Laravel Service Container, чтобы попрактиковаться в некоторых шаблонах проектирования, а затем создать частную микросхему.
Класс выглядит сейчас так:
class Container implements ContainerInterface
{
/**
* Concrete bindings of contracts.
*
* @var array
*/
protected $bindings = [];
/**
* Lists of arguments used for a class instantiation.
*
* @var array
*/
protected $arguments = [];
/**
* Container's storage used to store already built or customly setted objects.
*
* @var array
*/
protected $storage = [];/**
* Returns an instance of a service
*
* @param $name
* @return object
* @throws \ReflectionException
*/
public function get($name) {
$className = (isset($this->bindings[$name])) ? $this->bindings[$name] : $name;
if (isset($this->storage[$className])) {
return $this->storage[$className];
}
return $this->make($className);
}
/**
* Creates an instance of a class
*
* @param $className
* @return object
* @throws \ReflectionException
*/
public function make($className) {
$refObject = new \ReflectionClass($className);
if (!$refObject->isInstantiable()) {
throw new \ReflectionException("$className is not instantiable");
}
$refConstructor = $refObject->getConstructor();
$refParameters = ($refConstructor) ? $refConstructor->getParameters() : [];
$args = [];
// Iterates over constructor arguments, checks for custom defined parameters
// and builds $args array
foreach ($refParameters as $refParameter) {
$refClass = $refParameter->getClass();
$parameterName = $refParameter->name;
$parameterValue =
isset($this->arguments[$className][$parameterName]) ? $this->arguments[$className][$parameterName]
: (null !== $refClass ? $refClass->name
: ($refParameter->isOptional() ? $refParameter->getDefaultValue()
: null));
// Recursively gets needed objects for a class instantiation
$args[] = ($refClass) ? $this->get($parameterValue)
: $parameterValue;
}
$instance = $refObject->newInstanceArgs($args);
$this->storage[$className] = $instance;
return $instance;
}
/**
* Sets a concrete implementation of a contract
*
* @param $abstract
* @param $concrete
*/
public function bind($abstract, $concrete) {
$this->bindings[$abstract] = $concrete;
}
/**
* Sets arguments used for a class instantiation
*
* @param $className
* @param array $arguments
*/
public function setArguments($className, array $arguments) {
$this->arguments[$className] = $arguments;
}
}
Это работает нормально, но я ясно вижу нарушение SRP в make()
метод. Поэтому я решил делегировать логику создания объекта отдельному классу.
Проблема, с которой я столкнулся, состоит в том, что этот класс будет тесно связан с Container
учебный класс. Потому что ему нужен доступ к $bindings
а также $arguments
массивы, и get()
метод. И даже если мы передадим эти параметры классу, хранилище все равно останется в контейнере. В общем, вся архитектура неверна, и нам нужно еще 2 класса: StorageManager
а также ClassFactory
, Или, может быть ClassBuilder
? И должен ClassFactory
быть в состоянии построить аргументы конструктора или ему нужен другой класс — ArgumentFactory
?
Что вы думаете, ребята?
Задача ещё не решена.
Других решений пока нет …