[Design Pattern] 스트레티지 패턴(Strategy Pattern) 개념과 예제

스트레티지 패턴(Strategy Pattern)에 대해서 알아보자

해당 내용은 책 ‘JAVA 객체지향 디자인 패턴’을 참고해서 작성하였습니다.


환경

  • Java


스트레티지 패턴(Strategy Pattern)이란?

스트레티지 패턴(Strategy Pattern)

  • 스트레티지 패턴(Strategy Pattern) : 특정한 알고리즘을 정의하고 이를 캡슐화하여 해당 알고리즘이 필요한 대상들이 이를 상호 교체 가능하게 하는 구조
  • Java의 경우 인터페이스를 생성하고 해당 부분을 구현해서 캡슐화 하고 이를 원하는 객체의 메소드에 붙여주면 된다. 자세한 설명과 예제는 아래 참고
  • 이미지 출처 : https://en.wikipedia.org/wiki/Strategy_pattern


필요성

로봇 사례

  • 로봇을 만든다고 생각해보자. 로봇은 태권V도 있고 아톰도 있고 건담도 있다. 이때 어떤 방법으로 만들어야할까? 가장 기본틀인 로봇을 추상 클래스로 만들고 상속을 통해 각각을 만들어보자.
  • 우선 로봇 추상 클래스를 만들고 태권V와 아톰만 만들어보자.


예제

Robot

public abstract class Robot {
    private String name;
    public Robot(String name){ this.name = name; }

    public String getName(){ return name;}

    public abstract void attack();
    public abstract void move();
}

TaekwonV

public class TaekwonV extends Robot {

    public TaekwonV(String name){
        super(name);
    }

    @Override
    public void attack() {
        System.out.println("I can kick");
    }

    @Override
    public void move() {
        System.out.println("I can walk");
    }
}

Atom

public class Atom extends Robot {

    public Atom (String name){
        super(name);
    }

    @Override
    public void attack() {
        System.out.println("I can punch");
    }

    @Override
    public void move() {
        System.out.println("I can fly");
    }
}

Main

public class Main {
    public static void main(String[] args) {

        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");

        System.out.println("I am " + taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println();

        System.out.println("I am " + atom.getName());
        atom.attack();
        atom.move();
    }
}


해당 코드의 문제점

  • 기존 로봇의 공격 방법을 수정하려면 새로운 기능을 위해 기존 코드를 수정하기 때문에 OCP(Open-Closed Principle)에 위반된다. 예를들어 위의 예제에서 아톰이 태권V와 똑같은 move()를 사용하려 하면 코드를 직접 수정해야하고 나아가 중복이 발생하며 로봇의 종류가 많아질수록 코드의 관리는 어려워진다.
  • 새로운 로봇을 만들어서 아톰과 같은 공격과 태권V와 같은 이동을 하게 하고 싶다면? 동일한 코드를 복사하게 작성하게 되면 중복이 발생하게 된다. 예를 들어서 건담을 만들때 아톰의 attack()과 태권V의 move() 코드를 또 동일하게 작성하기 때문에 중복이 발생한다.


해결 방법

  • 로봇을 만드는 과정에 있어서 변화하는 부분이 무엇인지 먼저 찾아야한다. 변화될 수 있는 는 부분을 찾아서 이를 캡슐화하고 상호 교체 가능하도록 만드는것이 중요하다.
  • 예제 코드들에서는 attack()과 move()의 코드들이 계속 변경될 수 있으며 이를 캡슐화해서 필요할때 붙여주면 코드의 중복도 줄이고 코드의 변경도 줄일 수 있다.
  • 직접적인 구현은 각각을 interface로 구현해서 객체를 만들때 붙여주면 된다.
  • 그림에서 보면 move()의 경우에 MovingStrategy라는 인터페이스를 만들고 각각의 방법에 대한 구현을 implements를 통해서 클래스화 시키면된다.


예제

Robot

public abstract class Robot {
    private String name;
    private MovingStrategy movingStrategy;
    private AttackStrategy attackStrategy;

    public Robot(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void attack() {
        attackStrategy.attack();
    }

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

    public void setAttackStrategy(AttackStrategy attackStrategy) {
        this.attackStrategy = attackStrategy;
    }

    public void setMovingStrategy(MovingStrategy movingStrategy) {
        this.movingStrategy = movingStrategy;
    }

}

AttackStrategy

public interface AttackStrategy {
    public void attack();
}

MovingStrategy

public interface MovingStrategy {
    public void move();
}

KickingStrategy

public class KickingStrategy implements AttackStrategy {
    @Override
    public void attack() {
        System.out.println("I can kick");
    }
}

PunchingStrategy__

public class PunchingStrategy implements AttackStrategy {
    @Override
    public void attack() {
        System.out.println("I can punch");
    }
}

WalkingStrategy

public class WalkingStrategy implements MovingStrategy {
    @Override
    public void move() {
        System.out.println("I can walk");
    }
}

FlyingStrategy

public class FlyingStrategy implements MovingStrategy {
    @Override
    public void move() {
        System.out.println("I can fly");
    }
}

TaekwonV

public class TaekwonV extends Robot {
    public TaekwonV(String name) {
        super(name);
    }
}

Atom

public class Atom extends Robot {
    public Atom(String name) {
        super(name);
    }
}

Gundam

public class Gundam extends Robot {
    public Gundam(String name) {
        super(name);
    }
}

Main

public class Main {
    public static void main(String[] args) {

        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");
        Robot gundam = new Gundam("Gundam");

        taekwonV.setAttackStrategy(new KickingStrategy());
        taekwonV.setMovingStrategy(new WalkingStrategy());

        atom.setAttackStrategy(new PunchingStrategy());
        atom.setMovingStrategy(new FlyingStrategy());

        gundam.setAttackStrategy(new PunchingStrategy());
        gundam.setMovingStrategy(new WalkingStrategy());

        System.out.println("I am " + taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println();

        System.out.println("I am " + atom.getName());
        atom.attack();
        atom.move();

        System.out.println();

        System.out.println("I am " + gundam.getName());
        gundam.attack();
        gundam.move();

    }
}public class Main {
    public static void main(String[] args) {

        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");
        Robot gundam = new Gundam("Gundam");

        taekwonV.setAttackStrategy(new KickingStrategy());
        taekwonV.setMovingStrategy(new WalkingStrategy());

        atom.setAttackStrategy(new PunchingStrategy());
        atom.setMovingStrategy(new FlyingStrategy());

        gundam.setAttackStrategy(new PunchingStrategy());
        gundam.setMovingStrategy(new WalkingStrategy());

        System.out.println("I am " + taekwonV.getName());
        taekwonV.attack();
        taekwonV.move();

        System.out.println();

        System.out.println("I am " + atom.getName());
        atom.attack();
        atom.move();

        System.out.println();

        System.out.println("I am " + gundam.getName());
        gundam.attack();
        gundam.move();

    }
}


참고자료