전략 패턴이란?
알고리즘군을 정의하고 캡슐화해서 각각의 알고리즘군을 수정해서 쓸 수 있게 한다
클라이언트로부터 알고리즘을 분리해서 독립적으로 변경할 수 있다
처음 정의를 보고 무슨 말인지 이해를 못했다
역시 코드를 직접 쳐보고 예제를 리팩토링하면서 공부하는게 가장 빠르게 습득하고 이해하는 지름길인 것 같다
처음엔 각각의 클래스로 구현
public class Bin {
void eat() {
System.out.println("돈까스!");
}
}
public class Jin {
void eat() {
System.out.println("돈까스!");
}
}
public class Anna {
void eat() {
System.out.println("돈까스!");
}
}
상속을 이용한 서브클래스 확장
- 친구들은 각각 다른 돈까스를 먹고싶다는 문제가 생겼고 이를 해결하기 위해 상속을 이용했다
- Person 으로 분리하고 추상 메소드를 활용해 각 클래스에서 구현하도록 했다
- 여기서 문제가 하나 생긴다 코드를 보고 한번씩 생각을 해봤으면 좋겠습니다
public abstract class Person {
void say() {
System.out.println("배고파");
}
abstract void eat();
}
public class Bin extends Person {
@Override
void eat() {System.out.println("그냥 돈까스!");}
}
public class Jin extends Person {
@Override
void eat() {System.out.println("치즈 돈까스!");}
}
public class Anna extends Person {
@Override
void eat() {System.out.println("고구마 돈까스!");}
}
- 문제는 Person 클래스의 say() 라는 메소드다 친구들이 항상 배고프다는 문제가 생겨버린다
- say() 오버라이딩을 해서 각 서브클래스에서 배가 고프지 않다고 구현을 해도 되지만
- 매번 구현하는 클래스가 어떨지 생각해야하고 어떤 돈까스를 먹는지 고민하고 하는 문제를 또 만나게 될 것이다
인터페이스를 활용한 분리
public interface SayStrategy {
void say();
}
public class HungryStrategy implements SayStrategy {
@Override
public void say() {System.out.println("배고파");}
}
public class NotHungryStrategy implements SayStrategy {
@Override
public void say() {System.out.println("배 안고파!");}
}
public abstract class Person {
protected SayStrategy sayStrategy;
void say() {
sayStrategy.say();
}
abstract void eat();
}
- 이렇게 분리를 하면 Person 을 상속하는 서브 클래스들 생성자에서 배고픈지 아닌지 전략별로 갈아 끼울 수 있다
public class Anna extends Person {
public Anna() {
sayStrategy = new HungryStrategy();
sayStrategy = new NotHungryStrategy();
}
...
}
동적으로 변경하기
- 결국 각 구현체 생성자 안에서 전략인터페이스를 구현한 클래스를 고정시켜서 사용했다
- 동적으로 활용할 수 있게끔 리팩토링을 해보자
public abstract class Person {
void setSayStrategy(SayStrategy sayStrategy) {
this.sayStrategy = sayStrategy;
}
}
- 그리고 서브 클래스들은 처음엔 NotHungryStrategy 를 할당해준다
- 배가 안고프다가 배가 고파진다면 그때 HungryStrategy 를 할당하는 동적인 코드가 완성되었다
package strategypattern;
public class MainApplication {
public static void main(String[] args) {
Person bin = new Bin();
bin.say();
System.out.println();
Person jin = new Jin();
jin.say();
jin.setSayStrategy(new HungryStrategy());
jin.say();
jin.eat();
System.out.println();
Person anna = new Anna();
anna.say();
anna.setSayStrategy(new HungryStrategy());
anna.say();
anna.eat();
}
}
출력결과--------------------------------------------------
배 안고파!
배 안고파!
배고파
치즈 돈까스!
배 안고파!
배고파
고구마 돈까스!
Bin 은 처음에 모든 클래스에 NotHungryStrategy를 할당했기에 배가 고프지 않다
Jin 과 Anna 도 처음엔 배가 고프지 않았지만 배고픈 전략을 동적으로 할당해서 배고픈 상태가 되었고
각자가 먹고 싶은 eat() 을 호출한다
📝 요약 정리
처음에 언급했던 알고리즘군은 위에서 전략을 구현하는 클래스들로 이해하면 된다. NotHungryStrategy, HungryStrategy 이 클래스들은 각각 알고리즘이고 이 클래스들을 합친 것을 알고리즘군 이라 한다.
모든 곳곳에 전략 패턴을 사용하면 좋을까? 당연히 아니라고 본다. 알고리즘군이 작으면 그냥 if-else 로 나눠서 분기처리 하는 것이 더 편하다고 생각한다. 하지만 알고리즘군이 크고 다양하게 구현이 될 수 있다면 전략패턴을 사용할 적절한 기회일 것 같다고 느껴졌다.
전략 패턴이란 무엇인가 질문이 들어온다면은 이렇게 대답을 할 것 같다
- 요구사항이 바뀌었을 때 기존 코드를 수정하지 않아도 된다는 장점이 있다
- 전략 패턴의 의도는 구현체를 요리조리 갈아끼우는 것이다
리팩토링을 하면서 디자인 패턴의 중요성을 깨달았고 패턴을 공부하면서 적용시키니 코딩이 즐겁고 행복하다
여기서 eat 메소드는 따로 패턴을 적용시키지 않았다. 이 글을 읽는 사람들이 직접 돈가스 종류별로 구현하고 만들어보면 좋을 것 같다
예제 코드 - https://github.com/wugawuga/design-patterns/tree/main/src/strategypattern
'dev > 🧩 디자인패턴' 카테고리의 다른 글
팩토리 패턴이란? (Factory Pattern) (0) | 2022.11.08 |
---|---|
싱글턴 패턴 - 동시성 문제 해결 (0) | 2022.10.25 |
싱글턴 패턴이란? (Singleton Pattern) (0) | 2022.10.25 |
MVC1 과 MVC2 차이 그리고 Spring MVC 란? (0) | 2022.05.27 |
MVC 패턴이란? (Model-View-Controller) (0) | 2022.05.27 |