Design pattern/행동 패턴

[디자인 패턴] 23장 전략 패턴

미스터로즈 2021. 6. 18. 18:34

공부하기 위해서 요약정리해놓은 것입니다..

 

정확하고 꼼꼼한 자료는 쉽게 배워 바로 써먹는 디자인 패턴을 확인하시고

코드는 github.com/infohojin/patterns 을 참고해 주세요.

 

전략 패턴은 객체 내부에서 해결해야 하는 목적을 알고리즘 객체로 분리 적용하는 기법입니다.

실제 내부 동작을 외부 알고리즘 객체로 분리하여 유연하게 동작을 변경시킬 수 있습니다.

 

문제

프로그램은 반복되는 문제를 해결합니다. 발생한 문제를 해결하는 방법은 다양한데, 이들 각각의 해결 방법을 알고리즘이라고 합니다.

 

전략은 어떤 목표를 정하고 진행하는 큰 틀을 말하며, 군대에서 적과 싸울 때의 작전에 비유할 수 있습니다.

 

전술은 전략과 유사한 용어로, 전략을 짜면서 정한 목표를 달성하기 위한 상세 내용을 의미합니다.

전술은 전략을 수행하기 위한 효과적인 행동을 말하는데, 전략 패턴에서 전략은 알고리즘입니다.

그리고 전술은 실제 알고리즘이 동작하는 상세 내용입니다.

 

알고리즘

프로그램의 목적은 주어진 문제를 해결하는 것이고 알고리즘은 주어진 문제를 해결하는 전술입니다.

 

한 번 개발된 프로그램은 산업 현장에서 생각보다 오랫동안 사용되고, 이러한 프로그램은 시간이 지나면서 환경적인 영향을 받으면서 변화가 필요합니다.

 

외부적으로 큰 환경 변화가 있을 때는 코드 수정이 어렵습니다. 큰 변화가 요구되면 기존의 방법을 버리고 새로운 방법을 도입하는 것이 현명하며 이때 리팩터링이 필요합니다.

 

문제를 해결하는 방법은 다양하며 한 가지 문제를 해결하는 방법은 수십, 수백 가지가 존재합니다. 또한 발생한 문제는 다르지만 유사한 방법으로 해결할 수도 있습니다.

 

문제 해결 전략

 

문제 해결하는 방법을 다르게 적용하려고 합니다. 해결 방법이 변경되면 코드도 같이 수정해야 하는데, 이때 코드를 찾고 오래된 코드를 다시 분석하는 등 많은 시간이 필요합니다.

 

변화가 예상되는 부분을 별도의 클래스로 분리합니다. 대부분 처리 로직 부분의 변화가 예상되며, 이처럼 분리된 처리 로직을 알고리즘이라고 부릅니다. 알고리즘은 문제를 해결하는 하나의 패턴입니다.

 

 

분리

템플릿 메서드 패턴은 공통된 기능을 분리하여 템플릿화하고, 추상화를 통해 상황별로 다르게 처리할 수 있도록 실제 동작을 분리합니다.

 

템플릿 메서드는 하나의 알고리즘을 다양한 방식으로 재정의할 수 있도록 행동을 분리하는 패턴입니다.

하지만 공통 템플릿도 코드 일부분을 수정해야 하는 경우가 발생합니다.

 

분리된 알고리즘을 별도의 클래스로 캡슐화합니다. 패턴에서는 알고리즘의 캡슐화를 전략이라고 하며 캡슐화된 알고리즘은 상황에 맞게 교체하며 사용합니다.

 

전략 패턴은 구조를 그대로 사용하면서 캡슐화된 알고리즘으로 동작을 변경하는 행위입니다.

 

기존 프로그램의 구조를 그대로 유지하면서 새로운 변화를 적용하기 위해서 인터페이스를 적용하여 설계합니다.

 

인터페이스

인터페이스를 활용하면 구조를 유지하면서 호환성 있는 하위 코드를 설계할 수 있습니다.

 

전략 패턴은 변화되는 부분을 찾아 알고리즘으로 분리합니다. 구조의 일부분을 알고리즘으로 분리할 때는 구조적 호환성을 유지하는 것이 중요합니다.

 

분리된 행동의 호환성을 유지하기 위해 인터페이스를 설계합니다.

 

<?php
/**
 * 무기에 대한 인터페이스를 선언합니다.
 */
interface Weapon
{
    public function attack();
}

 

전략 패턴은 공통된 부분과 변화되는 부분을 분리합니다. 전략 패턴에서 사용되는 알고리즘 클래스는 인터페이스를 적용하여 구체화합니다.

 

<?php
/**
 * 무기 인터페이스를 적용하여 객체의 실체 코드 구현을 작성합니다.
 */
class Knife implements Weapon
{
    public function attack()
    {
        print "칼 공격합니다.";
        echo "\n";
    }
}

 

<?php
/**
 * 무기 인터페이스를 적용하여 객체의 실체 코드 구현을 작성합니다.
 */
class Gun implements Weapon
{
    public function attack()
    {
        print "총을 발포합니다.";
        echo "\n";
    }
}

 

인터페이스를 활용해 케릭터의 무기 종류를 각각 다른 알고리즘 클래스로 분리했습니다. 이것은 알고리즘의 객체화입니다.

 

인터페이스는 구조적 관계를 유지한다는 약속입니다. 인터페이스는 하위 코드에 특정 구현 방법을 고정하지 않고 약속된 인터페이스만 유지하면 됩니다.

 

다형성은 인터페이스를 통해 프로그램을 작성하는데, 메서드의 실제 내용은 인터페이스를 적용한 구현 클래스에서 작성합니다.

다형성을 이용하면 변화에 쉽게 대응할 수 있습니다.

 

전략

문제를 해결하기 위해 다양한 방법을 모두 구현할 수는 없습니다. 완벽한 방법을 찾아서 구현하는 것은 시간적으로 한계가 있습니다.

따라서 당장 성능이 떨어지더라도 동작하는 코드를 만드는 것을 우선시합니다.

 

 

알고리즘을 실제 코드와 결합해서 설계하면 향후 유지 보수가 힘들어집니다. 이때 알고리즘을 추상화해 전략 패턴으로 전환합니다. 전략 패턴으로 변경된 알고리즘은 차후에 객체를 변경하여 보완할 수 잇습니다.

 

Strategy 클래스는 추상클래스이고, 전략 패턴에서 사용하는 인터페이스는 전략을 적용하기 위한 추상 메서드입니다.

 

<?php

abstract class Stategy
{
    // 추상적인 접근점
    protected $delegate;

    // 무기 교환 패턴
    public function setWeapon(Weapon $weapon)
    {
        echo "== 무기를 교환합니다. ==\n";
        $this->delegate = $weapon;
    }

    abstract public function attack();
}

 

추상 메서드를 이용하여 인터페이스를 적용하는 것은 구조를 유지하면서 변화되는 부분만 분리 결합하기 위해서입니다.

 

적용 사례

전략 패턴은 실제 프로젝트에서 가장 많이 사용되는 패턴 유형 중 하나입니다.

 

정렬 알고리즘에는 버블 정렬, 셸 정렬, 퀵 정렬 등 다양한 알고리즘을 갖고 있습니다. 즉, 전략 패턴은 알고리즘을 변경하여 문제를 해결하는 데 매우 유용합니다.

 

통신 프로토콜을 변경하는 시스템에도 적용할 수 있습니다.