草庐IT

php - 了解 IoC 容器和依赖注入(inject)

coder 2023-04-28 原文

我的理解:

  • 依赖是指 ClassA 的实例需要 ClassB 的实例来实例化 ClassA 的新实例。
  • 依赖注入(inject)是当 ClassA 被传递一个 ClassB 的实例时,通过 ClassA 的构造函数中的参数或通过 set~DependencyNameHere~(~DependencyNameHere~ $param) 函数。 (这是我不完全确定的领域之一)。
  • IoC 容器是一个单例类(在任何给定时间只能实例化 1 个实例),其中可以注册为该项目实例化这些类的对象的特定方式。 Here's a link to an example of what I'm trying to describe along with the class definition for the IoC container I've been using

  • 所以在这一点上,我开始尝试将 IoC 容器用于更复杂的场景。到目前为止,似乎为了使用 IoC 容器,对于我想创建的几乎所有具有它想要在 IoC 容器中定义的依赖项的类,我都只能使用 has-a 关系。如果我想创建一个继承类的类,但前提是父类是以特定方式创建的,它在 IoC 容器中注册了怎么办。

    所以举个例子:我想创建一个mysqli的子类,但是我想在IoC容器中注册这个类,只用我之前在IoC容器中注册的方式构造的父类进行实例化。我想不出不复制代码的方法(因为这是一个学习项目,我试图让它尽可能“纯”)。 Here are some more examples of what I am trying to describe.

    所以这里是我的一些问题:
  • 在不违反 OOP 的某些原则的情况下,我上面尝试做的事情是否可行?我知道在 C++ 中我可以使用动态内存和复制构造函数来完成它,但我一直无法在 php 中找到那种功能。 (我承认,除了 __construct 之外,我几乎没有使用任何其他魔术方法的经验,但是如果我理解正确的话,从阅读和 __clone 来看,我无法在构造函数中使用它来使子类被实例化为父类的实例)。
  • 与 IoC 相关的所有依赖类定义应该放在哪里? (我的 IoC.php 应该在顶部有一堆 require_once('dependencyClassDefinition.php') 吗?我的直觉 react 是有更好的方法,但我还没有想出一个)
  • 我应该在哪个文件中注册我的对象?当前在类定义之后对 IoC.php 文件中的 IoC::register() 进行所有调用。
  • 在注册需要依赖项的类之前,是否需要在 IoC 中注册依赖项?因为在我实际实例化在 IoC 中注册的对象之前我不会调用匿名函数,所以我猜不是,但它仍然是一个问题。
  • 还有什么我应该做或使用的东西吗?我正在尝试一步一步来,但我也不想知道我的代码是可重用的,最重要的是,对我的项目一无所知的人可以阅读并理解它。
  • 最佳答案

    简单地说(因为它不是仅限于 OOP 世界的问题),一个 依赖 在这种情况下,组件 A 需要(依赖)组件 B 来做它应该做的事情。该词还用于描述此场景中的依赖组件。用 OOP/PHP 术语来说,请考虑以下带有强制性汽车类比的示例:

    class Car {
    
        public function start() {
            $engine = new Engine();
            $engine->vroom();
        }
    
    }
    
    Car取决于 Engine . EngineCar的依赖。不过这段代码很糟糕,因为:
  • 依赖是隐含的;您不知道它在那里,直到您检查 Car的代码
  • 类是紧密耦合的;您无法替代 EngineMockEngine用于测试目的或 TurboEngine在不修改 Car 的情况下扩展原始的.
  • 汽车能够为自己制造发动机看起来有点傻,不是吗?

  • 依赖注入(inject) 是通过使 Car 成为事实来解决所有这些问题的一种方式。需求Engine明确并明确地为其提供一个:
    class Car {
    
        protected $engine;
    
        public function __construct(Engine $engine) {
            $this->engine = $engine;
        }
    
        public function start() {
            $this->engine->vroom();
        }
    
    }
    
    $engine = new SuperDuperTurboEnginePlus(); // a subclass of Engine
    $car = new Car($engine);
    

    以上是的例子构造函数注入(inject) ,其中依赖(被依赖对象)通过类构造函数提供给被依赖者(消费者)。另一种方法是暴露 setEngine Car 中的方法类并使用它来注入(inject) Engine 的实例.这被称为 二传手注入(inject)并且主要用于应该在运行时交换的依赖项。

    任何重要的项目都由一堆相互依赖的组件组成,并且很容易很快就会忘记什么被注入(inject)到哪里。一个 依赖注入(inject)容器 是一个对象,它知道如何实例化和配置其他对象,知道它们与项目中其他对象的关系并为您进行依赖注入(inject)。这使您可以集中管理所有项目的(相互)依赖项,更重要的是,可以更改/模拟其中的一个或多个,而无需编辑代码中的一堆位置。

    让我们抛开汽车类比,以 OP 试图实现的目标为例。假设我们有一个 Database对象取决于 mysqli目的。假设我们想使用一个非常原始的依赖检测容器类 DIC公开两个方法:register($name, $callback)注册在给定名称下创建对象的方法和 resolve($name)从该名称获取对象。我们的容器设置看起来像这样:
    $dic = new DIC();
    $dic->register('mysqli', function() {
        return new mysqli('somehost','username','password');
    });
    $dic->register('database', function() use($dic) {
        return new Database($dic->resolve('mysqli'));
    });
    

    请注意,我们告诉容器获取 mysqli 的实例。从自身组装 Database 的实例.然后得到一个Database自动注入(inject)其依赖项的实例,我们只需:
    $database = $dic->resolve('database');
    

    这就是它的要点。一个更复杂但仍然相对简单且易于掌握的 PHP DI/IoC 容器是 Pimple .查看其文档以获取更多示例。

    关于 OP 的代码和问题:
  • 不要为你的容器(或其他任何事情)使用静态类或单例; they're both evil .改为查看 Pimple。
  • 决定是否需要您的 mysqliWrapper类扩展 mysql或依赖它。
  • 调用 IoC从内部 mysqliWrapper您正在将一种依赖项交换为另一种依赖项。你的对象不应该知道或使用容器;否则它不再是 DIC,而是服务定位器(反)模式。
  • 你不需要require一个类文件,然后再将其注册到容器中,因为您根本不知道是否要使用该类的对象。在一处完成所有容器设置。如果您不使用自动加载器,您可以 require在您向容器注册的匿名函数中。


  • 其他资源:
  • Inversion of Control Containers and the Dependency Injection pattern作者:马丁福勒
  • Don't look for things -- 干净的代码谈 IoC/DI
  • 关于php - 了解 IoC 容器和依赖注入(inject),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18562752/

    有关php - 了解 IoC 容器和依赖注入(inject)的更多相关文章

    1. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

      我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

    2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

      我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

    3. ruby - 这个 ruby​​ 注入(inject)魔术是如何工作的? - 2

      我今天看到了一个ruby​​代码片段。[1,2,3,4,5,6,7].inject(:+)=>28[1,2,3,4,5,6,7].inject(:*)=>5040这里的注入(inject)和之前看到的完全不一样,比如[1,2,3,4,5,6,7].inject{|sum,x|sum+x}请解释一下它是如何工作的? 最佳答案 没有魔法,符号(方法)只是可能的参数之一。这是来自文档:#enum.inject(initial,sym)=>obj#enum.inject(sym)=>obj#enum.inject(initial){|mem

    4. ruby - 有什么方法可以告诉 sidekiq 一项工作依赖于另一项工作 - 2

      有什么方法可以告诉sidekiq一项工作依赖于另一项工作,并且在后者完成之前无法开始? 最佳答案 仅使用Sidekiq;答案是否定的。正如DickieBoy所建议的那样,您应该能够在依赖作业完成时将其启动。像这样。#app/workers/hard_worker.rbclassHardWorkerincludeSidekiq::Workerdefperform()puts'Doinghardwork'LazyWorker.perform_async()endend#app/workers/lazy_worker.rbclassLaz

    5. ruby - 了解在 Ruby 中与 lambda 一起使用的 inject 行为 - 2

      我经常将预配置的lambda插入可枚举的方法中,例如“map”、“select”等。但是“注入(inject)”的行为似乎有所不同。例如与mult4=lambda{|item|item*4}然后(5..10).map&mult4给我[20,24,28,32,36,40]但是,如果我制作一个2参数lambda用于像这样的注入(inject),multL=lambda{|product,n|product*n}我想说(5..10).inject(2)&multL因为“inject”有一个可选的单个初始值参数,但这给了我......irb(main):027:0>(5..10).inject

    6. ruby-on-rails - 如何测试自己对 Ruby/ROR 的了解? - 2

      是否有self验证的问题列表。看着那个,我可以确定我知道。我应该复习一下。在学习的过程中,我列了一个这样的list,但它只包含我在某处听说过的项目。我需要一段时间才能找到新的东西。 最佳答案 以下是针对ruby​​和Rails的一些测试列表。证书名称:RubyonRails谁提供:oDeskIncorporation认证费用:免费网站:https://www.odesk.com/tests/985?pos=0证书名称:RubyonRails提供者:Techgig.com(TimesBusinessSolutionsLimited(T

    7. ruby-on-rails - 了解 "attribute_will_change!"方法 - 2

      我想覆盖store_accessor的getter。可以查到here.代码在这里:#Fileactiverecord/lib/active_record/store.rb,line74defstore_accessor(store_attribute,*keys)keys=keys.flatten_store_accessors_module.module_evaldokeys.eachdo|key|define_method("#{key}=")do|value|write_store_attribute(store_attribute,key,value)enddefine_met

    8. ruby - 有人可以解释一下在 Ruby 中注入(inject)的真实、通俗易懂的用法吗? - 2

      我正在学习Ruby,遇到了inject。我正处于理解它的风口浪尖,但当我是那种需要真实世界的例子来学习一些东西的人时。我遇到的最常见的例子是人们使用inject来添加一个(1..10)范围的总和,我不太关心这个。这是一个任意的例子。在实际程序中我会用它做什么?我正在学习,所以我可以继续使用Rails,但我不必有一个以Web为中心的示例。我只需要一些我可以全神贯注的目标。谢谢大家。 最佳答案 inject有时可以通过它的“其他”名称reduce更好地理解。它是一个对Enumerable进行操作(迭代一次)并返回单个值的函数。它有许多有

    9. ruby - 防止SQL注入(inject)/好的Ruby方法 - 2

      Ruby中防止SQL注入(inject)的好方法是什么? 最佳答案 直接使用ruby?使用准备好的语句:require'mysql'db=Mysql.new('localhost','user','password','database')statement=db.prepare"SELECT*FROMtableWHEREfield=?"statement.execute'value'statement.fetchstatement.close 关于ruby-防止SQL注入(inject

    10. ruby - 我怎样才能更好地了解/了解更多关于 Ruby 的知识? - 2

      按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭9年前。我最近开始学习Ruby,这是我的第一门编程语言。我对语法感到满意,并且我已经完成了许多只教授相同基础知识的教程。我已经写了一些小程序(包括我自己的数组排序方法,在有人告诉我谷歌“冒泡排序”之前我认为它非常聪明),但我觉得我需要尝试更大更难的东西来理解更多关于Ruby.关于如何执行此操作的任何想法?

    随机推荐