What is Inversion of Control?

前端 未结 30 2882
清歌不尽
清歌不尽 2020-11-22 00:13

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 appropria
30条回答
  •  臣服心动
    2020-11-22 00:38

    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.

提交回复
热议问题