Strategy : 유연한 행동 방식을 선택하는 방법

Strategy : 유연한 행동 방식을 선택하는 방법

Strategy 패턴은 동일한 작업을 수행하는 여러 알고리즘이나 행동을 유연하게 선택하고 변경할 수 있게 해주는 디자인 패턴입니다. 이 패턴은 행동(알고리즘)을 캡슐화하여, 클라이언트가 알고리즘을 직접 구현하는 대신, 행동을 객체화하고 실행 시점에 쉽게 교체할 수 있게 합니다.


Code Smell

Code Smell은 특정 상황에서 유연성이 부족하거나 변경하기 어려운 코드가 발생할 때 나타납니다. 전통적인 방식에서는 행동을 클래스 내부에 직접 구현해 놓는 경우가 많습니다. 예를 들어, 전사의 무기 사용 방식을 생각해봅시다. 전사가 으로 공격하든, 으로 공격하든 무기의 종류에 따라 행동이 달라질 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Warrior
{
    private string weaponType;

    public Warrior(string weaponType)
    {
        this.weaponType = weaponType;
    }

    public void Attack()
    {
        if (weaponType == "Sword")
        {
            // Sword Attack Process
            Console.WriteLine("Attacking with a sword!");
        }
        else if (weaponType == "Spear")
        {
            // Spear Attack Process
            Console.WriteLine("Attacking with a spear!");
        }
        else
        {
            Console.WriteLine("No weapon equipped!");
        }
    }
}

문제가 될 수 있는 상황

  1. 확장성 부족: 무기 종류가 추가될 때마다, Warrior 클래스 내부의 Attack() 메서드를 수정해야 합니다. 예를 들어, 새로운 무기(Bow)가 추가된다면, Attack() 메서드에 또 다른 else if 블록을 추가해야 합니다. 기존 클래스를 수정하는 일이 많아서 코드의 유지 보수성이 떨어집니다.

  2. 코드 중복: 각 무기에 대한 공격 로직이 유사하지만 약간씩 다른 경우, 중복된 코드가 많이 발생할 수 있습니다.


Strategy 패턴의 코드 구현

Strategy 패턴을 사용하면 무기와 같은 행동을 전략 객체로 분리하여 유연하게 교체할 수 있습니다. 전사가 검이나 창을 사용하는 것처럼, 무기 전략을 쉽게 교체할 수 있습니다.

코드 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public abstract class WeaponStrategy
{
    public abstract void Attack();
}

public class SwordStrategy : WeaponStrategy
{
    public override void Attack()
    {
        Console.WriteLine("Attacking with a sword!");
    }
}

public class SpearStrategy : WeaponStrategy
{
    public override void Attack()
    {
        Console.WriteLine("Attacking with a spear!");
    }
}

public class Warrior
{
    private WeaponStrategy weaponStrategy;

    public Warrior(WeaponStrategy weaponStrategy)
    {
        this.weaponStrategy = weaponStrategy;
    }

    public void SetWeapon(WeaponStrategy newWeaponStrategy)
    {
        weaponStrategy = newWeaponStrategy;
    }

    public void Attack()
    {
        weaponStrategy.Attack();
    }
}

사용 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
class Program
{
    static void Main()
    {
        // 검으로 공격
        Warrior warrior = new Warrior(new SwordStrategy());
        warrior.Attack();  // Output: Attacking with a sword!

        // 창으로 무기를 변경 후 공격
        warrior.SetWeapon(new SpearStrategy());
        warrior.Attack();  // Output: Attacking with a spear!
    }
}

장점

  1. 유연성:
    행동(무기 전략)을 쉽게 교체할 수 있습니다. 객체의 내부 구현을 수정하지 않고, 전략을 바꿔 객체의 행동을 유연하게 제어할 수 있습니다.

  2. 확장성:
    새로운 전략을 추가하는 것이 매우 쉽습니다. 예를 들어, 새로운 무기(BowStrategy)를 추가하려면 해당 전략 클래스를 구현하고 교체하기만 하면 됩니다.

  3. 코드 중복 감소:
    비슷한 행동을 각각의 클래스에 중복 구현할 필요 없이, 전략으로 분리하여 한 곳에서 관리할 수 있습니다.


단점

  1. 클래스 증가:
    각 행동을 별도의 클래스(전략)로 분리하다 보면, 클래스가 많아져 관리가 복잡해질 수 있습니다.

  2. 객체 간의 의존성 증가:
    객체가 전략 객체에 의존하게 되므로, 객체 간의 의존성이 높아질 수 있습니다.


결론

Strategy 패턴은 행동을 유연하게 교체하거나 확장할 수 있는 매우 강력한 도구입니다. 무기의 종류처럼 행동이 바뀔 때마다 기존 코드를 수정하지 않고 전략만 교체할 수 있어 코드의 유연성확장성이 크게 향상됩니다. 이 패턴은 특히 행동의 변화가 자주 발생하는 상황에서 매우 유용하며, 게임 개발이나 알고리즘 선택에서 많이 사용됩니다. 그러나, 클래스가 많아질 수 있다는 단점도 있으므로, 필요할 때 적절하게 적용하는 것이 중요합니다.

SWeetJelly
SWeetJelly Programmer, Game Designer
comments powered by Disqus