kimddakki 2022. 8. 25. 13:54
의존성

 - 의존성은 구성 요소들이 서로 의존하는 성질입니다.

// 의존성은 구성 요소들이 서로 의존하는 성질입니다.
// Programmer 클래스는 Laptop 클래스에 의존성을 띕니다.

class Laptop {
  public turnOn() {}
}

class Macbook extends Laptop {}

class Programmer {
  private laptop: Laptop;

  constructor() {
    this.laptop = new Laptop();
  }

  public programming() {
    this.laptop.turnOn();
  }
}

// programmer가 Mabook을 사용한다면 생성자 함수에 Macbook으로 변경해야 하는 이슈가 발생한다.
class Programmer2 {
  private laptop: Laptop;

  constructor() {
    this.laptop = new Macbook();
  }

  public programming() {
    this.laptop.turnOn();
  }
}

const programmer: Programmer = new Programmer();
programmer.programming();

 - 위 코드에서 Programmer 클래스는 Laptop 클래스에 의존성을 띄게 됩니다.

 - 위의 예제에서 Programmer가 평범한 Laptop이 아닌 Macbook을 사용하도록 만들게 되면 2번째와 같은 코드를 수정해야

   합니다.

 

 - Laptop 클래스를 상속 받는 Macbook 클래스를 생성하고 Programmer 클래스의 laptop 변수가 Macbook 클래스의 

   객체를 할당 받도록 수정해주면 됩니다.

 - 이럴 경우 Programmer 클래스가 Laptop 클래스에 의존하고 있었기 때문에 Programmer 클래스를 직접 수정해야 하는

   번거로움이 생기게 됩니다.

   => 즉 이렇게 의존성은 하나를 변경하면 그것에 의존하는 다른 것들도 모두 변경해야 합니다.

 

의존성 주입

 - 의존성 주입이란 외부에서 의존성을 주입해주느 것을 말합니다.

// 의존성 주입이란 말그대로 외부에서 의존성을 주입해주는 것을 말합니다.

export class Laptop {
  public turnOn() {
    console.log("programming start");
  }
}

export class Macbook extends Laptop {
  public turnOn() {
    console.log("programming start with Macbook");
  }
}

export class Programmer {
  private laptop: Laptop;

  constructor(laptop: Laptop) {
    this.laptop = laptop;
  }

  public programming() {
    this.laptop.turnOn();
  }
}

const programmer: Programmer = new Programmer(new Laptop());
const programmer2: Programmer = new Programmer(new Macbook());

// programming start
// programming start with Macbook

 - 첫 예제에 의존성 주입을 사용하였습니다. Programmer 클래스가 의존하던 Laptop 클래스의 객체를 외부에서 

   주입해주고 있습니다.

 - Macbook을 사용하는 프로그래머를 만드는 것도 Laptop 클래스의 객체 대신 Macbook 클래스의 객체를 주입해주면

   Programmer 클래스를 수정하지 않고도 아주 편리하게 변경사항을 적용할 수 있습니다.

 - 이것이 의존성 주입입니다.

 

의존 관계 역전의 원칙

 - 의존 관계 역전의 원칙은 SOLID 원칙 중 하나로, 변화하기 쉬운 것 보단 변화하기 어려운 것에 의존하라는 원칙입니다.

 

 - 예를 들면, 프로그래머들의 구체적으로 어떤 기종을 사용할지는 변화하기 쉬운 것입니다.

   하지만 프로그래머들이 노트북만으로 코딩을 한다는 점은 변하기 어렵습니다.

 

interface Laptop {
  turnOn(): void;
}

class Macbook implements Laptop {
  public turnOn() {}
}

class Gram implements Laptop {
  public turnOn() {}
}

class Programmer {
  constructor(private laptop: Laptop) {}

  public programming() {
    this.laptop.turnOn();
  }
}

const programmer1: Programmer = new Programmer(new Macbook());

const programmer2: Programmer = new Programmer(new Gram());

 - Macbook 클래스와 Gram 클래스가 Laptop 인터페이스를 구현하고 있습니다. 

   그리고 Programmer 클래스의 생성자는 Laptop 타입의 인자를 받고 있습니다.

 

 - 따라서 모든 클래스가 Laptop이라는 인터페이스에 의존하게 되고, 이는 일반적인 의존 관계 

   ( 하위 클래스가 상위 클래스에 의존 ) 한다는 것과 다르다는 것을 알 수 있습니다.

 

 - 여기서 Macbook, Gram과 함께 삼성 노트북도 사용하고 싶다면 Laptop 인터페이스를 구현하는 삼성 노트북 클래스를

   만들면 됩니다. 삼성 노트북 클래스의 인스턴스를 Programmer에게 주입하더라도 어떤 문제도 발생하지 않습니다.

 

 - 즉 이러한 의존 관계는 유연한 확장을 가능하게 만들고, 변경이 불필요하도록 만듭니다.

   이는 SOLID 원칙 중 개방 - 폐쇄 원칙이 지켜진 것입니다.

 

제어권 역전

 - 의존성 주입만 사용하게 되면 우리가 직접 의존성을 관리해야 합니다.

 - 하지만 클래스가 가질 수 있는 의존성은 무한합니다. 따라서 새로운 객체를 생성할 때마다 직접 주입해 주는 것은

   지루하고 비효율적입니다.

 

 - 그렇기에 TypeI가 존재합니다.!


 

 

TypeScript와 typedi로 의존성 주입 이해하기

아름다운 코드를 짜기 위해서 의존성 주입을 알아봅시다.

medium.com