当前位置:网站首页>Programming philosophy - automatic loading, dependency injection and control inversion

Programming philosophy - automatic loading, dependency injection and control inversion

2022-04-23 14:41:00 The clouds are idle

Knowledge precursor

You need to know java import、package The substance or php Of namespace and use The essence . They are not file paths , Just to avoid class attribute conflicts . Documents need to be passed javapath perhaps php Of require Only when introduced can .

Class autoload

Problem introduction

When we write code , It is often necessary to import the corresponding class file first , In this way, it is troublesome when we frequently introduce other files and use their methods . So , We hope , When we instantiate a class , The code can help us automatically import the corresponding files

Realization

// Goods.php
<?php
class Goods
{
    
    public function getGoods()
    {
    
        return 'phone';
    }
}

stay PHP Development process , If you want to introduce a class, You usually use include and require Method , To define this class The file contains , However, this may make the new script that references the file , There is a lot of include or require Method call , If you omit for a moment, you will make mistakes , Makes the code difficult to maintain .
since PHP5 after , Introduced autoload This interceptor method

// test_autoload.php
function __autoload($class_name = "") {
    
    echo 'class:' . $class_name . PHP_EOL;
    include "./{
      $class_name}.php";
}

$goods = new Goods();
echo $goods->getGoods();

such We don't need to import files manually anymore .

And now a lot of IDE Very smart , such as PHPSTORM When we knock $a=new TestMgr(); when ,ide Will automatically help us write at the top of the file use app\api\manager\TestMgr;

use app\api\manager\TestMgr;//ide Automatically help us add 

class Test
{
    
    public function index()
    {
    
        $a=new TestMgr();
    }
}

Dependency injection

Problem introduction

When we use automatic loading , Writing code is very convenient . But then we found out , If our programming technology grows , One day, I suddenly felt that the previous code was very messy , Want to refactor the code , For example, log in the original scattered wechat 、 Wechat sends service number message 、 Wechat applet login and other wechat official api Call methods are grouped together to form a single class WeChatService, At this time, you will find that you have a headache , Because you have to take the previous one by one use xxx And countless in the code new xxx() Change it .

Scene Introduction

Chapter one : Xiao Ming and his cell phone

Once upon a time, there was a man named Xiao Ming
Xiao Ming has three hobbies , smoking , Drink …… Cough , sorry , Wrong set . It should be a stroll 、 Play king pesticide and grab wechat red envelopes
We use a simple piece of pseudo code , To make such a Xiaoming

class Ming extends Person
{
    
    private $_name;

    private $_age;

    function read()
    {
    
        // Go to Zhihu 
    }

    function  play()
    {
    
        // Play with pesticides 
    }

    function  grab()
    {
    
        // Grab a red envelope 
    }

}

however , Xiao Ming as a human , There is no way to achieve the above functions by yourself , He has to rely on a cell phone , So he bought one iphone6, Next, let's make a iphone6

class iPhone6 extends Iphone
{
    
    function read($user=" Sb. ")
    {
    
        echo $user." Opened Zhihu and made up a story  \n";
    }

    function play($user=" Sb. ")
    {
    
        echo $user." Opened the king pesticide and sent his head  \n";
    }

    function grab($user=" Sb. ")
    {
    
        echo $user." I started to grab red envelopes, but I just couldn't send them  \n";
    }
}

Xiao Ming cherishes his new mobile phone very much , Keep it firmly in the palm of your hand every day , So Xiao Ming became like this

class Ming extends Person
{
    
    private $_name;

    private $_age;

    public function  __construct()
    {
    
        $this->_name = ' Xiao Ming ';
        $this->_age = 26;
    }

    function read()
    {
    
        //……  Omit some codes 
        (new iPhone6())->read($this->_name); // Go to Zhihu 
    }

    function  play()
    {
    
        //……  Omit some codes 
        (new iPhone6())->play($this->_name);// Play with pesticides 

    }

    function  grab()
    {
    
        //……  Omit some codes 
        (new iPhone6())->grab($this->_name);// Grab a red envelope 

    }

}

Today is Saturday , Xiao Ming doesn't have to go to work , So he got up , And strolled around in turn , Play king pesticide , And robbed a red envelope .

$ming = new Ming();  // Xiao Ming gets up 
$ming->read();
$ming->play();
$ming->grab();

This is the time , We can see the output on the command line as follows

 Xiao Ming opened Zhihu and made up a story  
 Xiao Ming opened the king pesticide and sent his head  
 Xiao Ming started to grab red envelopes, but he just couldn't get them 

This day , Xiao Ming has a full life , He thinks he is the happiest person in the world .

Chapter two : Xiao Ming's happiness and sadness

Xiao Ming and his mobile phone had a good time together , When it comes to free time , He just held his cell phone , Go to Zhihu , Brush micro-blog , Play a game , He felt he didn't need a girlfriend at all , As long as there is a mobile phone around , It's enough .

But who would have thought , Time after time, the system update completely broke his dream , His cell phone is getting more and more stuck , The service life of batteries is getting shorter and shorter , Until one day in the cold wind , His cell phone finally couldn't stand the cold , Turn off the machine without looking back .

Xiao Ming is very sad , He realized , I'm going to change my cell phone .

In order to get a better use experience , Xiao Ming bit his teeth , Cut off one iphoneX, This cell phone rings very loud , It's full of electricity , Can also double card double stay , Xiao Ming likes , But he had a problem , It's that he relied too much on the original one before iPhone6, They are deeply coupled , If you want to change your cell phone , He will pick up a knife to transform himself , Of all the methods in your body iphone6 All for iphoneX.
 Insert picture description here

The long transformation process

After a long transformation process , Xiao Ming finally put iphone6 All replaced with iphoneX. Although it's hard , But Xiao Ming thinks he is happy .

So Xiaoming went to work happily with his mobile phone , And was stolen by thieves on the way back . In case of emergency , Xiao Ming had to reuse the newly abandoned iphone6, But at the thought of the long transformation process , Xiao Ming has unspeakable grievances in his heart , He felt too dependent on his cell phone , Why does he have to transform himself every time something goes wrong with his mobile phone , It's not just over coupling , Putting the cart before the horse , He shouted to the sky , I don't want to control my cell phone anymore .

The creator in the sky , That is, as a programmer, i , Heard his cry , I told him , You don't have to control your cell phone anymore , Leave it to me to manage , Give me control . This is called control reversal .

The third chapter : The wisdom of the Creator

Xiao Ming heard me , He is happy , A little scared , He knelt down and hit his head , Speak piously :“ So you are the legendary Creator , God bagmec . I heard you just say Inversion of control Four words , Is to hand over the control of the mobile phone from me to you , But that's just your idea , It's just an idea , What method can be used to realize control reversal , Let me continue to use my mobile phone ?”

“ o “, As the creator, after showing disdain , Dropped four big words ,“ Dependency injection !”

Next , The great I began to transform Xiao Ming inhumanely , as follows

class Ming extends Person
{
    
    private $_name;
    private $_age;

    private $_phone; // Take the mobile phone as your member variable 

    public function  __construct($phone)
    {
    
        $this->_name = ' Xiao Ming ';
        $this->_age = 26;
        $this->_phone = $phone;
        echo " Xiao Ming gets up  \n";
    }

    function read()
    {
    
        //……  Omit some codes 
        $this->_phone->read($this->_name); // Go to Zhihu 
    }

    function  play()
    {
    
        //……  Omit some codes 
        $this->_phone->play($this->_name);// Play with pesticides 

    }

    function  grab()
    {
    
        //……  Omit some codes 
        $this->_phone->grab($this->_name);// Grab a red envelope 

    }

}

Next , Let's simulate the day when Xiao Ming runs

$phone = new IphoneX(); // Create a iphoneX Example 
if($phone->isBroken()){
    // If iphone Unavailable , Then use the old mobile phone 
    $phone = new Iphone6();
}
$ming = new Ming($phone);// Xiao Ming doesn't care what kind of mobile phone it is , He just has to play .
$ming->read();
$ming->play();
$ming->grab();

Let's take a look first iphoneX Whether it can be used , If not available , Then directly replace with iphone6, Then wake up Xiao Ming , And put the phone in his hand , let me put it another way , Inject the cell phone he relies on directly into him , He doesn't need to care what cell phone he's holding , He just needs to use it directly .

This is dependency injection .

Chapter four : Xiao Ming's feeling

Xiao Ming's life began to become simple , And he used all the time he saved to write notes , He wrote in his notebook

I used to have a strong desire for control , Over reliance on my phone , It leads to too high coupling between me and my mobile phone , As long as the phone has a little problem , I have to transform myself , This is a waste of time and easy to go wrong . Since I gave control to the Creator , Every day before he wakes me up , I've already selected my cell phone , I just have to play with my cell phone as usual , Don't care what mobile phone it is . Even if there's something wrong with the phone , It can also be done directly by the Creator , I don't need to change myself anymore , I have bought seven mobile phones now , To the Creator , Change one every day , A fun !
I also got this feeling from it : If a class A The implementation of the function needs the help of class B, So we call it a class B It's a class A Dependence , If in class A To instantiate a class inside of B, Then there will be a high coupling between the two , Once the class B There is a problem , class A It also needs to be transformed , If it's more like this , There are a lot of dependencies between each class , Then there will be a situation of pulling one hair and moving the whole body , Programs can be extremely difficult to maintain , And it's easy to have problems . To solve this problem , Will the A Class to B The control of class is pulled out , Give it to a third party , Turn control over to a third party , It's called reverse control (IOC Inversion Of Control). Inversion of control is an idea , It's a possible result of being able to solve the problem , And dependency injection (Dependency Injection) Is the most typical implementation method . By a third party ( We call it IOC Containers ) To control dependency , Get him through the constructor 、 Property or factory mode , Inject into class A Inside , In this way, the class can be classified to a great extent A And the class B To understand the coupling .

The fifth chapter Xiao Ming's confusion

one day , When Xiao Ming finds himself trying to read Zhihu , Read such a line .

To be continued …

The above is quoted from https://zhuanlan.zhihu.com/p/33492169

notes : The above scenario only explains why dependency injection , Instead of talking a lot about dependency inversion . We can find out The above example There are still problems 1 We need to do it manually new A specific object We want to be lazier Don't write it yourself 2 Yes, if we have many nested classes Not to new Many classes

Inversion of control

On the definition of concepts

When we have more than one class Probably A B C D Probably A rely on B C ,C And depend on D class , This makes the relationship very complicated , So dependency inversion produces
 Insert picture description here
Dependency inversion :
 Insert picture description here

Application example of control inversion (java Realization It's very good. !)

To understand these concepts , Let's still use the example of the car above . It's just code this time . Let's first define four Class, vehicle , body , chassis , tire . Then initialize the car , Last run this car . The code structure is as follows :
 Insert picture description here
such , It's equivalent to the first example above , The superstructure depends on the substructure —— The constructor of each class directly calls the constructor of the underlying code . Suppose we need to change the tire (Tire) class , Make its size dynamic , Not all the time 30. We need to change :
 Insert picture description here
Because we changed the definition of tire , In order to make the whole program run normally , We need to make the following changes :
 Insert picture description here
So we can see , Just to modify the constructor of the tire , This design needs to modify the constructors of all classes in the upper layer !
The above example can also be changed to the case of ordinary functions , The underlying database layer needs to add one more parameter , For example, active time , So start at the top , Everyone who calls this function All have to be changed !
In software engineering , Such a design is almost non maintainable —— In actual engineering projects , Some classes may be the bottom of thousands of classes , If you modify this class every time , We have to modify all classes that depend on it , The maintenance cost of the software is too high .
So we need to reverse control (IoC), And the upper layer controls the lower layer , Instead of the lower layer controlling the upper layer . We use dependency injection (Dependency Injection) This way to achieve control reversal . So called dependency injection , Is to pass the lower class as a parameter into the upper class , Realize the of the upper class to the lower class “ control ”. Here we rewrite the definition of car class by using the dependency injection passed by the construction method :
 Insert picture description here
Here we turn the tire size into dynamic , Also, in order to make the whole system run smoothly , We need to make the following changes :
 Insert picture description here
You see ? Here I just need to modify the tire class , No need to modify any other upper class . This is obviously easier to maintain code . More Than This , In practical engineering , This design pattern is also conducive to the cooperation of different groups and unit testing : For example, four different groups develop these four classes , So as long as the interface is defined , Four different groups can be developed simultaneously without mutual restrictions ; And for unit testing , If we want to write Car Unit test of class , Just need Mock once Framework Class in Car That's it , Instead of putting Framework, Bottom, Tire All new Do it again Car.

Here, we use the constructor passed in for dependency injection . There are actually two other ways :Setter Delivery and interface delivery . There's not much to talk about here , The core idea is the same , All to achieve control reversal .
 Insert picture description here
Seeing this, you should be able to understand what control inversion and dependency injection . So what is the control reversal container (IoC Container) Well ? In fact, in the above example , Where the code for initializing the vehicle class occurs , Is to control the inversion container .
Obviously you should have observed , Because of dependency injection ,** In the process of initialization, it is inevitable to write a large number of new. And there is only one parameter we really care about , That's it new Car(40) Everything else is written in a fixed way ** here IoC The container solves this problem . This container can automatically initialize your code , You only need to maintain one Configuration( It can be xml It can be a piece of code ), Instead of having to write a large section of initialization code every time you initialize a car . This is the introduction of IoC Container The first benefit of .

IoC Container The second advantage of is : We don't need to know the details when creating an instance . In the example above , We create a car by hand instance When , From the bottom to the top new Of :
 Insert picture description here
In the process , We need to understand the whole Car/Framework/Bottom/Tire How class constructors are defined , To step by step new/ Inject .

and IoC Container When doing this work, it is the opposite , It starts at the top and looks down for dependencies , After reaching the bottom, go up one step new( Kind of like depth first traversal ):
 Insert picture description here
 Insert picture description here
We are like factory customers . We just need to ask the factory for one Car example , Then it gives us according to Config Created a Car example . We don't care about this at all Car How instances are created step by step .

In actual projects , yes , we have Service Class Maybe it was written ten years ago , There are hundreds of classes as its bottom layer . Suppose we write a new API You need to instantiate this Service, We can't go back and find out the constructors of these hundreds of classes ?IoC Container This feature perfectly solves this kind of problem —— Because this architecture requires you to write class You need to write the corresponding Config file , So you have to initialize a long time ago Service Class time , Predecessors have already written Config file , You inject this directly where you need it Service That's all right. . This greatly increases the maintainability of the project and reduces the development difficulty .
Spring Dependency injection in

We mentioned above , Dependency injection is a way to achieve inversion of control . Let's combine Spring Of IoC Containers , Describe the process briefly .

class MovieLister...
    private MovieFinder finder;
    public void setFinder(MovieFinder finder) {
    
        this.finder = finder;
    }
class ColonMovieFinder...
    public void setFilename(String filename) {
    
        this.filename = filename;
    }

Let's first define two classes , It can be seen that dependency injection is used , Pass in dependencies through the outside , Instead of creating your own dependencies . So here comes the question , Who passes on dependence to them , That is, who is responsible for creating finder, And the finder Pass to MovieLister. The answer is Spring Of IoC Containers . To use IoC Containers , The first step is to configure . Here we use xml Configuration of , It can also be configured through code annotation . Here is spring.xml The content of

<beans>
    <bean id="MovieLister" class="spring.MovieLister">
        <property name="finder">
            <ref local="MovieFinder"/>
        </property>
    </bean>
    <bean id="MovieFinder" class="spring.ColonMovieFinder">
        <property name="filename">
            <value>movies1.txt</value>
        </property>
    </bean>
</beans>

stay Spring in , Every bean Represents an instance of an object , The default is singleton mode , That is, in the life cycle of the program , All objects have only one instance , For reuse . By configuring bean,IoC When the container is started, it will generate according to the configuration bean example . Specific configuration syntax reference Spring file . All I need to know is IoC The container will be created according to the configuration MovieFinder, When running, put MovieFinder Assign a value to MovieLister Of finder attribute , Complete the dependency injection process . Here is the test code

public void testWithSpring() throws Exception {
    
    ApplicationContext ctx = new FileSystemXmlApplicationContext("spring.xml");//1
    MovieLister lister = (MovieLister) ctx.getBean("MovieLister");//2
    Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
    assertEquals("Once Upon a Time in the West", movies[0].getTitle());
}

Generate... Based on configuration ApplicationContext, namely IoC Containers .
Get from container MovieLister Example .
summary

Inversion of control is an idea of decoupling in software engineering , Calling class only depends on interface , Instead of depending on a specific implementation class , Reduced coupling . Control is given to the container , It is only at runtime that the container decides to implement the specific dynamic “ Inject ” To the object of the calling class .
Dependency injection is a design pattern , It can be used as an implementation of control inversion . Dependency injection is to pass instance variables into an object (Dependency injection means giving an object its instance variables).
adopt IoC frame , class A Dependent class B Strong coupling can be established at runtime through containers , That is to say, create B The work of the instance is handed over to the container , class A Just use it .

Above from https://zhuanlan.zhihu.com/p/77415657 The original author is no longer available

Simple summary ( Dependency injection and inversion of control )

In fact, this kind of interface is very much like ! In strongly typed languages , Parameters must be declared , And this class can't be an ordinary class So it becomes an interface . __construt(Phone $phone) ,iphone6 and iphonex Are its implementation classes

Most object-oriented programming languages , When calling a class , First instantiate this class , Generate an object . If you're writing a class , Many other classes are called in the procedure , Even the other classes here , Also want to “ rely on ” For more classes , So imagine , How many instantiations do you have to do . This is it. “ rely on ” It means . Dependency injection , The full name is “ Dependency injection into the container ”, Containers (IOC Containers ) It's a design pattern , It's also an object , You put a class ( No matter how much dependency there is ) Put it in this container , Sure “ analysis ” Give an example of this class . So dependency injection is to put the dependent classes into the container (IOC Containers ) in , Then parse out an instance of this class . That's it .

Originally, I took various parameters to construct an object , Now only one parameter is accepted —— Objects that have already been instantiated .
That is to say, I have a deep understanding of the object 『 Dependency is injected 』, And it's decoupled from the way it's constructed . Construct and destroy these 『 control 』 The operation was also handed over to a third party , That is to say 『 Inversion of control 』.

The code now just implements the logic ,new The task of the object is entrusted to IoC Container to do .

 Insert picture description here

Realization –PHP

Implementation of dependency injection

Dependency injection (DI) Although the concept of "I love you" sounds profound , But if you've used something new php Frame words , about DI It must be no stranger , Because they all use dependency injection to deal with the dependency relationship between classes .

php There are three ways to transfer dependencies in
In fact, to understand DI, The first thing to understand is php How to pass dependencies in .

First option , It's also the worst option , Is in the A Class new Key words to create a B class , As shown in the following code :

<?php
class A
{
    
  public function __construct()
  {
    
    $b = new B();//a To rely on b Methods   Then create one manually B  In fact, it is the first way of writing by Xiao Ming 
    // It's written here in the constructor   Is to unify it into one place   You don't have to write everywhere 
  }
}

Why is this kind of plan not desirable ? Because in that case ,A And B It's coupled together , in other words A Class cannot be detached from B Class work .

The second solution is to A Class B class , As shown in the following code :

class A
{
    
  public function __construct(B $b)
  {
    
  		$this->b = $b;
  		// The advantage is that  1 A Classes don't have to be like B Classes bound together 
  		//2  The dependency is manually passed in outside the function B Example 
  		//3  Is to unify it into one place   You don't have to write everywhere 
  }
}

This method is better than the first one ,A Class does not have to be associated with B Classes are bundled together , As long as the incoming class satisfies A Class requirements , It can also be C class , It can also be D Class, etc. .
But the disadvantage of this scheme is If A Class depends on more classes , The parameter list will be long , It's prone to chaos .

The third solution is to use set Methods the incoming , As shown in the following code :
This method is better than the first one ,A Class does not have to be associated with B Classes are bundled together , As long as the incoming class satisfies A Class requirements , It can also be C class , It can also be D Class, etc. . As shown in the following code :

class A
{
    
  public function setB(B $b)
  {
    
    $this->b = $b;
  }
}

This scheme has the same disadvantages as the second one , When the number of dependent classes increases , We need a lot, a lot of set Method .

ps Here is the increase in breadth and java Where? Is the increase in depth

The realization of inversion of control —— A special class , Or a container to manage

At this time, we are thinking that if there is a special class ( Or a container ) Just help us manage these dependencies .

Unified management dependency injection of control reversal

A simple example of dependency injection
The following code comes from twittee:

// Container Class is external   A unified and centralized place to manage these dependencies 
class Container {
    
 private $s=array();// Be careful   The form of array is adopted here   You can avoid long parameter lists 
 function __set($k, $c) {
     
 	$this->s[$k]=$c;  //k Is to give the class a new name  
 }
 function __get($k) {
     //S This is the current name of this class ( Be careful   It's not just the name   Also instantiated )
 	return $this->s[$k]($this); 
 }
}

With container How can we manage after class A And B The dependence between , Speak in code :

<?php
class A
{
    
  private $container;
  // It only needs container A parameter 
  public function __construct(Container $container)
  {
    
    $this->container = $container;
  }
  public function doSomeThing()
  {
    
    //do something which needs class B
    $b = $this->container->getB();
    //to do
  }
}

then B Class into the container class :

$c = new Container();
$c->setB(new B());

Control the of inversion ---- Control instantiation

You can also pass in an anonymous function , such B Class will not be instantiated immediately when it is passed in , It is The instantiation is completed only when it is actually called


$c = new Container();
$c->setB(function (){
    
  return new B();
});

The implementation code comes from https://zhuanlan.zhihu.com/p/365923544

It's not as good as java It's good there But there is a need to instantiate And the idea of array management Well written

One sentence summary

Dependency injection is ,A Dependent class B before A own newB Now let the caller new Okay B An instance of is passed directly to A. There are two benefits :1 Good modification 2 It's equivalent to your new name for class life

Inversion of control is because A Rely on more than one B class , If we have to call it ourselves We need to check all dependencies ourselves , so much trouble . therefore , Let the caller new Okay A Need a series of examples of this work , Give it to a special class for centralized management , This is inversion of control ( Defined when writing the people of the underlying class )

tp6 Automatic binding and automatic instantiation mean

<?php
class A
{
    
  public function __construct(B $b)
  {
    
  		$this->b = $b;
  }
}

When we take this form , To use A class , You have to manually new One B class , Like before

$phone = new IphoneX(); // Create a iphoneX Example 
$ming = new Ming($phone);// Xiao Ming doesn't care what kind of mobile phone it is , He just has to play .

however , We want to be more concise , as long as new Ming() Just fine . therefore $phone= new IphoneX This is called auto instantiation , And we just want to new Ming() hold Phone Get in automatically It's called automatic binding ?

When to use

When we write classes ( We call it the middle class ) Methods of other classes are used , And our middle class itself may also be called by the upper class
The first way is , Let's go by hand new Lower class , And this will cause the above problems .
The second way is , Change the parameters of the lower object into its own class , then new Give it to the upper function ( Don't think about containers first ).
The second method is preferred .new Just find a framework with containers to do the work

The underlying database layer needs to add one more parameter , For example, active time , So start at the top , Everyone who calls this function All have to be changed ! At this time, we must consider dependency injection !

How to reverse control — By reflection

In short Is through reflection Instantiate layer by layer

PHP With complete reflection API, Provides a pair of classes 、 Interface 、 function 、 Methods and extend the ability of Reverse Engineering . Through the ability provided by class reflection, we can know how classes are defined , What properties does it have 、 What method 、 What parameters do methods have , What is the path of the class file and other important information . It's also official because there are a lot of reflections on classes PHP The framework can realize dependency injection and automatically solve the dependency relationship between classes , This brings great convenience to our usual development . This article mainly explains how to use class reflection to realize dependency injection (Dependency Injection), I'm not going to talk about it one by one PHP Reflection Each of them API, The details of the API For reference information, please refer to the official documents

Just to understand , Let's look at the reflection of a class through an example , And how to implement dependency injection .
The following class represents a point in the coordinate system , There are two attributes abscissa x And the vertical y.

/** * Class Point */
class Point
{
    
  public $x;
  public $y;
 
  /** * Point constructor. * @param int $x horizontal value of point's coordinate * @param int $y vertical value of point's coordinate */
  public function __construct($x = 0, $y = 0)
  {
    
    $this->x = $x;
    $this->y = $y;
  }
}

Next, this class represents a circle , You can see that one parameter in its constructor is Point Class , namely Circle Classes are dependencies and Point Class .

class Circle
{
    
  /** * @var int */
  public $radius;// radius 
 
  /** * @var Point */
  public $center;// Center point 
 
  const PI = 3.14;
 
  public function __construct(Point $point, $radius = 1)
  {
    
    $this->center = $point;
    $this->radius = $radius;
  }
   
  // Print the coordinates of the dot 
  public function printCenter()
  {
    
    printf('center coordinate is (%d, %d)', $this->center->x, $this->center->y);
  }
 
  // Calculate the area of the circle 
  public function area()
  {
    
    return 3.14 * pow($this->radius, 2);
  }
}

ReflectionClass

Now let's reflect on Circle Reverse engineer this class .
hold Circle The name of the class is passed to reflectionClass To instantiate a ReflectionClass Class object .

$reflectionClass = new reflectionClass(Circle::class);
// The return value is as follows 
object(ReflectionClass)#1 (1) {
    
 ["name"]=>
 string(6) "Circle"
}

Reflect the constant of the class

$reflectionClass->getConstants();

Returns an associative array of constant names and values

array(1) {
    
 ["PI"]=>
 float(3.14)
}

Get attributes by reflection

$reflectionClass->getProperties();

Returns a ReflectionProperty An array of objects

array(2) {
    
 [0]=>
 object(ReflectionProperty)#2 (2) {
    
  ["name"]=>
  string(6) "radius"
  ["class"]=>
  string(6) "Circle"
 }
 [1]=>
 object(ReflectionProperty)#3 (2) {
    
  ["name"]=>
  string(6) "center"
  ["class"]=>
  string(6) "Circle"
 }
}

Reflect the methods defined in the class

$reflectionClass->getMethods();

return ReflectionMethod An array of objects

array(3) {
    
 [0]=>
 object(ReflectionMethod)#2 (2) {
    
  ["name"]=>
  string(11) "__construct"
  ["class"]=>
  string(6) "Circle"
 }
 [1]=>
 object(ReflectionMethod)#3 (2) {
    
  ["name"]=>
  string(11) "printCenter"
  ["class"]=>
  string(6) "Circle"
 }
 [2]=>
 object(ReflectionMethod)#4 (2) {
    
  ["name"]=>
  string(4) "area"
  ["class"]=>
  string(6) "Circle"
 }
}

We can also go through getConstructor() To get the constructor of the class separately , The return value is a ReflectionMethod object .

$constructor = $reflectionClass->getConstructor();

Reflect the parameters of the method

$parameters = $constructor->getParameters();

Its return value is ReflectionParameter An array of objects .

array(2) {
    
 [0]=>
 object(ReflectionParameter)#3 (1) {
    
  ["name"]=>
  string(5) "point"
 }
 [1]=>
 object(ReflectionParameter)#4 (1) {
    
  ["name"]=>
  string(6) "radius"
 }
}

Dependency injection

Well, let's write a program called make Function of , Pass the class name to make Function returns the object of the class , stay make It will help us inject class dependencies , That is, in this case, help us inject Point The object is to Circle Construction method of class .

// Build the object of the class 
function make($className)
{
    
  $reflectionClass = new ReflectionClass($className);
  $constructor = $reflectionClass->getConstructor();
  $parameters = $constructor->getParameters();
  $dependencies = getDependencies($parameters);
   
  return $reflectionClass->newInstanceArgs($dependencies);
}
 
// Dependency resolution 
function getDependencies($parameters)
{
    
  $dependencies = [];
  foreach($parameters as $parameter) {
    
    $dependency = $parameter->getClass();
    if (is_null($dependency)) {
    
      if($parameter->isDefaultValueAvailable()) {
    
        $dependencies[] = $parameter->getDefaultValue();
      } else {
    
        // It is not an optional parameter. For simple and direct assignment, it is a string 0
        // In the case of the necessary parameters of the construction method 
        //laravel It's through service provider register closure To IocContainer,
        // stay closure through return new Class($param1, $param2) To return an instance of the class 
        // And then in make Call back this closure You can parse out the object 
        // I will describe the details in another article 
        $dependencies[] = '0';
      }
    } else {
    
      // Recursively resolve the objects of dependent classes 
      $dependencies[] = make($parameter->getClass()->name);
    }
  }
 
  return $dependencies;
}

Well defined make Method, we use it to help us instantiate Circle Class object :

$circle = make('Circle');
$area = $circle->area();
/*var_dump($circle, $area); object(Circle)#6 (2) { ["radius"]=> int(1) ["center"]=> object(Point)#11 (2) { ["x"]=> int(0) ["y"]=> int(0) } } float(3.14)*/

If through the normal call process

new point
new circle
 And then call the method  

Through the above example, I briefly described how to use PHP Class to implement dependency injection ,Laravel Dependency injection is also realized through this idea , But the design is more precise and makes a lot of use of closure callbacks to deal with all kinds of complex dependency injection .

source :https://www.jb51.net/article/134530.htm

Problem correlation

thinkphp How to automatically import files

Other questions

It seems to be done automatically You can't customize the parameters ? For example, if you want to modify the distance between two points, what's the matter .
I guess because web Development General classes are only used to call methods Not like ordinary java like that set get such as new(“ Zhang San ”,“ Chinese ”) So you can instantiate No need to pass parameters .
 Insert picture description here
such as CI Framework of the load Method The essence is to instantiate an object . however If you want to add a layer service, It's also easy for you to rewrite loader class , For example, let it also search service Go to the catalog . however In this way, the precise positioning is lost It becomes a search file ! Especially layered more than one That's not emmm. Of course, there are other ways such as this->load->service. But this way , If there are more layers That doesn't have to define many . Or You can automatically match directories by suffix ( This seems quite good ? You can also write a directory reflection What suffix corresponds to what Directory )-》 But this will lead to the problem of dependency injection For example, due to the service number and applet push message You want to switch to the official account in part. ( Complex implementation logic Not just a function To write a class ) At this time, go and change ---- forehead I have no idea

Factory pattern and dependency injection

Scene Introduction

First step : Why can't we new object ?

Whenever and wherever possible new Objects can cause a problem , You are the one new Instances of will fly all over the sky . In this way, you can expand each entity , modify , When you give up, you need to find someone all over the world , One by one .
for example :
collection map=new HashMap();
One day the boss told you HashMap Not very good , All changed to the new version XXXXMap Well ~~~ Do you need to change them all together ?
So at this time, we need to manage objects through factory mode . The code is as follows :
collection map=MapFactory.createMap();
thus , We always want to replace HashMap It's very simple .

The second step : Why don't we use the factory model , Instead, we should specialize in the dependency injection framework .
When you only need one Map The object is very simple . That is true ~~~ But the object you need to create is a multi-level object ? So here's the problem .
for example , I want a car , Then you need tires , I need a tire , Then you need a hub , Several screws , tyre , Inner tube , brake ……
So here's the problem . What I should do is plant rubber trees when I build cars ?
This is not right .
What we should do is , When the car was built , It was found that there was a complete production of tires , Just call the tire and install it . As for what you need in the tire , I'm not doing it again , Instead, it calls something ready-made .
This is dependency injection .
What he stands for is : I didn't have a car before I went to make tires , It's about having tires before you start making cars .
This division also represents a way of thinking , Decouple all your objects from thinking and realization . In this way, my object is truly independent .
So solve a puzzle you must have encountered :XML It's just a very tiring implementation of dependency injection , Dependency injection actually has other implementations , For example, the way of annotation .

The third step , Dependency injection is a good idea , But where are the advantages ? Where is the decoupling ?
Continue to use the above example of car making .
Car making , Call the wheel , Call the outer tire , Call rubber .
I just built a car , Now we need to change the wheel to another wheel .
thus , The advantages of dependency injection are reflected , Because you only need to replace the wheel object that calls the wheel .
Three steps down , The advantage of dependency injection comes out . Especially when working as a team , He can completely replace the wheel object without understanding the details of making the wheel .

Now you know what dependency injection is , stay spring In the actual combat , The author also strongly recommends using annotation to operate spring.

Simply speaking

such as map The implementation of the , You may have multiple functions of a class in your code 、 Use... In multiple classes , But it may often change . So you decide to use the factory model . But you don't know what you might need factory patterns to create , Cannot define in advance . If it's all defined in advance , How much definition code does it have to write , And in case of nesting, you have to code a set of code .
For example, the constructor is abbreviated as truck ( wheel ) wheel ( Tire ) Tire ( rubber ) rubber ( Rubber tree ) If I suddenly want to change a tire one day Then write a new tire B class Tire B( rubber ) And wheels ( Tire B) that will do Just change two places 1 It's a tire B New definition of ( Constructor parameters remain unchanged ) 2 It's a wheel ( Tire B) Replace dependency with B Just change the constructor class name
And if we use new In the form of , If there is only one call chain, it's ok . We will only call the highest level new truck . But there may be a variety of trucks , There are many kinds of wheels Now you change the tire B Then you have to modify all wheel classes in turn To change their NEW wheel . But with dependency injection So you only use
such as The interface layer Service layer The model layer It turns out that you will get accesstoken Function of ( abbreviation g) Put it in the login svc in . This function is not only called by this service layer Maybe other services 、 Other interfaces This service class is also called directly . So one day You feel bloated This function should be done separately manger layer , It's called wechat basic service layer , Then put this function in , And add parameters , such as ?. So here comes the question , You have to change a lot , You cannot log in to the controller directly this->g Called , therefore this->g All have to be modified . The rest can't be this-》load> Sign in svc , then this-》 Sign in svc-》g Called . The latter is easy to change Sure ide One click replacement But the front load What about Other login functions may also be used to You can't get rid of every . And if you rely on injected thinking from the beginning , Then it's easy to change ,??? I can't write any more

Dependency injection is convenient for a single class Multiple functions Or a vertical chain of references to a single class Go there or something It is not convenient for multiple classes to introduce the same class Then it's convenient to modify
therefore Factory pattern and dependency injection can be combined

then The tire modification B Of Because this is a chain call Tire A Other business lines need to use it So you can't directly modify the wheel Change it into a tire B . This scenario does not rely on injection It should be the factory model . Dependency injection is a scenario for unified business to modify

Oh I see All wrong
Dependency injection is to give these things to loc Automatic processing . The factory mode is manual Especially when creating an instance with nested relationship, it is very troublesome .
The third step above is the common benefits of dependency injection and factories Instead of relying on the benefits of injecting a So I got the logic wrong There is a misunderstanding

My own

scene 1

You write a class A, Many of these methods depend on another class B, The so-called dependence is understood as the need import Just instantiate . First , I have to write a lot new Duplicate code for so much trouble . secondly , So if each function goes alone new, once B Class changed its name Then all functions will be changed once . Even if you can ide One click modification , But what if you change a function to another place , Moved to C( Maybe because this classification is more appropriate ), because B There is still ,A Used in the B Other methods So you can't directly replace with one key . And there may be a problem with one click replacement Don't rely too much on ide What if you make a mistake
So you think , Put this class , Create good in the constructor .

__construt(){
    
this->B = new B();// Here in front of B  You can also change your name   such as  this-》map = new HashMap
}

But there are two problems :
1 Caused and B Strong dependency of class , Because not all functions use B Class , And this caused ,B have a problem be-all A Can't work properly
2 B Classes are not just A Use If you want to change it That's not all used B All classes have to be changed ?
3 But what if you change a function to another place , Moved to C( Maybe because this classification is more appropriate ), because B There is still ,A Used in the B Other methods So you can't directly replace with one key .---- Add a... To this constructor C That's it , And then one click replacement .
4、 Repeat instantiation

scene 1.1 and 1.4

Here you can see php Realized there use set In the form of , But this way It's also troublesome when the parameter list is long , So use a container . In the end, the public constructor only needs to pass in a container class . You can instantiate it when you need it .
The alias here can be defined in a separate file And then rely on B Other classes of can also directly access .
But most of the time A class will not rely on so many other classes in parallel at one level , So when you understand , Just use the constructor example to understand it directly

scene 1.2

Factory mode
such as
collection map=new HashMap();
Turn into
collection map=MapFactory.createMap();

however
1、 You don't know what might need factory patterns to create , Cannot define in advance . If it's all defined in advance , How much definition code does it have to write ? In case of nesting, you have to code a set of codes .
2、 And not every class needs to be changed .

scene 1.2.1

in general

Need the unified management of factory mode It's just provider Register alias in container
You don't need Add a type to the constructor

Others speak well

author :93 Contestant No
link :https://www.zhihu.com/question/32108444/answer/121881566
source : You know
The copyright belongs to the author . Commercial reprint please contact the author for authorization , Non-commercial reprint please indicate the source .

If there is one ship (Chuan) class , There must be a in the member variable Oars (Jiang) class ,class Chuan{
Jiang j = new Jiang() ;
} If the ship is going to do something , It definitely needs the participation of oars . So it's very “ rely on ” Oars ; New needs , The paddle needs a uniform length of 10 rice . Need to refactor : At this time, we need to control the length of the paddle to 10 In the constructor . We need to write ;class Chuan{
Jiang j = new Jiang(10) ;
}
coming , This change also needs to modify the class of paddle, that is, add the length attribute , You need to modify the ship's new Jiang() Code incoming length ( Two changes ). At this time, the designer thinks , Why do we need to change the code in two classes to add a feature ( This means high coupling )! So we need to decouple and rely on injection ; Common decoupling methods : The construction method is injected as follows : When I refactor the code, I don't have to care whether the paddle length is short or not , No matter the width or the color ! Because the ship construction method relies on oars . No matter how you design the paddle , When I use it, just pass an oar in .( The lower level depends on the upper level , Pass in when using , Instead of modifying for the lower level )class Chuan{
Jiang j ;
public Chuan(Jiang j){
this.j = j;
};
}
The new requirements injected into the factory mode are as follows : The manufacturer of oars feels that you can't make an oar for ships all over the country , When does Tianjin ship need Tianjin local new An oar , Beijing needs Beijing Local new An oar , This results in multiple code dependencies in use , In this way, the specification of the propeller will be modified everywhere , It's too troublesome and easy to make mistakes. It's not uniform . So set up a national factory , Let the production process of oars be new The process of is in this class , The ship only focuses on Factory Methods in the class can .( The classes that the core business logic needs to depend on , The instantiation of this class is handed over to a third-party class to implement injection .) Factory mode New Factory Factory class to inject ; Factory categories are as follows class Factory {
/**
* adopt msg The length... The color you want to determine . The factory produced a set of specifications . After that, constraint extension can be carried out in this class
**/
public Jiang getJiang(String msg){
if(msg=“10”){
return new Jiang(10)
}else if(msg=“red”){
return new Jiang(“red”)
};
};

} The ship's code changes to class Chuan {
Jiang j ;
void run(){
Factory h = new Factory();
j=h.getJiang(“red“); // Got the red oar
j=h.getJiang(“10“); // Get the length 10 Paddle
};

} Frame injection ( The essence is the concrete realization of factory design pattern ) In essence, it is also a third-party dependency injection , But this third party can leave the class . Storing object dependency mapping information in a container is generally .xml Or in a specific object , And realize dynamic injection . Just take it when you need it . Finally, I personally understand : Why dependency injection ( A design code pattern ), Because we want to control inversion ( The idea of designing code ). Why control reversal . Because our software design needs to comply with the principles of software design ( Design code principles ), Principle of single responsibility , Opening and closing principle . It all comes down to , Look for easily changing points in the business , Look for decoupling points , Make it more controllable , Make it easier for programmers to change code , At the same time, it has little impact on the system .

2

author :Rootrl
link :https://www.zhihu.com/question/32108444/answer/417561137
source : You know
The copyright belongs to the author . Commercial reprint please contact the author for authorization , Non-commercial reprint please indicate the source .

for instance :

class Comment{
    
········ Other code omissions 
		public function afterInsert()	{
    		
		$notification = new EmailNotification(...);	
			$notification->send(...);
				}}

Above , If we notify the commentee after the user submits a comment , The notification method here is email , And it directly instantiates the email notification class in the class , In this way, the code coupling is high , If you change the SMS notification method, you have to change the code in this , We will talk about the specific implementation below .#### Dependency injection dependency injection is a design pattern , It's a kind of IoC The concrete realization of , Realized IoC Naturally, it conforms to the principle of inversion of dependence . The core idea of dependency injection is to implement the instantiation process of the dependent unit in the class outside the class , Then inject the dependency . Common dependency injection methods include attribute injection and constructor injection . For example, use constructor injection to decouple the above code :

//  Notification interface 
interface Notifaction{
    	
public function send(...);
}
//  SMS notification interface 
class SmsNotification implements Notification{
    	
public function send(...)	{
    		
...	}
}

//  Comments 
class Comment{
    	
...	
protected $notification;	
public function __construct(Notification $smsNotification)	{
    		
$this->notification = $smsNotification;	
}	
public function afterInsert()	{
    		
$this->notification->send(...);	
}
}

//  Instantiate SMS notification class 
$smsNotification = new SmsNotification(...);//  Inject... Through constructor methods 
$comment = new Comment($smsNotification);
...
$comment->save();

such , Let's define Notification Interface , There's a send Method , Whether the SMS class or the email class is implemented later , Then inject it outside through the constructor , That's it Comment Class's dependence on specific notification methods , As long as it is realized Notification Interface , Can be passed in through the constructor ,Comment Class without any modification at all . In this way, whether for code maintenance or unit testing ( Can simulate the implementation of a Notification class ), It's very convenient .

3

author :CHANGEX
link :https://www.zhihu.com/question/32108444/answer/582118313
source : You know
The copyright belongs to the author . Commercial reprint please contact the author for authorization , Non-commercial reprint please indicate the source .

stay Spring There are two very important concepts in IoC( Inversion of control ) and DI( Dependency injection ), These two functions can be decoupled when used together . First of all, we need to understand IoC It is the realization of a factory mode ,DI The function of is to set the property value of an object . The main function is to make new Object is configurable , We are xml All kinds of... Written in it <bean> The node is actually in new object ( Reflection ) How does this decouple ? Why do that ? First, the first question , Let's take a case study :
according to MVC Architecture to write code , Let's see Dao Layer and the Service layer :
1.dao Layer interface TestDao
2.dao Layer implementation class TestDaoImpl
3.service layer

Don't use IoC and DI: stay service Need to use dao Layer instance :
private TestDao testDao=new TestDaoImpl();
here , If we need to switch dao The layer instance is TestDaoImpl2 What to do ?
1. To write TestDaoImpl2 Related code
2. modify service The layer code is :private TestDao=new TestDaoImpl2();
There's only one service The layer references this dao, Then if there is 100 All classes refer to this dao, So we need new100 individual dao Layer implementation class , You also need to change the code every time , Find this 100 individual service To modify the code .

Then if you use IoC and DI Well ?
1. stay TestDaoImpl Add up @Repository annotation
2. stay Service Layer injection :@Autowiredprivate TestDao testDao;
If it needs to be modified TestDao Implementation class of , We need to :
1. To write TestDaoImpl2 Specific code
2. Cancel TestDaoImpl Upper Repository annotation
3. stay TestDaoImpl2 Add up Repository annotation
You see , This time we didn't modify Service Layer any code , Even if there are 100 All classes are injected with TestDao, We don't need to change it anymore . And because of the singleton mode , this 100 individual service The same... Is used in dao example , Reduced memory usage . As for why decoupling , In fact, if our program doesn't need to be maintained and updated after the first launch , There is no need to consider any architecture at all 、 Decoupling, these messy concepts . But the reality is , After the program goes online, it needs continuous maintenance and update , Inevitably, you need to modify the previous code , If decoupling is not carried out , When we modify a certain code, we may need to modify many places at the same time . Decoupling is to solve this problem . Use xml The advantage of configuration is that it solves the problem of hard coding , If we need to switch implementation classes , It just needs to be modified xml file , No code changes required , Of course, there is no need to recompile the code .

My explanation

The difference between here and above is Factory pattern and dependency injection are used at the same time Interface is also used

No interface No factories Don't rely on Injection :
service layer Dao Instance class 1 Variable name = Dao Instance class 1
however This is the definition of service The usual way The parameter type of the method can only be written Dao Instance class 1 inconvenient If multiple methods are used Dao Instance class 1 To change to an instance 2 Words There's a lot to change

Use interface No factories Don't rely on Injection :
service layer Dao Interface Variable name = Dao Instance class 1
such At least service Method definition of layer Don't change everything , But if there is 100 Both use this instance class 1 Now change to 2 It's still troublesome

Use interface Use factory Don't rely on Injection
service layer
Dao Interface Variable name = Factory .createDao example ();
Then we can define it uniformly , however This can only be modified uniformly If we have some service Want to use the original instance class 1 Well

Use interface Use factory Using dependency injection
1. stay Dao Instance class 1 add @Repository annotation
2. stay Service Layer injection :@Autowiredprivate Instance class 1 The interface name
If it needs to be modified TestDao Implementation class of , We need to :
1. Write instance classes 2 Specific code
2. Cancel instance class 1 Upper Repository annotation
3. In instance class 2 Add up Repository annotation
forehead Something's wrong here It doesn't seem to solve the above problem

ps The law of logical separation
in fact If we explain Method 1 There are also the following problems But it's messy to say so And cross So like this Push step by step to talk about

4

In the beginning , We create objects directly new Object out , This drawback is that there is great laziness between programs , Not easy to maintain and expand , We introduce the factory model for decoupling , The principle of the factory model is to analyze ( Read ) The configuration file , Create objects through reflection technology , The advantage is that when we need to create objects in the future , Get the required objects directly from the factory , Reduced coupling , In the whole process of creating objects, we take the initiative new Become a passive factory to create or find objects for us , The idea of acquiring objects by passive reception is control inversion , So is it spring One of the cores of the framework , But there is a drawback , That is, if the loop calls the factory to get the object , Then the program becomes multi instance mode , It also takes up memory space , To solve this problem , We lead to IoC Concept of container , After the factory creates each object, we store the object in the collection , Because the array length is fixed , Arrays are not considered here ,List And Map The choice of collection depends on whether you have the need to query , What we're using here is Map Collection holds objects , Stored in the collection are the unique identification and object of the reflection creation object , This Map The collection is IoC Containers , adopt IoC Containers , We can get what we need , And establish dependencies between objects , This process is called dependency injection (DI)

You can put IoC Patterns are seen as the sublimation of the factory model , You can put IoC As a big factory , But the objects to be generated in this big factory are all in XML As defined in the document , And then use it Java Of “ Reflection ” Programming , according to XML The corresponding object is generated by the class name given in . In terms of implementation ,IoC It is to generate code from objects that were previously written dead in factory methods , Change to XML File to define , That is to separate the factory from the object generation , The goal is to improve flexibility and maintainability .

5

 Insert picture description here
It seems that the factory is manual Injection is automatic ?

6

 Insert picture description here

7

 Insert picture description here

版权声明
本文为[The clouds are idle]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204231435240789.html