DIP-역방향 원리에 의존

의존 역치 원칙은 SOLID의 일부이며, SOLID는 알파벳 줄임말로 총 5개의 디자인 원칙을 포함한다.
그것은 보통 깨끗한 코드와 관련이 있다.
그러나 그것이 도대체 무엇인지, 너에게 중요한지, 관심을 가져야 하는가?

그것은 무엇을 설명했습니까?


Modules that encapsulate high-level policy should not depend upon modules that implement details. Rather, both kinds of modules should depend upon abstractions.


이것은 약간 복잡하게 들릴 수 있지만 다음과 같이 분해할 수 있습니다.
  • 고급 모듈은 하급 모듈에 의존해서는 안 된다.둘 다 추상적(예를 들어 인터페이스)에 의존해야 한다.
  • 추상은 디테일에 의존해서는 안 된다.세부 사항(구체적 실현)은 추상에 달려 있어야 한다.
  • 아직도 안 좋아요?
    네, 이렇게 보세요.

    Don't let anything you build depend on a concrete implementation of something.
    Better depend on an abstract description of a contract, and let any implementor decide how to satisfy that contract.


    하나의 예


    예를 보면 통상적으로 하나의 개념을 더욱 잘 이해하는 데 도움이 되기 때문에 여기에 하나의 예가 있다.
    백엔드 API를 구성하는 방법입니다.
    API의 세부 사항을 실현하는 자원 층, 업무 논리를 포함하는 서비스와 처리 지속성 층(예를 들어 데이터베이스 상호작용)을 포함하는 저장소.
    class PositionRepository {
      constructor() {}
    }
    
    class TransactionRepository {
      constructor() {}
    }
    
    class TransactionService {
      private readonly transactionRepository: TransactionRepository;
      private readonly positionRepository: PositionRepository;
    
      constructor(
        transactionRepository: TransactionRepository,
        positionRepository: PositionRepository
      ) {
        this.transactionRepository = transactionRepository;
        this.positionRepository = positionRepository;
      }
    }
    
    class TransactionResource {
      private readonly transactionService: TransactionService;
    
      constructor(transactionService: TransactionService) {
        this.transactionService = transactionService;
      }
    }
    
    자세히 살펴보면, 고급 클래스 (자원) 는 구체적인 저급 클래스 (서비스) 에 의존하고, 저급 클래스 (서비스) 자체는 두 개의 (더 많은) 저급 클래스 (저장소) 에 의존하는 것을 발견할 수 있습니다.
    따라서 컨트롤 흐름은 왼쪽에서 오른쪽, 최고급에서 최저급으로 흐른다.
    그것은 마치 하나의 체인 시계와 같고, 심지어는 나무와도 같다.모든 고급 모듈은 최소한 저급 모듈을 인용해야 한다.

    그것은 무엇을 막으려고 시도합니까?


    하나의 모듈 (하나의 클래스, 하나의 함수, 하나의 완전한 모듈) 을 다른 모듈에 결합할 때마다, 첫 번째 모듈은 많은 낮은 단계의 세부 사항에 의존한다.
    대상 시스템에서, 이것은 이 종류에서 파생되고 기존 논리를 덮어써서 의존하는 논리를 바꿀 수 밖에 없다는 것을 의미한다.
    이것은 매우 긴밀한 결합이며, 매우 유연성이 부족하다.
    저급 논리를 교체하는 것은 매우 어려워지고 저급 논리의 변경은 고급 논리에 더욱 큰 영향을 미칠 수 있다.
    상상해 보십시오. 당신은 지금 예시에 따라 재구성 TransactionService 을 하고 있습니다.
    TransactionResource를 명확하게 보지 않으면, 현재 그곳에서 조정이 필요할 수도 있다고 판단할 수 없습니다.
    또 다른 문제는 낮은 등급의 모듈에 대한 직접적인 의존이다.
    만약에 이 예시를 하나의 라이브러리에 적용하고 class A class B에 의존하며 둘 다 다른 모듈에서 온다면 사용자는 시종 module Amodule B (전달 가능) 에 의존할 것이다.
    사용자는 직접적인 관계와 의존 관계가 존재하기 때문에 받아들이지 않을 수도 없다module B.

    문제를 해결하다


    모든 구체적인 실현을 인터페이스에 의존하거나 상기 인터페이스를 실현함으로써 흐름을 반전시킬 수 있습니다. 아래와 같습니다.
    interface PositionRepository {}
    
    interface TransactionRepository {}
    
    interface TransactionService {}
    
    class TransactionServiceImplementation implements TransactionService {
      constructor(
        transactionRepository: TransactionRepository,
        positionRepository: PositionRepository
      ) {}
    }
    
    class TransactionResource {
      private readonly transactionService: TransactionService;
    
      constructor(transactionService: TransactionService) {
        this.transactionService = transactionService;
      }
    }
    
    흐름을 제어하는 것은 최고 등급의 모듈에서 최저 등급의 모듈이 아니라 그것들 사이에 추상적인 것이 있다.

    네가 큰소리로 말하기 전에: 네, 이것은 전체적으로 더 많은 코드를 증가시켰습니다. 왜냐하면 모든 인터페이스도 먼저 설명해야 하기 때문입니다.
    그런데 모듈 의존성 문제 기억나세요?module A도 필요하지 않고module Bmodule B도 필요합니다module A(주어진module A인터페이스가 정의되어 있음). 이로써 사용자는 끌어들이고 싶은지module B하고 싶은지module C인터페이스를 제공하는 또 다른 실현 대안으로 선택할 수 있습니다. 이것은 그들의 용례에 있어서 더 좋은 선택일 수 있습니다!

    신경 써요?


    그건 상황을 봐야지.
    JavaScript와 같은 동적 유형 언어는 거의 모든 곳에서 인터페이스에 의존합니다. 왜냐하면 모든 대상의 방법이나 속성을 간단하게 호출할 수 있기 때문입니다.
    사용자가 당신이 필요로 하는 모든 것을 제공하기만 하면 당신은 시작할 수 있습니다!
    그러나 정적 입력이 작용하기 시작하면 진정으로 관심을 가져야 한다.
    여기에서 설명한 모든 장점을 얻을 수 있습니다. 코드의 질, 사용 가능성, 유지보수성을 크게 향상시킬 것입니다.
    결국 이런 장점들은 상당히 중요하므로 소홀히 해서는 안 된다.

    네가 떠나기 전에


    만약 당신이 나의 내용을 좋아한다면, 계속 나를 방문해 주십시오. 아마도 당신은 당신이 본 것을 좋아할 것입니다.

    좋은 웹페이지 즐겨찾기