Strategy Pattern
스트레티지, 혹은 전략 패턴은 코드가 실행되는 중인 런타임 환경 중, 객체를 바꾸는 패턴입니다.
일반적으로 상속을 사용할 경우, 코드 작성 단계에서 이미 부모 클래스를 상속받았으므로,
코드 실행 중 부모 클래스를 변경하는 것은 불가능하기에,
주로 동적으로 알고리즘(전략, 동작, 수행)을 바꾸기 위해 쓰입니다.
출저 - https://ko.wikipedia.org/wiki/%EC%A0%84%EB%9E%B5_%ED%8C%A8%ED%84%B4
전략 패턴 - 위키백과, 우리 모두의 백과사전
위키백과, 우리 모두의 백과사전. 전략 패턴(strategy pattern) 또는 정책 패턴(policy pattern)은 실행 중에 알고리즘을 선택할 수 있게 하는 행위 소프트웨어 디자인 패턴이다. 전략 패턴은 특정한 계열
ko.wikipedia.org
위 그림에서 Strategy는 각 전략들(Strategy1, Strategy2)의 추상화 인터페이스이며,
Strategy1, 2는 인터페이스인 Strategy가 구체화된 형태입니다.
Context는 해당 전략을 교체 및 수행합니다.
전략 패턴은 디자인 패턴의 꽃이라고도 불리며, 꽤 자주 쓰이는 패턴 중 하나입니다.
RPG 게임을 하나 가져와 예시를 하나 들어보겠습니다.
RPG 게임을 만들어보자.
캐릭터가 하나 있습니다. 아직 개발 초기 단계라 초보자 캐릭터밖에 없네요.
일반 공격 하나 밖에 없습니다.
전직 시스템 두두둥장!
앗, 그런데 게임을 점점 개발하다 보니 전직시스템을 넣고 싶어졌습니다.
전사 - 검
마법사 - 파이어볼
궁수 - 화살 뾱뾱
으로 기본공격을 바꾸려고 합니다!!!
캐릭터에 여러 공격(메소드)를 넣어 강력해진 캐릭터가 여러 공격을 하는 모습입니다.
하지만, 이 경우 문제가 있습니다.
바로, 전사 캐릭터는 비록 검 공격만 하고 있다곤 하나,
쓸모 없는 공격(메소드)인 bowAttack과 magicAttack 역시 가지고 있다는 점 입니다.
또한, 각각의 캐릭터가 서로 다른 메소드를 사용해 공격을 해야 합니다.
그렇다면, 모두 같은 메소드를 통해 공격을 하려면 어떻게 해야 할까요?
캐릭터의 직업을 지정하자! - State 패턴
state 패턴을 사용해 캐릭터의 상태를 생성과 동시에 넣어주고,
캐릭터의 상태에 따라 attack을 수행합니다.
비록 새 캐릭터가 생길 때 마다 attack에 새로운 코드를 추가해줘야 한다는 단점이 있긴 하지만,
그래도 그 정도는 감수할 수 있을 것 같아요! 코드가 한결 이뻐졌습니다!
예 뭐요...? 캐릭터가 100개가 넘는다고요?
캐릭터가 많아야 재밌다는 대표의 말에
졸지에 캐릭터를 100개나 더 만들게 생겼습니다!
일단 if - else if - else 혹은 switch가 최소 100줄은 잡아먹겠네요...ㅠㅠ
또한 기존의 캐릭터의 공격이 수정 혹은 새 캐릭터가 추가될 때 마다 Character 클래스의 코드는 수정 될 수 밖에 없습니다!
Charater가 무엇을 하는지 선택하는게 아니라, 무엇을 할지 건네줘 볼까?
각각의 캐릭터의 공격(검, 마법, 활)을 추상화한 Attack Interface를 만들고,
이를 implements 해 구체화한 Wizard, Warrior, Archer 만들었습니다.
Charater 클래스는 Attack Interface를 갖고 있고,
이를 동적으로 바꿔 각각 다른 공격을 구사합니다.
이제 캐릭터가 100개가 되더라도, Charater의 코드는 단 한 줄도 수정하지 않아도 됩니다!
즉, 클라이언트가 전략을 생성하여, 전략을 넘겨주는 것이지요!
이는 구체화된 클래스에 의존하는 것이 아닌, 추상화에 의존해야 한다는 DIP(의존 역전 원칙) 과도 통합니다.
비록 객체지향 프로그래밍의 특성 상, 소스 파일의 수가 늘어난다는 단점이 있긴 하지만,
유지보수 및 추가기능을 개발할 때 얻게될 이점에 비하면 그리 큰 문제는 아닙니다.
Template Callback Pattern
템블릿 콜백 패턴은 이러한 전략 패턴의 변형 패턴입니다.
전략 패턴과 꽤 비슷하지만, Warrior, Wizard, Aucher와 같이 클래스를 생성하지 않고,
익명 내부 클래스를 만들어 사용한다는 점이 있습니다.
클래스를 따로 정의하지 않아도, 이름이 없는 클래스,
즉 익명 내부 클래스를 만들어 즉석에서 메소드를 오버라이딩 합니다.
코틀린에서는 오버라이딩 해야할 메소드가 하나일 때엔 소괄호와 메소드 이름을 생략할 수 있기 때문에
위와 같이 매우 깔끔하게 표현할 수 있습니다.
어 이거 어디서 많이 본 형태인데?
안드로이드, 웹 개발 혹은 그 외 버튼 클릭 리스너들을 다뤄보신 분들이라면 흠칫하실 겁니다.
Button btn = (Button) findViewById(R.id.btn);
btn.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "버튼 눌림", Toast.LENGTH_SHORT).show();
}
}) ;
안드로이드의 setClickListener... Template Callback Pattern...?!
맞습니다.
안드로이드 혹은 다른 프레임워크에서 버튼 클릭 리스너, 키보드 클릭 리스너 등은
Strategy + Template Callback Pattern (Strategy 파생 패턴) + Observer 패턴을 혼합한 형태로 볼 수 있을 것 같아요.
Observer 패턴 혹은 구독 - 발행 패턴은 특정 객체를 관찰(Observe, Subscribe) 하고 있다가,
객체의 상태가 변화하면 의존된 다른 객체에게 자동으로 알림을 발송하는 패턴입니다.
버튼 클릭을 감지하는 onButtonClickListener 등은 옵저버 패턴의 일부로 볼 수 있으며,
버튼이 눌리는지 관측(Observe) 하고 있다가,
버튼이 클릭된다면 onClick() 메소드를 실행하는데,
onClick()을 호출했을 때, 작성된 로직을 수행되는 부분은 Template Callback Pattern으로 볼 수 있습니다.
물론, OnClickListener를 implements 하여 CustomOnClickListener를 만들어 넘겨줄 수도 있으며,
이 역시 전략 패턴에 해당한다고 볼 수 있을 것 같습니다.
마치며
스프링을 배우기 위해 스프링의 객체지향 관련 책을 보다가,
디자인 패턴과 관련된 파트에서, 유명한 디자인 패턴인 Strategy 패턴을 보게되어 포스팅해봤습니다.
안드로이드, 자바스크립트, 그 외의 여러 리스너들은 마냥 Observer 패턴에 기반을 두고 있다 정도로만 알고 있었는데,
Strategy를 포함해 여러 디자인 패턴을 혼합한 것 같네요.
디자인 패턴은 단독으로 쓰일 수도 있지만
꼭 하나만 해야 하는 게 아니라, 다른 패턴들과 혼합해 사용할 수도 있다는 것도 자연스럽게 배운 것 같습니다.
스프링을 배우려다가 안드로이드의 Listener 까지 공부해버렸네요.
공부하면 할 수록, 스프링과 안드로이드를 포함해 여러 라이브러리와 프레임워크의 설계는 너무나도 놀랍습니다.
정말 잘 설계되어 있고, 수많은 고민과 디자인 패턴, 노하우의 집약체인 것 같아요.
전체 코드는 아래 Github를 참고해주세요.
https://github.com/yoon6763/design-pattern
GitHub - yoon6763/design-pattern: 재밌지만 심오한 디자인 패턴의 세계.
재밌지만 심오한 디자인 패턴의 세계. Contribute to yoon6763/design-pattern development by creating an account on GitHub.
github.com
'CS 지식 > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] Adaptor(어댑터) 패턴 (1) | 2024.05.08 |
---|---|
[디자인 패턴] Singleton(싱글톤) 패턴 (1) | 2024.03.16 |
[디자인 패턴] Factory(팩토리) 패턴 (0) | 2024.03.10 |
[디자인 패턴] Decorator (데코레이터) 패턴 (1) | 2023.09.12 |
[디자인 패턴] Observer(옵저버) 패턴 (0) | 2023.09.08 |