依赖
另一件事需要的东西,比如“汽车”中的“引擎”
容器
能够存储许多其他对象的对象或类,例如“引擎”,“变速箱”甚至“汽车”
依赖注入
每个依赖关系被注入对象的过程,所以如果我需要“汽车”,我知道我必须注入“引擎”,“变速箱”和许多其他东西.重要的是,“汽车”不会产生“引擎”,但“引擎”被放入“汽车”
服务定位器
对象请求另一个对象的过程,例如进入汽车时插入我们的容器,当汽车需要启动时需要从容器“引擎”,所以容器返回他的“引擎”
当我研究Symphony代码时,他们从依赖注入开始,但过了一段时间后我意识到当创建Controller时,会注入整个容器,然后你可以使用$this-> get(‘serviceName’)来获取它,所以它看起来更像服务定位器,根据少数文章是反模式.
播种怎么回事? DI和SL之间的界限是否很小,有时会破坏?或者我误解了什么?如果我使用DI,我是否需要将每个服务插入控制器,所以我从外面知道我使用的是什么?或者控制器在某些情况下可以容器?
此外,Symfony不以任何方式强制您使用它的Controller.你的控制器can be a service.天哪,它甚至可以是一个功能!
以下是将控制器实现为服务定位器的原因之一:性能.
让我们放下汽车类比,关注99%项目中你会遇到的真实情况:你需要CRUD资源.假设你正在构建一个Todo应用程序,你需要一个RESTfulish控制器来处理任务资源的CRUD操作.
您需要的最少的是一种读取所有任务的方法和一种添加新任务的方法,因为您需要两个操作:索引(通常也称为列表)和存储(通常也称为创建).
Symfony中的常见流程是伪代码:
indexAction -> getDoctrine -> getTaskRepository -> getAllTasks storeAction -> getFormFactory -> createForm -> bindRequestDataToForm -> getDoctrine -> saveData
如果Controller是服务定位器
指数行动
执行索引操作时,只有将从容器解析的服务才是ManagerRegistry(在本例中为Doctrine服务).然后我们会要求它给我们任务库,我们将用它来操作.
存储行动
当执行存储操作时,我们将做更多的工作:让容器给我们FormFactory,用它做一些操作,然后让它给我们Doctrine并用它做一些操作.
总结:当执行索引操作时,服务容器只需构造一个服务,执行更新时,必须构造两个服务.
如果Controller是常规服务
让我们看看我们的控制器需要什么.从上面的部分,我们看到它需要FormFactory和Doctrine.
现在,当您只想调用索引操作从数据存储中读取所有任务时,您的控制器必须通过容器实例化.在可以实例化之前,容器需要实例化它的依赖项:FormFactory和Doctrine.然后实例化控制器,同时将这两个注入其中.
因此,您正在调用根本不需要FormFactory的索引操作,但您仍然需要创建它的开销,因为在该请求中根本不会调用的操作需要它.
懒惰的服务
为了减少这种开销,有一个名为lazy service的东西.它通过实际将服务的代理注入控制器来工作.因此,就控制器而言,它有FormFactory.它不知道的是,它不是真正的FormFactory,而是一个伪对象,它会在你调用一些方法时委托调用真正的FormFactory代码.
把它包起来
控制器不必是服务定位器,但可以.使其成为服务定位器可以更高效,更容易引导,但隐藏了依赖性.此外,测试起来有点困难,因为你需要模拟依赖容器.您是否想要制作控制器服务,功能或服务定位器是您的选择,Symfony不会强制您使用这些方法.
根据我的经验,扩展默认的Symfony控制器并让控制器成为服务定位器就好了,只要你不在其中编写业务逻辑,而是将所有工作委托给从容器获得的服务.这样,你就不太可能在控制器代码中出现错误(因为方法通常包含2-3行代码)并且可以在不进行测试的情况下逃脱.