Inor

[DesignPattern] Strategy Pattern 본문

Computer Engineering/Design Pattern

[DesignPattern] Strategy Pattern

Inor 2017. 8. 2. 17:46

Strategy Pattern


 Strategy Pattern은 클라이언트가 사용하는 알고리즘들을 분류해서 하나의 알고리즘군으로 캡슐화하는 디자인 패턴 입니다. 이렇게 디자인을 했을 경우에는 알고리즘군으로 분류된 알고리즘들을 클라이언트에서 원하는 알고리즘으로 교환이 가능하고 위임을 통해서 어떤 행동을 할지 결정 합니다. 캡슐화를 했기 때문에 알고리즘에서 수정 사항이나 알고리즘의 추가해야하는 상황이 발생 했을 경우에는 알고리즘만 수정하면 되기 때문에 유지보수에서 유리 합니다. 하지만 이 알고리즘 객체는 메서드로만 구성되는 경우가 많아서 멤버 변수와 메서드를 갖고 있어야하는 객체라는 의미에 문제가 있을 수 있습니다. 아래는 Strategy Pattern을 UML로 표현한 것 입니다.



 스타크래프트에는 마린,저글링, 파이어뱃 등 여러 종류의 유닛이 등장 합니다. 유닛들은 공격이 가능하고 움직일 수 있습니다. 그러나 이러한 기능들을 모든 유닛 클래스의 메서드로 구현을 하면 유닛마다 이동, 공격 메서드를 만들어서 구현을 해야 합니다. 이런 경우에 코드의 중복이 발생할 수 있습니다. 예를 들어, 마린과 파이어뱃은 이동하는 방법이 똑같습니다. 그러나 각각 다른 클래스이기 때문에 다른 메서드로 구현을 해줬습니다. 이렇게 되면 마린과 파이어뱃의 클래스에서는 같은 방식으로 동작하는 move()라는 메서드가 존재 하게 되는 것 입니다.

 그래서 Strategy Pattern에서는 이런 알고리즘군을 분류하고 따로 구현을 했습니다. 위에 보면 Unit이라는 클래스와 그것을 상속 받는 Marine, Firebat, Battlecruiser가 있습니다. Attackable, Movable 인터페이스가 있고 인터페이스를 구현한 각각의 클래스들이 있습니다. Unit 객체는 Attackable, Movable 객체를 멤버 변수로 갖고 있고 어떤 이동 방식과 공격 방식을 택할지 set 메서드에서 설정할 수 있습니다. 그리고 그것을 실제로 Unit 객체에서 사용할 수 있도록 move와 attack 메서드를 선언 했습니다. 

 Strategy Pattern을 사용할 경우에 Unit을 상속 받는 클래스들이 어떤 이동 방식과 공격 방식을 사용할지 선택할 수 있으며 코드의 중복 또한 피할 수 있습니다. 아래는 위의 UML을 코드로 구현한 것 입니다.


//이동하는 방법에대한 인터페이스와 인터페이스를 구현한 클래스
interface Movable{
	public void move();
}

class Walking implements Movable{
	public void move(){
		System.out.println("걸어서 이동");
	}
}

class Flying implements Movable{
	public void move(){
		System.out.println("날아서 이동");
	}
}

//공격하는 방법에대한 인터페이스와 인터페이스를 구현한 클래스
interface Attackable{
	public void attack();
}

class LongRangeAttack implements Attackable{
	public void attack(){
		System.out.println("원거리 공격");
	}
}

class ShortRangeAttack implements Attackable{
	public void attack(){
		System.out.println("단거리 공격");
	}
}

/*
 * 이동과 공격이라는 알고리즘군을 사용하는 클래스
 * set을 이용해서 어떤 알고리즘을 사용할지 선택 할 수 있습니다.
 */
class Unit{
	private Attackable attackable;
	private Movable movable;
	
	public void setAttackable(Attackable attackable){
		this.attackable = attackable;
	}
	
	public void setMovable(Movable movable){
		this.movable = movable;
	}
	
	public void move(){
		movable.move();
	}
	
	public void attack(){
		attackable.attack();
	}
}

class Marine extends Unit{
	public void steampack(){
		System.out.println("스팀팩!!!");
	}
}

class Firebat extends Unit{
	public void steampack(){
		System.out.println("스팀팩!!!");
	}
}

class Battlecruiser extends Unit{
	public void shotYamatoCannon(){
		System.out.println("야마토 캐논 발사!!!");
	}
}


 코드를 보시기 전에 UML을 통해 전체적인 내용을 확인 하시기 바랍니다. 코드를 보면 알고리즘군의 인터페이스가 잘 분리됐기 때문에 Attackable의 attack 메서드와 Movable의 move 메서드를 재정의한 코드를 수정하면 Unit 클래스를 상속 받은 클래스들을 수정하지 않고도 알고리즘만 수정할 수 있다는 것을 확인할 수 있습니다. 그리고 새로운 공격 방식과 이동 방식이 필요한 경우에 인터페이스를 재정의하는 클래스를 추가해서 새로운 방식을 사용하는 클래스에서 set 메서드로 설정을 해주면 되기 때문에 유지보수의 측면에서 매우 유용 합니다.

 위의 코드를 확인하면 마린과 파이어벳이 staempack이라는 기술을 사용하는 것을 확인할 수 있습니다. 배틀크루저도 shotYamatoCannon이라는 기술을 사용하는 것을 확인할 수 있습니다. 여기서는 단순하게 문자열을 출력하는 용도의 메서드들 이지만 실제로는 많은 알고리즘이 들어가는 코드일 것 입니다. 제 생각에는 이것도 하나의 알고리즘군으로 분류해서 인터페이스를 만들고 Unit 클래스에서 사용하도록 해야될 것 같습니다.




참고


Comments