What is Inversion of Control?

2020-01-22 11:28发布

Inversion of Control (IoC) can be quite confusing when it is first encountered.

  1. What is it?
  2. Which problem does it solve?
  3. When is it appropriate to use and when not?

30条回答
啃猪蹄的小仙女
2楼-- · 2020-01-22 11:43

Inversion of Control is what you get when your program callbacks, e.g. like a gui program.

For example, in an old school menu, you might have:

print "enter your name"
read name
print "enter your address"
read address
etc...
store in database

thereby controlling the flow of user interaction.

In a GUI program or somesuch, instead we say:

when the user types in field a, store it in NAME
when the user types in field b, store it in ADDRESS
when the user clicks the save button, call StoreInDatabase

So now control is inverted... instead of the computer accepting user input in a fixed order, the user controls the order in which the data is entered, and when the data is saved in the database.

Basically, anything with an event loop, callbacks, or execute triggers falls into this category.

查看更多
神经病院院长
3楼-- · 2020-01-22 11:44

Inversion of Control, (or IoC), is about getting freedom (You get married, you lost freedom and you are being controlled. You divorced, you have just implemented Inversion of Control. That's what we called, "decoupled". Good computer system discourages some very close relationship.) more flexibility (The kitchen in your office only serves clean tap water, that is your only choice when you want to drink. Your boss implemented Inversion of Control by setting up a new coffee machine. Now you get the flexibility of choosing either tap water or coffee.) and less dependency (Your partner has a job, you don't have a job, you financially depend on your partner, so you are controlled. You find a job, you have implemented Inversion of Control. Good computer system encourages in-dependency.)

When you use a desktop computer, you have slaved (or say, controlled). You have to sit before a screen and look at it. Using the keyboard to type and using the mouse to navigate. And a badly written software can slave you even more. If you replace your desktop with a laptop, then you somewhat inverted control. You can easily take it and move around. So now you can control where you are with your computer, instead of your computer controlling it.

By implementing Inversion of Control, a software/object consumer gets more controls/options over the software/objects, instead of being controlled or having fewer options.

With the above ideas in mind. We still miss a key part of IoC. In the scenario of IoC, the software/object consumer is a sophisticated framework. That means the code you created is not called by yourself. Now let's explain why this way works better for a web application.

Suppose your code is a group of workers. They need to build a car. These workers need a place and tools (a software framework) to build the car. A traditional software framework will be like a garage with many tools. So the workers need to make a plan themselves and use the tools to build the car. Building a car is not an easy business, it will be really hard for the workers to plan and cooperate properly. A modern software framework will be like a modern car factory with all the facilities and managers in place. The workers do not have to make any plan, the managers (part of the framework, they are the smartest people and made the most sophisticated plan) will help coordinate so that the workers know when to do their job (framework calls your code). The workers just need to be flexible enough to use any tools the managers give to them (by using Dependency Injection).

Although the workers give the control of managing the project on the top level to the managers (the framework). But it is good to have some professionals help out. This is the concept of IoC truly come from.

Modern Web applications with an MVC architecture depends on the framework to do URL Routing and put Controllers in place for the framework to call.

Dependency Injection and Inversion of Control are related. Dependency Injection is at the micro level and Inversion of Control is at the macro level. You have to eat every bite (implement DI) in order to finish a meal (implement IoC).

查看更多
闹够了就滚
4楼-- · 2020-01-22 11:44

Suppose you are an object. And you go to a restaurant:

Without IoC: you ask for "apple", and you are always served apple when you ask more.

With IoC: You can ask for "fruit". You can get different fruits each time you get served. for example, apple, orange, or water melon.

So, obviously, IoC is preferred when you like the varieties.

查看更多
Bombasti
5楼-- · 2020-01-22 11:46

But I think you have to be very careful with it. If you will overuse this pattern, you will make very complicated design and even more complicated code.

Like in this example with TextEditor: if you have only one SpellChecker maybe it is not really necessary to use IoC ? Unless you need to write unit tests or something ...

Anyway: be reasonable. Design pattern are good practices but not Bible to be preached. Do not stick it everywhere.

查看更多
可以哭但决不认输i
6楼-- · 2020-01-22 11:49

Before using Inversion of Control you should be well aware of the fact that it has its pros and cons and you should know why you use it if you do so.

Pros:

  • Your code gets decoupled so you can easily exchange implementations of an interface with alternative implementations
  • It is a strong motivator for coding against interfaces instead of implementations
  • It's very easy to write unit tests for your code because it depends on nothing else than the objects it accepts in its constructor/setters and you can easily initialize them with the right objects in isolation.

Cons:

  • IoC not only inverts the control flow in your program, it also clouds it considerably. This means you can no longer just read your code and jump from one place to another because the connections that would normally be in your code are not in the code anymore. Instead it is in XML configuration files or annotations and in the code of your IoC container that interprets these metadata.
  • There arises a new class of bugs where you get your XML config or your annotations wrong and you can spend a lot of time finding out why your IoC container injects a null reference into one of your objects under certain conditions.

Personally I see the strong points of IoC and I really like them but I tend to avoid IoC whenever possible because it turns your software into a collection of classes that no longer constitute a "real" program but just something that needs to be put together by XML configuration or annotation metadata and would fall (and falls) apart without it.

查看更多
孤傲高冷的网名
7楼-- · 2020-01-22 11:49

I found a very clear example here which explains how the 'control is inverted'.

Classic code (without Dependency injection)

Here is how a code not using DI will roughly work:

  • Application needs Foo (e.g. a controller), so:
  • Application creates Foo
  • Application calls Foo
    • Foo needs Bar (e.g. a service), so:
    • Foo creates Bar
    • Foo calls Bar
      • Bar needs Bim (a service, a repository, …), so:
      • Bar creates Bim
      • Bar does something

Using dependency injection

Here is how a code using DI will roughly work:

  • Application needs Foo, which needs Bar, which needs Bim, so:
  • Application creates Bim
  • Application creates Bar and gives it Bim
  • Application creates Foo and gives it Bar
  • Application calls Foo
    • Foo calls Bar
      • Bar does something

The control of the dependencies is inverted from one being called to the one calling.

What problems does it solve?

Dependency injection makes it easy to swap with the different implementation of the injected classes. While unit testing you can inject a dummy implementation, which makes the testing a lot easier.

Ex: Suppose your application stores the user uploaded file in the Google Drive, with DI your controller code may look like this:

class SomeController
{
    private $storage;

    function __construct(StorageServiceInterface $storage)
    {
        $this->storage = $storage;
    }

    public function myFunction () 
    {
        return $this->storage->getFile($fileName);
    }
}

class GoogleDriveService implements StorageServiceInterface
{
    public function authenticate($user) {}
    public function putFile($file) {}
    public function getFile($file) {}
}

When your requirements change say, instead of GoogleDrive you are asked to use the Dropbox. You only need to write a dropbox implementation for the StorageServiceInterface. You don't have make any changes in the controller as long as Dropbox implementation adheres to the StorageServiceInterface.

While testing you can create the mock for the StorageServiceInterface with the dummy implementation where all the methods return null(or any predefined value as per your testing requirement).

Instead if you had the controller class to construct the storage object with the new keyword like this:

class SomeController
{
    private $storage;

    function __construct()
    {
        $this->storage = new GoogleDriveService();
    }

    public function myFunction () 
    {
        return $this->storage->getFile($fileName);
    }
}

When you want to change with the Dropbox implementation you have to replace all the lines where new GoogleDriveService object is constructed and use the DropboxService. Besides when testing the SomeController class the constructor always expects the GoogleDriveService class and the actual methods of this class are triggered.

When is it appropriate and when not? In my opinion you use DI when you think there are (or there can be) alternative implementations of a class.

查看更多
登录 后发表回答