UFO ET IT

IoC 컨테이너 및 종속성 주입 이해

ufoet 2020. 12. 11. 19:03
반응형

IoC 컨테이너 및 종속성 주입 이해


내 이해 :

  • 종속성은 ClassA의 인스턴스가 ClassA의 새 인스턴스를 인스턴스화하기 위해 ClassB의 인스턴스가 필요한 경우입니다.
  • 종속성 주입은 ClassA가 ClassA 생성자의 매개 변수 또는 set ~ DependencyNameHere ~ (~ DependencyNameHere ~ $ param) 함수를 통해 ClassB의 인스턴스에 전달되는 경우입니다. (이것은 내가 완전히 확신하지 못하는 영역 중 하나입니다) .
  • IoC 컨테이너는이 프로젝트에 대해 해당 클래스의 개체를 인스턴스화하는 특정 방법을 등록 할 수있는 싱글 톤 클래스 (주어진 시간에 1 개의 인스턴스 만 인스턴스화 할 수 있음)입니다. 다음은 내가 사용하고있는 IoC 컨테이너에 대한 클래스 정의와 함께 설명하려는 예제에 대한 링크입니다.

이 시점에서 더 복잡한 시나리오에 IoC 컨테이너를 사용하기 시작합니다. 현재로서는 IoC 컨테이너를 사용하기 위해 IoC 컨테이너에서 정의하려는 종속성이있는 생성하려는 거의 모든 클래스에 대한 has-a 관계로 제한되어 있습니다. 클래스를 상속하는 클래스를 만들고 싶지만 부모 클래스가 특정 방식으로 생성 된 경우에만 IoC 컨테이너에 등록 된 경우 어떻게해야합니까?

예를 들어 mysqli의 자식 클래스를 만들고 싶지만 IoC 컨테이너에이 클래스를 등록하여 이전에 IoC 컨테이너에 등록한 방식으로 구성된 부모 클래스로만 인스턴스화하고 싶습니다. 코드를 복제하지 않고는이 작업을 수행하는 방법을 생각할 수 없습니다 (그리고 이것은 학습 프로젝트이기 때문에 가능한 한 '순수한'상태로 유지하려고합니다). 여기에 제가 설명하려는 몇 가지 예가 더 있습니다.

그래서 여기 내 질문 중 일부가 있습니다.

  • OOP의 일부 원칙을 위반하지 않고 위에서하려는 것이 가능합니까? 나는 C ++에서 동적 메모리와 복사 생성자를 사용하여이를 수행 할 수 있다는 것을 알고 있지만 PHP에서는 그런 종류의 기능을 찾을 수 없었습니다. (나는 __construct 외에 다른 마법의 방법을 사용하는 경험이 거의 없다는 것을 인정할 것이지만, 내가 올바르게 이해했다면 읽기와 __clone에서 생성자에서 자식 클래스를 인스턴스화되는 복제로 만들 수 없었습니다. 부모 클래스의 인스턴스).
  • 모든 종속성 클래스 정의는 IoC와 관련하여 어디로 가야합니까? (내 IoC.php에 require_once ( 'dependencyClassDefinition.php')가 많이 있어야합니까? 내 직감은 더 나은 방법이 있다는 것입니다.하지만 아직 하나를 생각해 내지 못했습니다)
  • 내 개체를 어떤 파일에 등록해야합니까? 현재 클래스 정의 후에 IoC.php 파일에서 IoC :: register ()에 대한 모든 호출을 수행하고 있습니다.
  • 종속성이 필요한 클래스를 등록하기 전에 IoC에 종속성을 등록해야합니까? IoC에 등록 된 개체를 실제로 인스턴스화 할 때까지 익명 함수를 호출하지 않기 때문에, 그렇지 않을 것 같지만 여전히 문제입니다.
  • 내가해야 할 일이나 사용해야하는 것 외에 내가 간과하는 것이 있습니까? 한 번에 한 단계 씩 수행하려고하지만 내 코드를 재사용 할 수 있고 가장 중요한 것은 내 프로젝트에 대해 전혀 모르는 사람이 코드를 읽고 이해할 수 있다는 사실을 알고 싶지도 않습니다.

간단히 말해서 (OOP에만 국한된 문제가 아니기 때문에) 종속성 은 구성 요소 A가해야 할 일을 수행하기 위해 구성 요소 B가 필요 ( 종속 )되는 상황입니다. 이 단어는이 시나리오에서 종속 된 구성 요소를 설명하는데도 사용됩니다. 이것을 OOP / PHP 용어로 표현하려면 의무적 인 자동차 비유와 함께 다음 예를 고려하십시오.

class Car {

    public function start() {
        $engine = new Engine();
        $engine->vroom();
    }

}

Car 의존Engine. Engine이다 Car의존성 . 이 코드는 다음과 같은 이유로 매우 나쁩니다.

  • 종속성은 암시 적입니다. Car의 코드 를 검사 할 때까지 거기에 있는지 알 수 없습니다.
  • 클래스는 밀접하게 결합되어 있습니다. 당신은 대체 할 수없는 Engine과를 MockEngine테스트 목적 또는 TurboEngine그은을 수정하지 않고 원래의 확장 Car.
  • 자동차가 스스로 엔진을 만들 수 있다는 것은 어리석은 것 같지 않습니까?

종속성 주입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);

위는 클래스 생성자를 통해 종속 (소비자)에게 종속성 (종속 된 객체)이 제공되는 생성자 주입 의 예입니다 . 또 다른 방법은 클래스 setEngine에서 메서드를 노출하고 Car이를 사용하여 Engine. 이것은 setter 주입 으로 알려져 있으며 주로 런타임에 교체되어야하는 종속성에 유용합니다.

Any non-trivial project consists of a bunch of interdependent components and it gets easy to lose track on what gets injected where pretty quickly. A dependency injection container is an object that knows how to instantiate and configure other objects, knows what their relationship with other objects in the project are and does the dependency injection for you. This lets you centralize the management of all your project's (inter)dependencies and, more importantly, makes it possible to change/mock one or more of them without having to edit a bunch of places in your code.

Let's ditch the car analogy and look at what OP's trying to achieve as an example. Let's say we have a Database object depending on mysqli object. Let's say we want to use a really primitive dependency indection container class DIC that exposes two methods: register($name, $callback) to register a way of creating an object under the given name and resolve($name) to get the object from that name. Our container setup would look something like this:

$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'));
});

Notice we're telling our container to grab an instance of mysqli from itself to assemble an instance of Database. Then to get a Database instance with its dependency automatically injected, we would simply:

$database = $dic->resolve('database');

That's the gist of it. A somewhat more sophisticated but still relatively simple and easy to grasp PHP DI/IoC container is Pimple. Check its documentation for more examples.


Regarding OP's code and questions:

  • Don't use static class or a singleton for your container (or for anything else for that matter); they're both evil. Check out Pimple instead.
  • Decide whether you want your mysqliWrapper class extend mysql or depend on it.
  • By calling IoC from within mysqliWrapper you're swapping one dependency for another. Your objects shouldn't be aware of or use the container; otherwise it's not DIC anymore it's Service Locator (anti)pattern.
  • You don't need to require a class file before registering it in the container since you don't know if you're going to use an object of that class at all. Do all your container setup in one place. If you don't use an autoloader, you can require inside the anonymous function you register with the container.

Additional resources:

참고URL : https://stackoverflow.com/questions/18562752/understanding-ioc-containers-and-dependency-injection

반응형