Strategy Pattern

김영우 (AvocadoSmasher)·2022년 10월 22일
0

Design Pattern

목록 보기
1/8

🔎 What is Strategy Pattern??

전략패턴(Strategy Pattern)은 객체들이 할 수 있는 행위(Behavior)를 각각에 대해 전략 클래스를 생성하고, 유사한 행위들을 캡슐화(Encapsulate) 하는 인터페이스를 정의하여, 객체의 프로그램의 수행 도중 행위를 바꾸고 싶은 경우 코드의 수정 없이 동적으로 전략의 수정만으로 행위를 유연하게 확장할 수 있도록 하는 방법이다.

⚓️ Digging into how it works

예를 들어서 제가 좋아하는 게임인 LoL의 캐릭터를 디자인 한다고 해보죠 롤에서는 원거리 공격을 하는 캐릭터와 근접 공격을 하는 2가지 타입의 캐릭터가 있습니다. 이들의 공격에 대한 패턴을 짠다고 가정해 보도록 하죠.

처음에는 단순한 패턴으로 코드를 구현하여 다음과 같은 구성을 했다고 생각해 보죠

그레이브즈(Graves)라는 원거리 공격 타입의 챔피언과 자크(Zac)라는 근거리 공격 타입의 챔피언을 구현했다고 해봅시다. 각각의 챔피언 클래스는 AttackAble이라는 interface를 상속받아 기본공격의 형태를 각자 클래스 내에서 implement하도록 된 구조입니다. 여기까지는 큰 문제가 없어보이죠.

여기서 갑자기 한 개발자가 기막힌 방법을 생각해냅니다. 그레이브즈가 총을 쏘기는 하지만 적이 가까우면 칼로 공격하는 방법을 채택하기로 했다고 해보죠. 그럼 위와 같은 구조에서는 어떻게 해야할까요? 그레이브즈 클래스의 auto_attack()의 코드를 수정하여 거리에 따른 조건문을 추가해주면 될까요?

네 물론 가능하죠 하지만 그렇게 되면 객체지향 프로그래밍의 기본 원칙(SOLID)의 2번째 원칙인 OCP(Open Close Principle)을 위반하게 됩니다. 그렇다면 여기서 문제입니다. 어떻게 객체지향의 SOLID 법칙을 위반하지 않으면서 동적으로 그레이브즈의 공격 타입을 바꿔줄 수 있을까요?

그 방법을 전략 패턴에서는 행위(Behavior)에 대한 것들을 모아서 모듈화 하여 행위에 대한 것을 조작하는 작업을 Behavior이라는 행위 객체에게 양도하도록 하는 것입니다. 말로만 하면 복잡하니 이렇게 수정하면 어떤 구조가 될 지 보도록 하죠.

자 위와 같은 수정이 이루어 졌습니다. 기본적으로 공격 방식은 AutoAttack이라는 interface를 직접 구현하는 클래스들로 모듈화 되었고 챔피언 쪽은 조금 구조가 추가되었는데 핵심만 보자면 AutoAttack에 대한 행위를 저장할 attackBehavior라는 속성이 추가되고 이를 동적으로 설정해줄 setAutoAttack 메소드가 추가되었습니다. 이렇게 되면 그레이브즈와 적의 사거리에 따라 setAutoAttack 메소드를 통해 RanageAttack이나 MeleeAttack으로 공격 방식을 선택해 주면 되겠죠? 그렇게 되면 동적으로 공격 방식이 바뀌더라도 해당 행위의 변경에 대한 것을 Champion쪽 모듈이 아니라 AutoAttack 모듈에서 적절한 객체를 끌어다 쓰면 됩니다.

+) RPG 게임으로 치면 무기의 변경에 따른 공격도 무기에 Behavior를 설정해놓고 무기 장착시 해당 캐릭터의 Behavior가 이를 받는 식으로 해도 편리해 질것 같아 보이네요..

💻 Code (feat. Java)

초기 코드

AttackAble interface

public interface AttackAble{
	void auto_attack();
}

Zac & Graves class

public class Zac implements AttackAble{
    @Override
    public void auto_attack() {
        System.out.println("Melee Attack");
    }
}
public class Graves implements AttackAble{
    @Override
    public void auto_attack() {
        System.out.println("Range Attack");
    }
}

변환 후 코드들.

AutoAttack interface

public interface AutoAttack {
    void auto_attack();
}

MeleeAttack & RangeAttack

public class MeleeAttack implements AutoAttack{
    @Override
    public void auto_attack() {
        System.out.println("Melee Attack");
    }
}
public class RangeAttack implements AutoAttack{
    @Override
    public void auto_attack() {
        System.out.println("Range Attack");
    }
}

Champion abstract class

public abstract class Champion {
    protected AutoAttack attackBehavior;
    public void setAutoAttack(AutoAttack attack){
        this.attackBehavior = attackBehavior;
    }
    public void auto_attack(){
        attackBehavior.auto_attack();
    };
}

Graves & Zac

public class Graves extends Champion{
	// Attributes & Methods related to Graves
}
public class Zac extends Champion{
	// Attributes & Methods related to Zac
}

📌 Summary

이번엔 전략 패턴에 대해 알아보았습니다. 전략 패턴의 핵심은 행위에 대한 작업을 따로 분리 / 모듈화 하여 메인 객체와 분리시켜서 동적인 변화에 대응한 행위의 변화에 대해 자유롭게 하는 것이었습니다. 그 예시로 게임을 들었는데 사실 공부를 하고 나름대로 생각해서 만든 예시라 완전 적절한지는 잘 모르겠네요… 혹시 잘못된 부분이 있다면 지적 환영합니다.

P.S ) 롤의 사미라 라는 캐릭터가 적절했는데 그레이브즈를 예시로 들었네요. 적다가 생각난거라 언급해봅니다.

profile
Android Studio 공부 중

0개의 댓글