PHP Control Inversion (IOC) and Dependency Injection (DI)

Keywords: PHP

Let's start with an example:

<?php

class A
{
	public $b;
	public $c;
	public function A()
	{
		//TODO
	}
	public function Method()
	{
		$this->b=new B();
		$this->c=new C();
		
		$this->b->Method();
		$this->c->Method();
		
		//TODO
	} 
}

class B
{
	public function B()
	{
		//TODO
	}
	public function Method()
	{
		//TODO
		echo 'b';
	}
}

class C
{
	public function C()
	{
		//TODO
	}
	public function Method()
	{
		//TODO
		echo 'c';
	}
}

$a=new A();
$a->Method();

?>

  

The above code, we can easily understand a sentence:

Class A depends on Class B and Class C

That is to say, if we want to modify class B or C in the future development process, once it involves function renaming, the number of function parameters changing, and even the adjustment of the structure of the whole class, we should also make corresponding adjustments to class A. The independence of class A is lost, which is very inconvenient in the development process, that is, what we call "pulling the whole body together", if the two classes are two people. Written separately, contradictions often arise at this time...

If we really want to change Class B and Class C, is there any way to change the code of Class A as little as possible without changing it? Control inversion is used here.

High-level modules should not rely on low-level modules, and both should rely on abstraction.

Inversion of Control (IOC) is an idea, and Dependency Injection (DI) is a method to implement it.

The first method is called constructor injection (which is not recommended, but is better than not).

class A
{
	public $b;
	public $c;
	public function A($b,$c)
	{
		$this->b=$b;
		$this->c=$c;
	}
	public function Method()
	{
		$this->b->Method();
		$this->c->Method();
	} 
}

The client class writes as follows:

$a=new A(new B(),new C());
$a->Method();

The constructor of Class A relies on Class B and Class C, and passes in the parameters of the constructor. At least one thing is that the creation of Class B and Class C objects has been moved out of Class A. So once class B and Class C are changed, Class A need not be changed, as long as it is changed in the client class.

If one day, we need to expand class B and make two subclasses of class B.

class B
{
	public function B()
	{
		//TODO
	}
	public function Method()
	{
		//TODO
		echo 'b';
	}
}
class B1 extends B
{
	public function B1()
	{
		//TODO
	}
	public function Method()
	{
		echo 'b1';
	}
}
class B2 extends B
{
	public function B2()
	{
		//TODO
	}
	public function Method()
	{
		echo 'b2';
	}
}

It's also very simple. The client class writes as follows:

$a=new A(new B2(),new C());
$a->Method();

So Class A does not need to care about which subclasses of Class B are, as long as it cares about the client class.

The second method is called factory mode injection (recommended).

class Factory
{
	public function Factory()
	{
		//TODO
	}
	public function create($s)
	{
		switch($s)
		{
			case 'B':
			{
				return new B();
				break;
			}
			case 'C':
			{
				return new C();
				break;
			}
			default:
			{
				return null;
				break;
			}
		}
	}
}

Our class A code is changed to:

class A
{
	public $b;
	public $c;
	public function A()
	{
		//TODO
	}
	public function Method()
	{
		$f=new Factory();
		$this->b=$f->create('B');
		$this->c=$f->create('C');
		
		$this->b->Method();
		$this->c->Method();
		
		//TODO
	} 
}

In fact, it has decoupled a small part, at least if the constructors of Class B and Class C change, such as modifying function parameters, we just need to change the Factory class.

Abstraction should not depend on details. Details should depend on abstraction.

To abstract the methods in Class B and Class C and make an interface

interface IMethod
{
	public function Method();
}

In this way, the $b variable and the $c variable in class A are no longer a concrete variable, but an abstract type of variable. Until the moment they run, they do not know how their Method is implemented.

class B implements IMethod
{
	public function B()
	{
		//TODO
	}
	public function Method()
	{
		//TODO
		echo 'b';
	}
}

class C implements IMethod
{
	public function C()
	{
		//TODO
	}
	public function Method()
	{
		//TODO
		echo 'c';
	}
}

Summarize a few points:

1. We move the creation of class B objects and class C objects in class A outside of class A.

2. Class A originally relies on Class B and Class C, but now it becomes Class A relies on Factory, Factory relies on Class B and C.

Posted by webent on Tue, 16 Apr 2019 23:03:33 -0700