我正在编写自己的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;
}
}
它工作正常,但我清楚地看到make()
方法中存在违反SRP的情况。所以我决定将一个对象创建逻辑委托给一个单独的类。
我遇到的一个问题是这个类将与Container
类紧密耦合。因为它需要访问$bindings
和$arguments
数组以及get()
方法。即使我们将这些参数传递给类,存储仍然保留在容器中。所以基本上所有架构都是错误的,我们还需要另外两个类:StorageManager
和ClassFactory
。或者也许ClassBuilder
? ClassFactory
是否应该能够构建构造函数参数,还是需要另一个类 - ArgumentFactory
?
你觉得怎么样?