ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [디자인패턴] 전략 패턴
    기타/북스터디 2022. 7. 10. 12:46

    오늘은 디자인패턴 중 하나인 전략 패턴에 대해 정리해보려고 한다.

     

    전략 패턴이란, 알고리즘군을 정의하고 캡슐화해서 각가의 알고리즘군을 수정해서 쓸 수 있게 해주는 패턴으로 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경을 할 수 있게 해준다.

     

    전략 패턴을 적용하는 과정을 아래 예시를 보며 이해해보자.

     

    여러 종류의 캐릭터가 존재하는 액션 어드벤쳐 게임을 만든다고 생각해보자.

    너무 상세하게 설계과정을 나열하다보면 글이 길어질 수 있어 시작은 추상클래스로 공통된 부분을 캡슐화하고 상속받는 구조로 시작하자.

    여기까지는 Character라는 슈퍼클래스를 상속받는 Knight와 Queen이라는 서브클래스가 있는 구조이고 기능을 추가하기 위해 무기를 가지고 싸우는 행동을 추가하기로 했다.

    무기를 가지고 싸우는 행동을 fight()라는 메서드로 네이밍하고 모든 캐릭터가 싸울 수 있기 때문에 슈퍼클래스인 Character에 fight()메서드를 추가해주면 간단하게 해결할 수 있다.

     

    하지만, 캐릭터 중에는 무기를 가지고 싸울 수 없는 캐릭터(전략가)가 존재하였고 해당 캐릭터가 Character클래스를 상속받고 있어서 fight()메서드를 실행시킬 수 있는 구조가 되었다.

    이를 해결하기 위해서는 전략가(무기를 가지고 싸울 수 없는 캐릭터)클래서에서 fight()를 오버라이드하여 아무 것도 실행되지 않게 해주면 된다.

    하지만, fight()를 오버라이드하여 사용하기에는 추후에 유지보수하기에는 번거로운 일이다.

    각각의 캐릭터의 fight() 행동은 기획이 변경될 때마다 Character의 서브클래스에서 일일이 fight() 메서드를 찾아보고 오버라이드해줘야하는 문제가 있다.

     

    그러면 두번째 방법으로 인터페이스를 사용해보는 것은 어떨까?

    무기를 가지고 싸울 수 있는 캐릭터는 Fightable이라는 인터페이스를 구현하도록 하면 무기를 가지고 싸울 수 없는 캐릭터는 굳이 fight()를 오버라이드할 필요성이 없어진다.

    하지만 이 방법도 문제점이 존재한다.

    각가의 캐릭터가 모두 fight()라는 행동이 다르다면 괜찮은 방법일 수 있지만 동일한 fight()행동을 하는 캐릭터가 여럿이면 중복코드가 발생하는 문제가 존재한다.

     

     

    이러한 상황에서 떠올릴 수 있는 원칙이 "애플리케이션에서 달라지는 부분을 찾아내고 달라지지 않는 부분과 분리한다."이다.

    즉, 코드에 새로운 요구 사항이 있을 때마다 바뀌는 부분이 있다면 분리해서 캡슐화해야한다.

    이러한 원칙을 적용하면 나중에 바뀌지 않는 부분에는 영향을 미치지 않고 그 부분만 고치거나 확장할 수 있다.

     

    이 원칙을 가지고 다시 캐릭터 설계로 돌아가보자.

    Character클래스에는 fight()를 제외하면 자주 달라지거나 바뀌지 않는다.

    (물론, display()라는 캐릭터의 모습을 나타내는 것도 자주 달라진다고 생각하겠지만 일단은 논외로 하자.)

    애플리케이션에서 달라지는 부분을 찾아냈으므로 변화하는 부분과 그대로 있는 부분을 분리하면 된다.

     

    이제 싸우는 행동을 구현하는 클래스 집합을 디자인하면 된다.

    최대한 유연하게 그리고 Character의 인스턴스에 행동을 할당할 수 있게 그리고 캐릭터의 싸우는 행동을 동적으로 바꿀 수 있게

    여기서 사용할 수 있는 방법은 FightBehavior라는 인터페이스를 만들어 주고 이 인터페이스를 구현하는 구체적인 행동 클래스를 만들어주는 것이다.

    싸우는 행동은 이제 Character클래스에서 구현하지 않고 특정 행동만을 목적으로 하는 클래스 집합을 만들었다.

    이전의 방법(슈퍼클래스에서 행동을 구현하고 서브클래스에서 오버라이드해서 사용)은 코드를 추가하지 않는 이상 행동을 변경할 여지가 없다는 것이다.

    하지만, 이렇게 특정 행동을 구현하는 것을 그 목적만을 가진 클래스 집합을 만들어주고 Character클래스에서는 해당 행동을 필드로 할당해주면된다.

    그러면 행동을 변경(FightWithKnife에서 FightWithAxe로)하고 싶으면 어떻게 하면 될까?

    간단한 방법으로 setter를 선언해주면 코드를 추가하지않고 동적으로 행동을 변경할 수 있게 된다.

     

    결론적으로 아래와 같은 구조를 가지게 된다.

    이렇게 특정 행동을 클래스 집합으로 만들어줌으로써 중복 코드를 제거할 수 있고 동적으로 행동을 변경할 수 있게 되었다.

    이러한 디자인 패턴을 전략 패턴이라고 한다.

     

    전략 패턴을 배우면서 느낀 점은 처음부터 이렇게 설계가 가능할까?이다.

    지금의 나였으면 처음에 설계한 슈퍼클래스를 선언해서 행동들을 추상 메서드나 구현 메서드로 만들어서 오버라이드해주는 구조를 가져갔을 것같다.

    추후에 소프트웨어가 어떠한 변화든 이루어질 수 있다는 생각을 가지고 지금부터 배우는 디자인패턴을 하나씩 적용해볼 수 있는 것이 중요한 것같다.

    처음에는 유지보수가 힘들고 유연성이 떨어지는 코드를 짰더라도 다시 리팩토링 해보는 과정에서 내가 지금부터 배우는 디자인 패턴을 떠올려 좋은 코드를 만들어가고 싶다는 생각이 들었다.

    댓글

Designed by Tistory.