mojo's Blog

Strategy Pattern 본문

Design Patterns

Strategy Pattern

_mojo_ 2024. 6. 6. 11:50

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
Comments