【问题标题】:Write a class in PHP Symfony 4.4 using the design pattern Factory Method to instantiate Service classes在 PHP Symfony 4.4 中编写一个类,使用设计模式 Factory Method 来实例化 Service 类
【发布时间】:2019-11-28 09:29:32
【问题描述】:

使用激活自动装配的 Symfony 4.4,我想使用设计模式 FactoryMethod 实例化一个类。

实例化的类是一个将自动装配的参数传递给构造函数的服务。

如果在工厂方法内部实例化每种类型的类的构造函数都相同,则效果很好。

但是,每个要实例化的服务都必须自动装配一些特定的服务才能工作。

我发现我们可以使用“setter 依赖注入”。描述它的文章:

我试图实现setter依赖注入,但里面的代码从未执行过。

考虑到文章,我们应该在调用 __construct 方法后立即使用 PHPDoc“@required”输入设置器(据我了解)。

它不适用于我的代码(见下文)。

我的实现是否正确?

有没有更好的方法?

我的代码如下:

// Controller
/**
 *@Route("/my_action/{param}")
 */
public function my_action (ThingManagerFactory $thingManagerFactory, $param)
{
    $thingManager = $thingManagerFactory->get($param);
    $thingManager->doSomething();
}


// ThingManagerFactory

class ThingManagerFactory
{
    private $firstManager;
    private $secondManager;
    private $thirdManager;

    public function __construct(FirstManager $firstManager, SecondManager $secondManager, ThirdManager $thirdManager)
    {
        $this->firstManager = $firstManager;
        $this->secondManager = $secondManager;
        $this->thirdManager = $thirdManager;
    }

    public function get($param): ThingManagerInterface
    {
        if($param == 1) {
            return new Thing1Manager(
                $this->firstManager,
                $this->secondManager,
                $this->thirdManager,
            );
        } elseif($param == 2) {
            return new Thing2Manager(
                $this->firstManager,
                $this->secondManager,
                $this->thirdManager,
            );
        }

        throw new \InvalidArgumentException("...");
    }
}


// ThingManagerInterface
interface ThingManagerInterface
{
    public function __construct(
            $this->firstManager,
            $this->secondManager,
            $this->thirdManager,
        );
    public function doSomething();
}


// Thing1Manager
class Thing1Manager implements ThingManagerInterface
{
    (...)
    private $spec1Manager;

    public function __construct(
            $this->firstManager,
            $this->secondManager,
            $this->thirdManager,
        )
    {
        (...)
    }

    /**
     * @required
     */
    public function setSpecificManager(Spec1Manager $spec1Manager)
    {
        // this code is never called
        $this->spec1Manager = $spec1Manager;
    }

    public function doSomething()
    {
        // we pass here before going into setSpecificManager
        (...)
    }
}


// Thing2Manager class
// is similar to Thing1Manager with multiple other specific managers.



感谢您的帮助。

【问题讨论】:

  • '@required' 参数与 Symfony 容器一起工作。新运营商对此一无所知。你的导师可能在谈论service locators。但是您确实需要向他/她/其他人询问更多详细信息。
  • 事实上,我正在努力做到这里所说的:autowiring other methods.
  • 是的,但 autowire 只能在 Symfony 服务容器中工作。 return new Thing1Manager() 不会自动调用 Thing1Manager::setSpecificManager。使用服务定位器(基本上是 Symfony 容器),您将返回 $serviceLocator->get($param) ,它会负责注入所有依赖项。
  • 我不明白必须在哪里定义服务定位器。我是否必须在每个 ThingXManager 中使用每个人需要的特定服务来定义它?或者在控制器中忘记编写 FactoryMethod 类?我应该把“return $serviceLocator->get($param)”放在哪里?感谢您的帮助。
  • 当尝试在 Thing1Manager 中添加服务定位器时: public static function getSubscribedServices() { return [ 'App\Manager\Spec1Manager' => Spec1Manager::class ];我收到以下错误:编译容器时,“App\Manager\Spec1Manager”服务或别名已被删除或内联。您应该将其公开,或者直接停止使用容器并改用依赖注入。

标签: php symfony design-patterns


【解决方案1】:

为了在 Symfony 中使用设计模式工厂方法,请使用服务定位器在控制器外部提供自动装配。 重构代码如下:

// Controller
/**
 *@Route("/my_action/{param}")
 */
public function my_action (ThingManagerFactory $thingManagerFactory, $param)
{
    $thingManager = $thingManagerFactory->get($param);
    $thingManager->doSomething();
}


// ThingManagerFactory
use App\Locator\ThingLocator;
class ThingManagerFactory
{
    private $locator;

    public function __construct(ThingLocator $locator)
    {
        $this->locator = $locator;
    }

    public function get($param): ThingManagerInterface
    {
        if($param == 1) {
            return $this->locator->get(Thing1Manager::class);
        } elseif($param == 2) {
            return $this->locator->get(Thing2Manager::class);
        }

        throw new \InvalidArgumentException("...");
    }
}


// ServiceLocatorInterface
interface ServiceLocatorInterface
{
    public function get(string $id);
}


// ThingLocator
class ThingLocator implements ServiceLocatorInterface, ServiceSubscriberInterface
{
    private $locator;

    public function __ construct(ContainerInterface $locator)
    {
        $this->locator = $locator;
    }

    public function get(string $id)
    {
        if (!$this->locator->has($id)) {
            throw new \Exception("The entry for the given '$id' identifier was not found.");
        }

        try {
            return $this->locator->get($id);
        } catch (ContainerExceptionInterface $e) {
            throw new \Exception("Failed to fetch the entry for the given '$id' identifier.");
        }
    }

    public static function getSubscribedServices()
    {
        return [
            Thing1Manager::class,
            Thing2Manager::class,
        ];
    }
}


// ThingManagerInterface
interface ThingManagerInterface
{
    public function doSomething();
}


// Thing1Manager
class Thing1Manager implements ThingManagerInterface
{
    // ...
    private $spec1Manager;

    public function __construct($firstManager, $secondManager, $thirdManager, $spec1Manager)
    {
        // ...
    }

    // This setter is no more needed. This manager can be added to the constructor method.
    // **
    // * @required
    // */
    //public function setSpecificManager(Spec1Manager $spec1Manager)
    //{
        // if not commented, this code would be called thanks to the Service Locator (which is a Symfony Service Container)
    //    $this->spec1Manager = $spec1Manager;
    //}

    public function doSomething()
    {
        // ...
    }
}

【讨论】:

    猜你喜欢
    • 2013-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-26
    相关资源
    最近更新 更多