designPattern) 전략패턴(Strategy Pattern) with typescript

junny·2023년 4월 21일
0

designPattern

목록 보기
1/4
post-thumbnail

정의

  • 알고리즘군을 정의하고 캡슐화 해서 각각의 알고리즘군을 수정해서 쓰는 패턴

왜 사용할까?

  • 클라이언트로 부터 알고리즘을 분리해서 독립적으로 변경할 수 있게 해주기 때문

그럼 어떻게 쓸까?

  • 정의만 보면 무슨 말인지 어려우니까 예시를 통해서 살펴보자

진짜 왜 쓸까?

  • 가정) 나는 고양이 가상화해서 만드는 전문 회사에 다니는 개발자이다
  • 우리회사에는 처음 이런 구성으로 고양이를 만들었다.
abstract class Cat {
  cute() {
    console.log("모든 고양이는 귀엽습니다.");
  }
  abstract display(): void;

  sound() {
    console.log("미야옹");
  }

}

class 삼색이 extends Cat {
  display(): void {
    console.log("3가지 색깔의 털을 가진 고양이");
  }
}

class 검은고양이 extends Cat {
  display(): void {
    console.log("검은 털을 가진 고양이");
  }
}

회사가 성장하면서 엄청 많은 고양이 들과 배를 만지는 행동이 추가 된다.

  • 삼색이는 미야옹소리를 내고 배를 만지면 싫어함
  • 검은고양이는 인형이여서 소리도 안내고 배를 만져도 반응 없음
  • 서당고양이는 한국말을하고 배를 만지면 좋아함
  • 어학당고양이는 영어를하고 배를 만지면 싫어함

문제점

이런경우 상속을 통해 구현한다고 생각하면

  • 추상 클레스에 함수 넣어주고 또 추상클레스를 상속받는 클레스들을 다 수정해줘야하는 일이 생긴다.
  • 울지 않는 고양이는 어떻게 만들것인가

이때 사용하면 좋은게 전략 패턴!

  • 추상클레스에서 변하는 부분을 캡슐화 하자 즉 들어내자
  • 여기서 변하는건 sound, 또 추가될 배를 만지는 행동(touchBelly)
    디자인 원칙을 지켜서
  • 객체 지향 원리상 구체보단 인터페이스에 의존시킨다
  • 상속보단 구성(composition)을 활용해 다형성을 갖게 한다

어떻게 할지 미리 구성도를 통해서 그려보면

그럼 변하는 부분을 인터페이스를 만들자

interface CatSound {
  sound(): void;
}

interface TouchBellyBehavior {
  touchBelly(): void;
}

그럼 구성 방법을 이용하자!
dependency injection을 이용해 CatSound, TouchBellyBehavior을 주입시켜 주자

abstract class Cat {
  constructor(
    private readonly catSound: CatSound,
    private readonly touchBellyBehavior: TouchBellyBehavior
  ) {}
  cute() {
    console.log("모든 고양이는 귀엽습니다.");
  }
  abstract display(): void;

  sound() {
    this.catSound.sound();
  }

  touchBelly() {
    this.touchBellyBehavior.touchBelly();
  }
}

CatSound 인터페이스를 구현

class 미야옹고양이 implements CatSound {
  sound(): void {
    console.log("미야옹");
  }
}

class 한국말고양이 implements CatSound {
  sound(): void {
    console.log("나랏말싸미 듕귁에 달아");
  }
}

class 영어하는고양이 implements CatSound {
  sound(): void {
    console.log("hello world!");
  }
}

class 인형고양이 implements CatSound {
  sound(): void {
    console.log("....");
  }
}

TouchBellyBehavior 인터페이스를 구현

class 배만지는걸싫어하는고양이 implements TouchBellyBehavior {
  touchBelly(): void {
    console.log("냥냥펀치!");
  }
}

class 배만지는걸좋아하는고양이 implements TouchBellyBehavior {
  touchBelly(): void {
    console.log("그릉그릉그릉");
  }
}

class 배만지면반응없는고양이 implements TouchBellyBehavior {
  touchBelly(): void {
    console.log(".....");
  }
}

이제 추상클레스 cat을 상속받는 고양이들을 만들어주자


class 삼색이 extends Cat {
  display(): void {
    console.log("3가지 색깔의 털을 가진 고양이");
  }
}

class 검은인형고양이 extends Cat {
  display(): void {
    console.log("검은 털을 가진 고양이");
  }
}

class 서당고양이 extends Cat {
  display(): void {
    console.log("갈색과 흰색이 섞인 고양이");
  }
}

class 어학당고양이 extends Cat {
  display(): void {
    console.log("긴 흰털을 가진 고양이");
  }
}

지금 만든걸 이름을 지어서 가상공간에 만들어서 풀어놓으면

const 나비 = new 삼색이(new 미야옹고양이(), new 배만지는걸싫어하는고양이());
const 내로 = new 검은인형고양이(new 인형고양이(), new 배만지면반응없는고양이());
const 한국냐옹이 = new 서당고양이(
  new 한국말고양이(),
  new 배만지는걸좋아하는고양이()
);
const 영국고양이 = new 어학당고양이(
  new 영어하는고양이(),
  new 배만지는걸싫어하는고양이()
);

[나비, 내로, 한국냐옹이, 영국고양이].forEach((고양이) => {
  고양이.cute();
  console.log(">>>>>고양이 생김새");
  고양이.display();
  console.log(">>>>>고양이가 소리를 낸다.");
  고양이.sound();
  console.log(">>>>>배를 만져준다!!");
  고양이.touchBelly();
});

우리의 가상 공간에는


모든 고양이는 귀엽습니다.
>>>>>고양이 생김새
3가지 색깔의 털을 가진 고양이
>>>>>고양이가 소리를 낸다.
미야옹
>>>>>배를 만져준다!!
냥냥펀치!
모든 고양이는 귀엽습니다.
>>>>>고양이 생김새
검은 털을 가진 고양이
>>>>>고양이가 소리를 낸다.
....
>>>>>배를 만져준다!!
.....
모든 고양이는 귀엽습니다.
>>>>>고양이 생김새
갈색과 흰색이 섞인 고양이
>>>>>고양이가 소리를 낸다.
나랏말싸미 듕귁에 달아
>>>>>배를 만져준다!!
그릉그릉그릉
모든 고양이는 귀엽습니다.
>>>>>고양이 생김새
긴 흰털을 가진 고양이
>>>>>고양이가 소리를 낸다.
hello world!
>>>>>배를 만져준다!!
냥냥펀치!

와 확장성 있는 고양이 들이 완성이 되었다!!!

정리!

  • 고양이의 행동에 대한 알고리즘을 정의하고 캡슐화하고 이를 수정했다.
    결과 -> 객체가 독립성을 갖게 되어 재사용성이 증가했다

느낀점

  • 객체지향의 모든 속성을 보여주고 기본이 되는 패턴인거 같다!
profile
오히려 좋아!

0개의 댓글