设计服务容器:SRP,选择正确的创建模式并避免紧密耦合

时间:2016-01-06 00:25:35

标签: php oop design-patterns containers service-locator

我正在编写自己的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()方法。即使我们将这些参数传递给类,存储仍然保留在容器中。所以基本上所有架构都是错误的,我们还需要另外两个类:StorageManagerClassFactory。或者也许ClassBuilderClassFactory是否应该能够构建构造函数参数,还是需要另一个类 - ArgumentFactory

你觉得怎么样?

0 个答案:

没有答案