본문 바로가기
DesignPattern

[Design Pattern] Strategy Pattern

by 봄석 2019. 10. 11.

[Design Pattern] Strategy Pattern

 

Strategy Pattern이란?

 

객체들이 할 수 있는 행위 각각에 대해 전략 클래스를 생성하고

유사한 행위들을 캡슐화하는 인터페이스를 정의합니다.

 

객체의 행위를 동적으로 바꾸고 싶은 경우 직접 행위를 수정하지 않고 전략을 바꿔주기만 함으로써

행위를 유연하게 확장하는 방법을 말합니다.

 

즉 , 객체가 할 수 있는 행위들을 각각의 전략으로 정의, 수정이 필요한 경우 전략을 바꾸는 것만으로도

수정이 가능하도록 만드는 패턴입니다.

 

 

 

 

 

전략 패턴을 사용해야하는 이유

전략패턴을 적용하지 않은 예를 보도록 하겠습니다.

위와 같은 관계의 클래스 구조가 있습니다.

 

Strategy Pattern with Java

public interface Movable {
    public void move();
}
public class Train implements Movable{
    public void move(){
        System.out.println("선로를 통해 이동");
    }
}
public class Bus implements Movable{
    public void move(){
        System.out.println("도로를 통해 이동");
    }
}
public class Client {
    public static void main(String args[]){
        Movable train = new Train();
        Movable bus = new Bus();

        train.move();
        bus.move();
    }
}

기차는 선로를 따라 이동하고, 버스는 도로를 따라 이동합니다.

그러다 시간이 흘러 선로를 따라 움직이는 버스가 개발되었다고 가정해봅시다.

 

그러면 Bus move() 메서드를 다음과 같이 바꿔주기면 하면 끝나게 됩니다.

아래처럼 말이죠

public void move(){
    System.out.println("선로를 따라 이동");
}

 

그런데,

이렇게 수정하는 방식은 SOLID의 원칙 중 OCP( Open-Closed Principle )에 위배됩니다.

OCP에 의하면 기존의 move()를 수정하지 않으면서 행위가 수정되어야 하지만, 지금은 Bus move() 메서드를 직접 수정하게 됩니다

 

또한 지금과 같은 방식의 변경은 시스템이 확장이 되었을 때 유지보수를 어렵게 합니다.

 

예를 들어, 버스와 같이 도로를 따라 움직이는 택시, 자가용, 고속버스, 오토바이 등이 추가된다고 할 때, 모두 버스와 같이 move() 메서드를 사용합니다.

만약에 새로 개발된 선로를 따라 움직이는 버스와 같이, 선로를 따라 움직이는 택시, 자가용, 고속버스... 등이 생긴다면,

택시, 자가용, 고속버스의 move() 메서드를 일일이 수정해야 할뿐더러, 같은 메서드를 여러 클래스에서 똑같이 정의하고 있으므로 메서드의 중복이 발생하고 있습니다.

 

 

위 같은 문제를 해결하기 위해 Strategy pattern을 사용할 수 있습니다.

 

 

 

 

Strategy Pattern 적용하기

위의 예에서 선로를 따라가는 여러 Vehicle들이 개발될 수 있다는 것을 가정하고

유연하게 변경 가능할 수 있도록 전략 패턴을 적용해보도록 하겠습니다.

 

 

Strategy Pattern with Java

 

전략 정의하기

public interface MovableStrategy {
    public void move();
}
public class LoadStrategy implements MovableStrategy {
    @Override
    public void move() {
        System.out.println("도로를 통해 이동");
    }
}
public class RailLoadStrategy implements MovableStrategy {
    @Override
    public void move() {
        System.out.println("선로를 통해 이동");
    }
}

 

운송수단 선언

public class Moving {
    private MovableStrategy movableStrategy;

    public void move() {
        movableStrategy.move();
    }

    public void setMovableStrategy(MovableStrategy movableStrategy) {
        this.movableStrategy = movableStrategy;
    }
}
public class Bus extends Moving {

}
public class Train extends Moving {
}

 

result

public class Client {
    public static void main(String[] args) {
        Moving train = new Train();
        Moving bus = new Bus();

        train.setMovableStrategy(new RailLoadStrategy());
        bus.setMovableStrategy(new LoadStrategy());

        train.move();
        bus.move();


        /** rail load bus development*/
        bus.setMovableStrategy(new RailLoadStrategy());
        bus.move();
    }
}

어떻게 Vehicle을 움직일 것인지 전략을 setMovableStrategy() 메서드로 설정합니다.

 

 

 

Strategy Pattern with Kotlin

printer ex

 

Kotlin에서는 고차 함수를 이용하여 전략 패턴을 더 쉽게 구현할 수 있습니다.

class Printer(private val stringFormatterStrategy: (String) -> String) {

    fun printString(string: String) {
        println(stringFormatterStrategy(string))
    }
}

Printer는 stringFormmatterStrategy 함수를 생성자로 전달받습니다.

class StrategyTest {

    val lowerCaseFormatter: (String) -> String = { it.toLowerCase() }
    val upperCaseFormatter = { it: String -> it.toUpperCase() }
    
    @Test
    fun Strategy() {

        val inputString = "LOREM ipsum DOLOR sit amet"

        val lowerCasePrinter = Printer(lowerCaseFormatter)
        lowerCasePrinter.printString(inputString)

        val upperCasePrinter = Printer(upperCaseFormatter)
        upperCasePrinter.printString(inputString)

        val prefixPrinter = Printer { "Prefix: $it" }
        prefixPrinter.printString(inputString)
    }
}

 

Printer를 생성할 때 Formmat전략을 전달합니다.

//result
lorem ipsum dolor sit amet
LOREM IPSUM DOLOR SIT AMET
Prefix: LOREM ipsum DOLOR sit amet

 

 

정리

Strategy Pattern은 

객체가 할 수 있는 행위들을 각각의 전략으로 정의,

수정이 필요한 경우 전략을 바꾸는 것만으로도 수정이 가능하도록 만드는 패턴입니다.

 

 

 

 

소스 보기

https://github.com/qjatjr1108/DesignPattern

 

qjatjr1108/DesignPattern

DesignPattern Sample. Contribute to qjatjr1108/DesignPattern development by creating an account on GitHub.

github.com

 

'DesignPattern' 카테고리의 다른 글

[Design Pattern] Memento Pattern  (0) 2019.10.17
[Design Pattern] Observer Pattern  (0) 2019.10.15
[Design Pattern] Flyweight pattern  (4) 2019.10.10
[Design Pattern] Composition Pattern  (2) 2019.10.10
[Design Pattern] Bridge Pattern  (4) 2019.10.10

댓글