티스토리 뷰

디자인 패턴

Strategy Pattern

기내식은수박바 2021. 9. 15. 15:04
반응형

전략 패턴?

전략을 쉽게 바꿀 수 있게 만들어주는 디자인 패턴

  • 전략? - 일을 수행하는 방식, 문제 해결 알고리즘 등등

 

 

등장 배경

어떤 게임 내에서 아래와 같이 두 개의 직업이 있고, 직업별로 특정한 행동 방식이 있도록 만든다고 가정해보자. 

  1. 전사
    • 공격 시 - 근거리에서 칼 휘두르기
    • 스킬 사용 시 - 힘을 모아 강하게 휘두르기 (강타)
  2. 궁수
    • 공격 시 - 원거리에서 활 쏘기
    • 스킬 사용 시 - 여러 발의 화살을 한 번에 쏘기 (멀티샷)

 

이를 클래스 다이어그램과 코드로 나타내면 아래와 같을 것이다.

클래스 다이어그램

public abstract class Position {
    private String nickName;
    
    public Position(String nickName) {
    	this.nickName = nickName;
    }
    
    public String getNickName() {
    	return this.nickName;
    }
    
    public abstract void attack();
    public abstract void skill();
}

public class Warrior extends Position {
    
    public Warrior(String nickName) {
    	super(nickName);
    }
    
    public void attack() {
    	System.out.println("칼 휘두르기!");
    }
    
    public void skill() {
    	System.out.println("스킬 : 강타!");
    }
}

public class Archor extends Position {

    public Archor(String nickName) {
    	this.nickName = nickName;
    }
    
    public void attack() {
    	System.out.println("활 쏘기!");
    }
    
    public void skill() {
    	System.out.println("스킬 : 멀티샷!");
    }
}

public class Main {
    public static void main(String[] args) {
    	Position warrior = new Warrior("전사1");
        Position archor = new Archor("궁수1");
        
        System.out.println("nickName : " + warrior.getNickName());
        warrior.attack();
        warrior.skill();
        
        System.out.println("nickName : " + archor.getNickName());
        archor.attack();
        archor.skill();
    }
}

 

문제점

  1. 전사나 궁수의 스킬을 수정하려면 어떻게 해야 할까?
  2. 새로운 직업인 기사가 나왔고, 기존의 공격 또는 스킬을 추가하거나 수정하려면 어떻게 해야할까?

 

1. 전사나 궁수의 스킬을 수정하는 경우

아래와 같이 skill의 메서드를 수정해야 한다.

...

public class Warrior extends Position {

    ...
    
    public void skill() {
    	System.out.println("스킬 : 내려찍기!");
    }
}
...

public class Archor extends Position {

    ...
    
    public void skill() {
    	System.out.println("스킬 : 유성화살!");
    }
}

 

새로운 기능으로 수정하기 위해 기존 코드를 변경해야하며 이는 객체 지향 설계 5가지 원칙 중 개방 폐쇄 원칙 (OCP, Open-Closed Principle) 에 위배된다.

 

2. 새로운 직업인 기사에 공격, 스킬 방법을 추가 또는 수정하는 경우

새로운 직업인 기사를 추가하면 클래스 다이어그램은 아래와 같을 것이다.

기사는 전사와 동일하게 무기를 휘두르는 방식으로 공격을 한다고 하면 전사와 기사의 attack() 메서드가 중복해서 사용될 것이다.

이는 나중에 큰 문제를 발생시킬 수 있으며, 추후에 수정이 될 경우 이와 동일한 코드를 일일이 모두 수정해야 한다.

 

 

해결책

위와 같은 문제를 해결하기 위해서는 무엇이 변화되었는지 찾고, 이를 클래스로 캡슐화해야 한다.

  • 변화된 것 : 캐릭터의 공격 방식과 스킬 (즉, 새로운 방식의 기능들이 계속해서 추가될 수 있으므로 별다른 코드 변경 없이 기존의 공격 방식이나 스킬을 다른 공격 방식이나 스킬로 쉽게 변경할 수 있어야 한다.)

아래 그림의 AttackStrategy는 공격 방식을 캡슐화, SkillStrategy는 스킬을 캡슐화하는 인터페이스이다.

 

아래는 개선된 설계 그림이다.

 

Position 클래스 입장에서는 공격 방식과 스킬이 ~Strategy 인터페이스에 의해 캡슐화되어 있어 추후에 발생할 변화들에 대응할 수 있게 된다.

즉, 새로운 기능 추가가 기존의 코드에 영향을 미치지 못하게 하기 때문에 OCP를 만족하는 설계가 된다.

변경된 새로운 구조에서는 외부에서 직업들의 공격 방식과 스킬을 임의대로 변경할 수 있도록 해주는 메서드가 필요한데, 이는 set~ 메서드를 정의하여 수정가능하도록 하였다.

public abstract class Position {
    private String nickName;
    private AttackStrategy attackStrategy;
    private SkillStrategy skillStrategy;
    
    public Position(String nickName) {
    	this.nickName = nickName;
    }
    
    public String getNickName() {
    	return this.nickName;
    }
    
    public void attack() {
    	attackStrategy.attack();
    }
    
    public void skill() {
    	skillStrategy.skill();
    }
    
    public void setAttackStrategy(AttackStrategy attackStrategy) {
    	this.attackStrategy = attackStrategy;
    }
    
    public void setSkillStrategy(SkillStrategy skillStrategy) {
    	this.skillStrategy = skillStrategy;
    }
}

public class Warrior extends Position {
    public Warrior(String nickName) {
    	super(nickName);
    }
}

public class Archor extends Position {
    public Archor(String nickName) {
    	this.nickName = nickName;
    }
}

interface AttackStrategy {
    public void attack();
}

public class SwordStrategy implements AttackStrategy {
    public void attack() {
    	System.out.println("칼 휘두르기!");
    }
}

public class ArrowStrategy implements AttackStrategy {
    public void attack() {
    	System.out.println("활 쏘기!");
    }
}

interface SkillStrategy {
    public void skill();
}

public class StrikeStrategy implements SkillStrategy {
    public void skill() {
    	System.out.println("스킬 : 강타!");
    }
}

public class MultiShotStrategy implements SkillStrategy {
    public void skill() {
    	System.out.println("스킬 : 멀티샷!");
    }
}

public class Main {
    public static void main(String[] args) {
    	Position warrior = new Warrior("전사1");
        Position archor = new Archor("궁수1");
        
        warrior.setAttackStrategy(new SwordStrategy());
        warrior.setSkillStrategy(new StrikeStrategy());
        
        archor.setAttackStrategy(new ArrowStrategy());
        archor.setSkillStrategy(new MultiShotStrategy());
        
        System.out.println("nickName : " + warrior.getNickName());
        warrior.attack();
        warrior.skill();
        
        System.out.println("nickName : " + archor.getNickName());
        archor.attack();
        archor.skill();
    }
}

 

 

Reference

  • 도서 'UML과 GoF 디자인 패턴 핵심 10가지로 배우는 JAVA 객체 지향 디자인 패턴'
반응형

'디자인 패턴' 카테고리의 다른 글

Factory Method Pattern  (0) 2021.12.04
Decorator Pattern  (0) 2021.10.11
Singleton Pattern  (0) 2021.10.02
Template Method Pattern  (0) 2021.09.26
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
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
글 보관함