SOLID 원칙 이해: 종속성 반전

드디어 우리의 여정이 끝났습니다.

하지만 이제 최종 보스를 만날 시간입니다.

DIP aka Dependency Inversion Principle

SOLID 보스의 최종 원칙.

장비를 갖추고 마나 물약을 마시고 전투를 준비하십시오.

목차


  • What is the dependency inversion principle?
  • Example
  • Why should I follow this principle?
  • Conclusion

  • 의존성 반전 원리란?

    Let's google that.

    We get this:

    The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.

    I get it, it sounds confusing.

    But let's break this down:

    1. Client: Your main class/code that runs the high-level module.
    2. High-Level Modules: Interface/Abstraction that your client uses.
    3. Low-Level Modules: Details of your interfaces/abstraction.

    What it basically says that imagine you have a car and your different components are:

    1. Client: You as the person driving the car.
    2. High-Level Modules: The steering wheel and the gas/brake peddles.
    3. Low-Level Modules: Engine

    Abstractions don't depend on details.

    For me, it doesn't matter whether my engine has changed or not, I still should be able to drive my car the same way.

    Details should depend upon abstractions.

    I would not want an engine that causes the brake to double the speed.

    간단한 예

    Imagine you have a budget reporting system that reads from a database.

    It will look something like this:

    <?php 
    
    class BudgetReport {
        public $database;
    
        public function __construct($database)
        {
            $this->database = $database;
        }
    
        public function open(){
            $this->database->get();
        }
    
        public function save(){
            $this->database->insert();
        }
    }
    
    class MySQLDatabase {
        // fields
    
        public function get(){
            // get by id
        }
    
        public function insert(){
            // inserts into db
        }
    
        public function update(){
            // update some values in db
        }
    
        public function delete(){
            // delete some records in db
        }
    }
    
    // Client
    $database = new MySQLDatabase();
    $report = new BudgetReport($database);
    
    $report->open();
    

    Everything works fine, but this code violates the dependency inversion principle because our high-level module BudgetReport concretely depends on the low-level module MySQLDatabase .

    This also violates the open-closed principle because what if we wanted a different kind of database such as MongoDB?

    We will have to change the BudgetReport class to have if-else statements for it not to break.

    To fix this is pretty simple, instead of concretely relying on the database class, we should use an abstraction. We will create a DatabaseInterface which will implement any kind of database we need and finally we can inject our database through the constructor.

    interface DatabaseInterface {
        public function get();
        public function insert();
        public function update();
        public function delete();
    }
    
    class MySQLDatabase implements DatabaseInterface {
        // fields
    
        public function get(){
            // get by id
        }
    
        public function insert(){
            // inserts into db
        }
    
        public function update(){
            // update some values in db
        }
    
        public function delete(){
            // delete some records in db
        }
    }
    
    class MongoDB implements DatabaseInterface {
        // fields
    
        public function get(){
            // get by id
        }
    
        public function insert(){
            // inserts into db
        }
    
        public function update(){
            // update some values in db
        }
    
        public function delete(){
            // delete some records in db
        }
    }
    
    class BudgetReport {
        public $database;
    
        public function __construct(DatabaseInterface $database)
        {
            $this->database = $database;
        }
    
        public function open(){
            $this->database->get();
        }
    
        public function save(){
            $this->database->insert();
        }
    }
    
    // Client
    $mysql = new MySQLDatabase();
    $report_mysql = new BudgetReport($mysql);
    
    $report_mysql->open();
    
    $mongo = new MongoDB();
    $report_mongo = new BudgetReport($mongo);
    
    $report_mongo->open();
    

    Now our BudgetReport does not depend concretely on the database class but on its abstraction DatabaseInterface . This approach also follows the open-closed principle because to add any new database we don't have to change the BudgetReport class. We just need to add a new database class that implements the DatabaseInterface .

    왜 이 원칙을 따라야 합니까?

    Because code that violates this principle may become too coupled together, and that makes the code hard to maintain, unreadable, and prone to side effects.

    결론

    In summary:

    1. The Dependency Inversion Principle (DIP) states that high-level modules should not depend on low-level modules; both should depend on abstractions. Abstractions should not depend on details. Details should depend upon abstractions.
    2. Code that doesn't follow this principle can be too coupled, which means you will have a hard time managing the project.

    If you have any questions, leave them down in the comments section.

    좋은 웹페이지 즐겨찾기