05-16 00:01
Recent Posts
Recent Comments
관리 메뉴

miinsun

[디자인패턴/자바] 전략 패턴 Strategy Pattern 본문

Language/Java

[디자인패턴/자바] 전략 패턴 Strategy Pattern

miinsun 2022. 8. 15. 19:41

오리 게임 만들기

전략 패턴을 이용해 오리 시뮬레이션 게임을 만들어보자. 오리 시뮬레이션 게임은 다양한 오리들이 날아다니거나 꽥꽥 소리를 낼 수 있는 게임이다. 전략 패턴을 이용해서 가지각색의 오리들을 효율적으로 만들 수 있다.

각각의 오리들은 다음의 기능을 가진다.

  1. quack() - 꽥 꽥 울 수 있다.
  2. swim() - 수영할 수 있다.
  3. display() - 오리의 생김새를 표현한다.
  4. fly() - 오리는 날 수 있다.

 

 


 

상속으로 구현하기

만약 상속을 이용해 청둥오리와 고무 오리를 구현한다면 다음과 같을 것이다. 청둥오리는 display메서드를 오버라이드 했고, 고무 오리는 display, quack메서드를 오버라이드 했다.

고무 오리는 날지 않기 때문에 fly메서드가 필요하지 않다. 그렇지만, 슈퍼클래스에 있는 fly메서드를 상속받아 서브 클래스에 적합하지 않은 나는 행동이 추가될 수 있다.

이와 같이 상속 방법은 코드를 재활용한다는 점에서 좋은 방법처럼 느껴지지만 몇 가지 문제가 있을 수 있다. 먼저, 유지보수가 어렵다는 점과 모든 오리들의 행동을 알기 어렵고, 실행 도중에 오리의 특성을 바꾸기 힘들다는 점이 있다. 또, 슈퍼클래스를 수정한다면, 다른 모든 오리들에게 원치 않은 영향을 끼칠 수 있다.

 

 


 

전략 패턴을 이용해 구현하기

전략 패턴은
알고리즘 군을 정의하고 캡슐화해 각각의 알고리즘 군을 수정해서 쓸 수 있게 해 준다. 전략 패턴을 사용하면 클라이언트로부터 알고리즘을 분리해서 독립적으로 변경 할 수 있다.

위의 상속으로 구현된 코드를 전략패턴을 이용해 고쳐보자.

우선, Duck 클래스에 flyBehavior와 quackBehavior라는 인터페이스 형식의 인스턴스 변수를 추가해준다. 각 오리 객체에서는 실행 시에 이 변수에 특정 행동 형식의 레퍼런스를 다형적으로 설정할 수 있다. 이 변수들이 기존의 fly, quack메서드의 역할을 하기 때문에, fly, quack 메서드는 제거하고 대신 performFly, performQuack메서드를 추가한다.

추가된 인터페이스에 맞춰서 직접 프로그래밍을 해보자.

public abstract class Duck{
        FlyBehavior flyBehavior;		// (1)
        QuackBehavior quackBehavior;	// (1)

        public Duck(){} ;

        public abstract void display();

        public void performFly(){		// (2)
            flyBehavior.fly();
        }

        public void performQuack(){
            quackBehavior.quack();		// (2)
        }

        public void swim(){
            System.out.println("모든 오리는 물에 뜹니다.");
        }
        
        public void setFlyBehavior(FlyBehavior fb){	// (3)
        	flyBehavior = fb;
        }
        
        public void setQuackBehavior(QuackBehavior qb){	// (3)
        	quackBehavior = qb;
        }
}

 

 

  • (1) 행동 인터페이스 형식의 레퍼런스 변수 2개를 선언한다. 같은 패키지에 속하는 모든 서브클래스들은 이 변수를 상속받는다.
  • (2) 행동 클래스에 행동을 위임
  • (3) 오리 행동 형식을 동적으로 지정하기 위해 setter를 만들어준다. setter메서드를 포함해서 프로그램 실행 중에도 오리의 행동을 바꿀 수 있다.

 

Fly, Quack의 행동을 구체화할 클래스를 작성해보자. 오리의 행동들을 일련의 행동으로 생각하는 대신, 하나의 같은 알고리즘 군으로 생각해보자. 

/* Fly 행동 인턴페이스 구현 */
public interface FlyBehavior {
	public void fly();
}

public class FlyWithWings implements FlyBehavior {
	public void fly(){
    	System.out.println("날 수 있다.");
    }
}

public class FlyNoWay implements FlyBehavior {
	public void fly(){
    	System.out.println("날 수 없다.");
    }
}

/* Quack 행동 인턴페이스 구현 */
public interface QuackBehavior {
	public void quack();
}

public class Quack implements QuackBehavior {
	public void quack(){
    	System.out.println("꽥");
    }
}

public class Squeak implements QuackBehavior {
	public void quack(){
    	System.out.println("삑");
    }
}

 

테스트 코드는 다음과 같다. 고무 오리는 삑 하고 울고, 날지 못한다. 고무 오리의 세팅을 아래와 같이 바꿔줄 수 있다. 만약 실행 중에 고무 오리의 행동을 바꾸고 싶으면 원하는 행동에 해당하는 다시 setter메서드를 호출하면 된다.

public calss Test{
	public static void main(String[] args){
    	Duck rubber = new MallardDuck();
        rubber.performQuack();	// "꽥"
        rubber.performFly();	// "날 수 있다.
        
        rubber.setQuackBehavior(new Squeak);
        rubber.performQuack();	// "삑"
        rubber.setFlyBehavior(new FlyNoWay);
        rubber.performFly();	// "날 수 없다.

    }
}

이렇게 인터페이스 방식을 이용해 디자인하면 다른 형식의 객체에서도 나는 행동과 꽥꽥거리는 행동을 재사용할 수 있다. 그리고 기존의 행동 클래스를 수정하거나 날아다니는 행동을 사용하는 Duck 클래스를 전혀 건드리지 않고도 새로운 행동을 추가할 수 있다.

 

Comments