mojo's Blog
Strategy Pattern 본문
Strategy Pattern
목적 : 특정 동작을 수행하기 위해 스왑할 수 있는 캡슐화된 알고리즘 집합을 정의
언제 사용할지
- 알고리즘의 여러 버전이 필요함
- 클래스의 동작은 런타임에 정의되어야 함
- 조건문은 복잡하고 유지하기 어려움
※ 초기 디자인
public abstract class Dog {
public void bark() { // barking impl }
public void walk() { // walking impl }
public abstract void display();
}
요구사항
- 모든 개는 짖어야 함
- 모든 개는 같은 길에서 걸어야 함
- 모든 개는 각자 소유한 "display" 구현이 필요함
위와 같은 디자인은 괜찮다.
하지만 아래와 같은 추가 요구사항을 받았으며, 위 클래스에 추가한다면?
=> "개는 하늘을 날 수 있다."
※ 변경 디자인
public abstract class Dog {
public void bark() { // barking impl }
public void walk() { // walking impl }
public void fly() { // flying impl }
public abstract void display();
}
위와 같이 변경하면서 생긴 문제점
=> 재사용을 목적으로 상속을 많이 상속하는 것은 유지 관리에 있어 잘 나타나지 않게됨
기존 요구사항을 상속한 클래스들은 fly 메서드를 사용하지 않았기 때문에,
위와 같은 디자인은 코드 재사용성이 떨어지게 된다.
재사용성을 높이기 위해 인터페이스를 추가하여 디자인을 하도록 한다.
※ 인터페이스를 추가한 디자인
public abstract class Dog {
FlyBehavior flyBehavior;
public void performFly() {
flyBehavior.fly();
}
public void bark() {
System.out.println("All dogs bark!");
}
public void walk() {
System.out.println("All dogs walk!");
}
public abstract void display();
}
FlyBehavior, BarkBehavior 은 인터페이스이며 object composition 을 활용하여
performFly, performBark 메서드에서 객체에게 행동을 위임하도록 설계하였다.
개가 하늘을 나는 경우와 날지 못하는 경우에 대한 인터페이스 구현은 아래와 같이 하면 된다.
public interface FlyBehavior {
public void fly();
}
// 개가 하늘을 나는 경우
public class FlyWithWings implements FlyBehavior {
public void fly() {
System.out.println("I can fly!");
}
}
// 개가 하늘을 날지 못하는 경우
public class FlyWithoutWings implements FlyBehavior {
public void fly() {
System.out.println("I can't fly!");
}
}
이제 Dog 클래스를 상속한 새로운 클래스를 만들어보도록 한다.
하늘을 나는 개와 하늘을 날지 못하는 개를 위 클래스를 활용하여 객체에 할당하도록 한다.
// 하늘을 나는 DogA
public class DogA extends Dog {
public DogA() {
flyBehavior = new FlyWithWings();
}
public void display() {
System.out.println("I'm a DogA");
}
}
// 하늘을 날지 못하는 DogB
public class DogB extends Dog {
public DogB() {
flyBehavior = new FlyWithoutWings();
}
public void display() {
System.out.println("I'm a DogB");
}
}
이러한 구조를 그림으로 나타내면 아래와 같다.
※ 디자인 원칙
- 클래스는 다형성 동작과 코드 재사용을 달성해야 한다.
- 클래스 상속보다는 유리한 객체를 구성한다.
- 대부분의 디자인 패턴은 상속보다는 객체 구성을 강조한다.
※ Class Inheritance
- 하위 클래스 구현은 부모 클래스의 구현 측면에서 정의
- 부모 클래스 구현은 종종 하위 클래스에서 볼 수 있음
- While-box reuse
장점: 컴파일 시 수행되며 사용하기 쉬움
단점: 하위 클래스는 상위 클래스 구현에 의존하며, 실행 시 상속된 구현을 변경할 수 없음
※ Object Composition
- 객체는 보다 복잡한 기능을 달성하도록 구성함
- 객체의 내부를 알 수 없기 때문에, 잘 정의된 인터페이스가 필요함
- 다른 객체에 대한 참조를 활용하여 런타임에 동적으로 기능을 획득함
- Black-box reuse
장점: 런타임에 구현 교체가 가능하며, 구현 종속성이 감소됨
단점: 이해하기 어려움 (어떤 클래스의 객체인지 알 수 없어서)
※ The Strategy Pattern
- 알고리즘의 패밀리를 정의함
- 각각을 캡슐화하고, 그것들을 상호 교환할 수 있도록 만듬
- 알고리즘을 사용하는 클라이언트와 독립적으로 변경할 수 있음
'Design Patterns' 카테고리의 다른 글
Template Method Pattern (0) | 2024.06.06 |
---|---|
Observer Pattern (1) | 2024.06.06 |
GRASP (1) | 2024.05.15 |
SOLID 원칙 (0) | 2024.05.06 |
객체지향 패러다임 (0) | 2024.05.06 |