전략 패턴 (Strategy Parttern)
전략 패턴이란 객체들이 할 수 있는 행위에 대해 전략 클래스를 생성하고 유사한 행위들을 캡슐화하는 인터페이스를 정의하여 객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략만 바꿔 행위를 유연하게 확장하는 방법을 의미한다.
헤드 퍼스트 디자인 패턴 책에서는 이를 오리게임에 비유하여 설명한다.
상황
모든 오리가 "꽥" 소리를 내고 수영을 할 수 있기에 슈퍼클래스로 작성한다. 오리마다 생김새는 다르기 때문에 display는 오버라이드 한다.
슈퍼클래스에 fly() 기능을 추가하고 RubberDuck(고무 오리) 클래스도 추가하였다.
Duck의 몇몇 서브 클래스만 날 수 있을 뿐 고무오리는 날지 못한다. 하지만 슈퍼 클래스에 fly()을 작성하였기 때문에 고무오리가 날아다니는 오류가 발생했다. 따라서 fly() 함수를 오버라이드 하여 아무것도 하지 않도록 조정했다. 하지만 이렇게 상속하게 되면 다음과 같은 문제가 발생한다.
- 서브 클래스에 코드가 중복된다.
- 모든 오리의 행동을 알기 힘들다.
- 실행 시에 특징을 바꾸기 힘들다.
- 코드를 변경했을 때, 다른 오리들(서브 클래스)에게 원치 않은 영향을 끼칠 수 있다.
즉 상속을 계속 활용하게 된다면 규격이 바뀔 때마다 프로그램에 추가했던 Duck 서브 클래스 fly()와 quack() 메서드를 일일이 살펴보고 상황에 따라 오버라이드 하여야 한다. 그래서 고민해 본 결과 Flyable과 Quackable 인터페이스를 만들어서 해당 기능을 원하는 오리에게만 넣어서 사용하기로 했다.
하지만 이 또한 코드의 중복이 발생했고, 날아가는 동작을 바꾸기 위해서 Duck의 서브 클래스에서 날아다니는 모든 코드를 고쳐야 했다.
해결방안
헤드 퍼스트 책에서 제시하는 디자인 원칙에서는 다음과 같이 이야기한다.
"애플리케이션에서 달라지는 부분을 찾아내고, 달라지지 않은 부분과 분리한다. 달라지는 부분을 찾아서 나머지 코드에 영향을 주지 않도록 캡슐화하게 되면 코드를 변경하는 과정에서 의도치 않게 발생하는 일을 줄이면서 시스템의 유연성을 향상할 수 있다.
즉 바뀌는 부분과 그렇지 않은 부분을 분리하여 캡슐화하라는 이야기인데, 이를 이용해서 fly()와 quack()을 Duck클래스로부터 분리하기 위해 각 행동을 나타낼 클래스 집합을 새로 만든다.
각 행동은 인터페이스로 표현하고 이를 인터페이스를 사용하여 행동으로 구현한다. 이렇게 진행되면 더 이상 Duck클래스에서 행동과 꽥꽥 소리를 구현하지 않게 된다. 대신 특정 행동만을 목적으로 하는 클래스 집합을 만들 수 있게 된다.
인터페이스에 맞춰서 프로그래밍 한다는 말은 사실 상위 형식에 맞춰 프로그래밍한다는 말과 같다. 즉 변수를 선언할 때, 추상 클래스나 인터페이스 같은 상위 형식으로 선언해야 한다. 객체를 변수에 대입할 때 상위 형식을 구체적으로 구현한 형식이라면 어떤 객체든 넣을 수 있기 때문이다. 그러면 변수를 선언하는 클래스에서 실체 객체의 형식을 몰라도 된다.
이런 식으로 디자인 하면, 다른 형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용할 수 있다. 그리고 기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 Duck 클래스를 전혀 건드리지 않고 새로운 행동을 추가할 수 있다.
여기서 가장 중요한 점은 나는 행동과 꽥꽥 거리는 행동을 Duck 클래스에서 정의한 메서드를 써서 구현하지 않고 다른 클래스에 위임하여 처리한다는 점이다.
클라이언트에서는 나는 행동과 꽥꽥거리는 행동을 캡슐화된 알고리즘으로 구현한다. 각 오리에는 FlyBehavior와 QuackBehavior가 있으며 나는 행동과 꽥꽥거리는 행동을 위임받는다. 이런 식으로 두 클래스를 합치는 것을 구성(Composition)을 이용한다고 한다.
참조 :
'Computer Theory > Design partterns' 카테고리의 다른 글
디자인 패턴과 SOLID 원칙 (0) | 2024.05.22 |
---|---|
팩토리 패턴 (0) | 2023.09.20 |
데코레이터 패턴 (0) | 2023.09.18 |
옵저버 패턴 (0) | 2023.09.04 |