ch-3-의존성-관리하기

객체 사이의 상호 작용, 메시지를 통해 문제의 해결책을 제공.

  • 각 메시지는 하나의 객체에서 시작되며 특정 행동을 유발하기 위해 존재

  • 그러므로 어떤 행동을 유발하고자 할 때

    • 객체는 그 행동을 이미 알고 있거나: 얘는 2장에서

    • 상속받았거나: 6장 상속을 통해 새로운 행동 얻기

    • 그 행동에 대해 알고 있는 다른 객체에 대해 알아야 함: 얘는 의존성 관리하기

서로 협업하려면 객체는 다른 객체에 대한 지식이 있어야함. 이 지식은 의존성을 만들어냄

3.1 의존성 이해하기

만약 1개 객체를 수정했을 때, 다른 객체를 수정해야 한다면 후자는 전자에 대해 의존적

class Gear {
  readonly cog: number;
  readonly chainRing: number;
  readonly rim: number;
  readonly tire: number;

  constructor(cog: number, chainRing: number, rim: number, tire: number) {
    this.cog = cog;
    this.chainRing = chainRing;
    this.rim = rim;
    this.tire = tire;
  }

  ratio() {
    return this.chainRing / this.cog;
  }

  // 새로 추가된 함수
  gear_inches() {
    // 타이어는 바퀴테를 가싸고 있어서, 지름 계산시 타이어 높이에 2를 곱함
    return this.ratio * (this.rim + this.tire * 2);
  }
}

class Wheel {
  readonly rim: number;
  readonly tire: number;

  constructor(rim: number, tire: number {
    this.rim = rim;
    this.tire = tire;
  }

  diameter() {
    return this.rim + (this.tire * 2);
  }
}

Gear.new(52, 11, 26, 1.5).gear_inches();

3.1.1 의존성이 있다는 걸 알기

객체가 다음 내용을 알고 있을 때 의존성을 갖음

  • 다른 클래스의 이름

    • Gear는 Wheel이라는 이름의 클래스가 있다는 걸 알고 있음

  • 자기 자신을 제외한 다른 객체에게 전송할 때 이름

    • Gear는 Wheel의 인스턴스가 diameter라는 메서드를 이해할 수 있다는 걸 알고 있음

  • 메시지가 필요로 하는 인자들

    • Gear는 Wheel.new를 위해 rim과 tire를 인자로 넘겨야함

  • 인자들을 전달하는 순서

    • Gear는 Wheel.new의 첫 번째 인자가 rim이고 두 번째 인자가 tire라는 걸 알고 있음

위에서 나열한 의존성들은 Wheel을 변경했을 때 어쩔 수 없이 Gear도 수정해야 하는 상황 만듬

  • Wheel과 Gear는 꼭 협업해야 하니까 어쩔 수 없이 의존성은 생김

  • 하지만 위에 나열한 의존성은 불필요함

  • 불필요한 의존성은 코드를 덜 reasonable 하게 만듬

  • 왜냐하면 이 의존성이 Gear 클래스의 수정을 강제하기 때문.(코드 수정이 애플리케이션 전체에 영향을 미쳐 여러 곳 수정해야하는 대공사도 유발)

3.1.2 객체들 간 결합

이런 의존성은 Gear를 Wheel에 결합(couple)시킴. 즉, 이런 결합이 의존성을 낳음.

  • Gear가 Wheel에 대해 더 많이 알수록 강하게 결합됨

의존성이 높은 객체들은 마치 하나인 것처럼 행동. 이로인해,

  • 이들 중 하나만 재사용하는 건 불가

  • 하나 바꾸려면 다 바꿔야함

3.2 약하게 결합된 코드 작성하기

불필요한 의존성 제거하기 or 코드 결합도 낮추기

3.2.1 의존성 주입하기

클래스의 이름을 통해 다른 클래스를 참조하는 의존성은 겉으로 보기에 문제가 없음

  • Wheel 클래스의 이름이 바뀌면 Gear의 gear_inches 메서드도 함께 변경

    • 사실 클래스 이름 변경되는 건 사소함(에디터 전체 변경하면됨)

class Gear {
  readonly cog: number;
  readonly chainRing: number;
  readonly rim: number;
  readonly tire: number;

  constructor(cog: number, chainRing: number, rim: number, tire: number) {
    this.cog = cog;
    this.chainRing = chainRing;
    this.rim = rim;
    this.tire = tire;
  }

  // 새로 추가된 함수
  gear_inches() {
    // 타이어는 바퀴테를 가싸고 있어서, 지름 계산시 타이어 높이에 2를 곱함
    return this.ratio * Wheel.new(rim, tire).diameter();
  }
}

진짜 문제는

  • Gear가 Wheel을 참조하는 부분을 gear_inches 메서드 속에 하드코딩 해놓았을 때

    • Gear는 Wheel 인스턴스의 gear_inches만을 계산하겠다고 명시적으로 선언

    • 즉, Gear의 gear_inches()는 다른 종류의 객체와 협업하기를 거부.

    • Wheel.new(rim, tire)가 하드코딩 되어있어서 diameter를 가진 다른 객체가 있어도 gear_inches() 사용 불가

중요한 것은 객체의 클래스가 무엇인지가 아닌 우리가 전송하는 메시지가 무엇인지이다

  • Gear는 대상이 되는 객체의 클래스가 무엇인지 알 필요 없음

  • gear_inches()를 계산하기 위해 Gear가 Wheel의 존재를 알 필요 없음

  • Gear에게는 diameter를 알고 있는 객체만 있으면 됨

class Gear {
  readonly cog: number;
  readonly chainRing: number;
  readonly wheel: object;

  constructor(cog: number, chainRing: number, wheel: object) {
    this.cog = cog;
    this.chainRing = chainRing;
    this.wheel = wheel;
  }

  // 새로 추가된 함수
  gear_inches() {
    // 타이어는 바퀴테를 가싸고 있어서, 지름 계산시 타이어 높이에 2를 곱함
    return this.ratio * Wheel.diameter();
  }
}

console.log(Gear.new(52, 11, Wheel.new(26, 1.5)).gear_inches());

Gear는 wheel 변수를 사용하고 wheel 메서드(getter)로 변수에 접근. 여기서 속지 말아야 할것

  • Gear는 wheel 객체가 Wheel 클래스의 인스턴스 라는 것을 알지 못하고 관심도 없음

  • Gear가 아는 것은 자기 자신이 diameter 메서드에 반응할 줄 아는 객체를 가지고 있는 것 뿐

위 코드 변화는 되게 작음. 하지만 엄청난 장점 있음

  • Wheel 인스턴스를 Gear 클래스 바깥에서 생성하기 떄문에 Gear와 WHeel 사이의 결합 없어짐

  • 이제 Gear는 diameter를 구현한 어떤 객체와도 협업 가능

위 기술을 의존성 주입이라고 부름. 이를 통해 얻은 것은

  • Wheel 초기화시 넘겨줘야 하는 인자와 인자의 순서에 대해 명시적 의존되고 있었음

  • 의존성 주입을 통해 모든 의존성이 diameter 메서드에 대한 단 1개의 의존성으로 줄어듬

  • 즉, Gear는 아는 것이 적어져서 더 똑똑해짐

정리하자면

  • 클래스의 이름을 알아야할 의무

  • 클래스에게 전송해야할 메시지를 알아야하는 의무

    이 2개의 의무는 다른 객체가 책임져야할 문제 였음

Last updated

Was this helpful?