Priv's Blog

5. 스트래티지 패턴 본문

Dev. Study Note/Design Pattern

5. 스트래티지 패턴

Priv 2022. 7. 3. 23:09

 


 

 

1. 로봇 만들기

이번 시간에는 Atom 클래스와 TaekwonV 클래스를 만들어볼 것이다.

Atom 클래스는 로봇 '아톰'을 정의하는 클래스이며, TaekwonV 클래스는 '태권 V'를 정의하는 클래스이다.

이 두 로봇은 공격과 이동 기능이 존재한다.

아톰은 공격할 때 주먹만 사용하지만 하늘을 날 수 있다.

태권 V는 미사일로 공격할 수 있지만 날 수는 없고 걸어 다닐 수만 있다.

이 두 로봇을 클래스 다이어그램으로 나타내면 다음과 같다.

다이어그램의 구조를 보면 TaekwonV 클래스, Atom 클래스 모두 Robot이라는 추상 클래스의 자식 클래스이다.

이는 아톰과 태권 V 모두 공격(attack)과 이동(move) 메서드를 가지고 있기 때문이다.

다만 어떻게 공격하고 이동하는지에 대해서는 서로 다르기 때문에 각각 자식 클래스로 정의해주었다.

이를 코드로 나타내면 다음과 같다.

package P5;

public abstract class Robot {
    private String name; 

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

    public String getName() {
        return this.name;
    }

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

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

    public void attack() {
        System.out.println("I have strong punch and can attack with it.");
    }

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

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

    public void attack() {
        System.out.println("I have missile and can attack with it.");
    }
    
    public void move() {
        System.out.println("i can only walk.");
    }
}

 


 

2. 문제점

기존 로봇의 공격이나 이동 방법을 바꾸려면 어떻게 해야 할까? 아톰이 걷고, 태권 V가 날아다니도록 바꾸려고 한다면?

새로운 로봇을 만들어 기존의 공격 또는 이동 방법을 추가하거나 수정하려면 어떻게 해야 할까? 새로운 로봇인 선가드(Sungard) 클래스를 만들어 태권 V의 미사일 공격 기능을 추가하려고 한다면?

 

2.1) 기존 로봇의 공격, 이동 방법을 수정할 경우

로봇의 이동/공격 방식을 수정하고 싶다면 attack 메서드 또는 move 메서드의 내용을 수정하면 된다.

다만 이러한 방법은 새로운 기능을 변경하기 위해 기존 코드의 내용을 수정하는 것이므로 OCP에 위반된다.

또한 아톰이 걸어 다니도록 코드를 수정한다고 가정하면, 태권 V와 아톰이 동일한 기능의 메서드를 중복으로 가지게 되는 문제도 발생한다.

 

2.2) 새로운 로봇에 공격, 이동 방법을 추가 및 수정할 경우

새로운 로봇을 추가할 경우에는 현재 설계에서 로봇 자체가 캡슐화 단위이기 때문에 매우 쉽게 작업을 처리할 수 있다.

Sungard 클래스를 추가한다면 그냥 클래스를 작성한 뒤, Robot 클래스를 확장하면 된다.

그러나, 새로운 로봇에 기존의 공격 또는 이동 방법을 추가하거나 변경하고자 한다면 문제가 발생한다.

Sungard 클래스에 TaekwonV에 있는 attack 메서드의 기능을 가져오고 싶다고 가정하면, 이 또한 메서드의 중복 사용이 된다.

 


 

3. 해결책

로봇 설계에서 문제를 해결하려면 무엇이 변화되었는지를 찾아야 한다.

변화된 것을 찾은 후에는 이를 클래스로 캡슐화해야 한다.

위에서 살펴본 로봇 예제의 경우에 문제를 발생시키는 요인은 로봇의 이동 방식과 공격 방식의 변화이다.

즉, 새로운 방식의 이동 및 공격 기능이 계속해서 추가될 수 있으므로, 기존의 로봇이나 새로운 로봇이 이러한 기능을 별다른 코드 변경 없이 제공받거나 기존의 공격, 이동 방식을 다른 기능으로 손쉽게 변경할 수 있어야 한다.

우리는 이미 이동/공격 기능이 변한다는 것을 알고 있다.

그러므로 이를 캡슐화하려면 외부에서 구체적인 이동/공격 방식을 담은 구체적인 클래스들을 은닉해야 한다.

이를 위해 공격과 이동을 위한 인터페이스를 각각 만들고 이들을 실제 실현한 클래스를 만들어야 한다.

이를 기반으로 설계를 개선하면 다음과 같다.

Robot 클래스 입장에서 보면 구체적인 이동 방식, 공격 방식이 MovingStrategy, AttackStrategy 인터페이스를 통해 캡슐화되어 있다.

이제 이들 기능을 이용하는 로봇 객체와는 상관없이 향후 등장할 이동 방식과 공격 방식의 변화, 현재 변화도 잘 처리할 수 있게 된 것이다.

즉, 새로운 기능의 추가(새로운 이동/공격 기능)가 기존의 코드에 영향을 미치지 못하게 하므로, OCP를 만족하는 설계가 되었다.

package P5;

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


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

    public String getName() {
        return this.name;
    }

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

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

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

    public void move() {
        this.movingStrategy.move();
    }
}
package P5;

public interface MovingStrategy {
    void move();
}
package P5;

public interface AttackStrategy {
    void attack();
}
package P5;

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

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

public class MissileStrategy implements AttackStrategy {
    public void attack() {
        System.out.println("I have Missile and can attack with it.");
    }
}
package P5;

public class PunchStrategy implements AttackStrategy {
    public void attack() {
        System.out.println("I have strong punch and can attack with it.");
    }
}
package P5;

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

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

 


 

4. 스트래티지 패턴

스트래티지 패턴은 전략을 쉽게 바꿀 수 있도록 해주는 디자인 패턴이다.

여기서 전략이란, 어떤 목표를 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제 해결 알고리즘 등이 있다.

게임 프로그래밍에서 특히 유용하게 사용할 수 있는 디자인 패턴이다.

게임 캐릭터가 자신이 처한 상황에 따라 공격 및 행동 방식을 변경할 수 있도록 만들고 싶다면 스트래티지 패턴을 활용해 구현하는 것이 효과적이다.

즉, 스트래티지 패턴이란 같은 문제를 해결하는 여러 알고리즘(방식)이 클래스별로 캡슐화되어 있고, 이들이 필요할 때 교체할 수 있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게 만드는 디자인 패턴이라고 할 수 있다.

 


 


수고하셨습니다!


'Dev. Study Note > Design Pattern' 카테고리의 다른 글

7. 스테이트 패턴  (0) 2022.07.03
6. 싱글턴 패턴  (0) 2022.07.03
4. 디자인 패턴  (0) 2022.07.03
3. SOLID 원칙  (0) 2022.07.03
2. 객체지향 원리  (0) 2022.07.03
Comments