전략 패턴(Strategy Pattern)

uglyduck.dev·2020년 9월 29일
2

개념 모아 🗂

목록 보기
33/40

정의

  • 전략을 쉽게 바꿀 수 있도록 해줌

  • 같은 문제를 해결하는 여러 알고리즘이 클래스별로 캡슐화되어 있고 이들이 필요할 때 교체할 수 있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게 하는 디자인 패턴

전략
어떤 목적을 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제를 해결하는 알고리즘
ex) 게임 프로그래밍에서 게임 캐릭터가 자신이 처한 상황에 따라 공격이나 행동하는 방식을 바꾸고 싶을 때

  • Strategy: 인터페이스나 추상 클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법을 명시

  • ConcreateStrategy1, 2, 3: 스트래티지 패턴에서 명시한 알고리즘을 실제로 구현한 클래스

  • Context: 스트래티지 패턴을 이용하는 역할 수행. 필요에 따라 동적으로 구체적인 전략을 바꿀 수 있도록 setter 메서드 제공

로봇 만들기

설계

  • 추상 클래스 Robot의 자식 클래스: Atom 클래스, TaekwonV 클래스
  • 추상 클래스 Robot의 추상 메서드 정의: attack method(), move method()

구현

Robot 추상 클래스

public abstract class Robot{
    private String name;
    public Robot(String name){
        this.name = name;
    }
    
    public String getName(){
        return name;
    }
    
    public abstract void attack();
    public abstract void move();
}

TaekwonV 구현 클래스

public class TaekwonV extends Robot{
    public TaekwonV(String name){
        super(name);
    }
    
    public void attack(){
        System.out.println("I have Missile and can attack with it.");
    }
    
    public void move(){
        System.out.println("I can only walk.");
    }
}

Atom 구현 클래스

public class Atom extends Robot{
    public Atom(String name){
        super(name);
    }
    
    public void attack(){
        System.out.println("I have strong punch and can attack with it.");
    }
    
    public void move(){
        System.out.println("I can fly.");
    }
}

Client 클래스

public class Client{
    public static void main(String[] args){
        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");
        
        System.out.println("My name is " + taekwonV.getName());
        taekwonV.move();
        taekwonV.attack();
        
        System.out.println();
        
        System.out.println("My name is " + atom.getName());
        atom.move();
        atom.attack();
    }
}

문제점

새로운 기능 수정의 경우

  • 기존 로봇의 공격 또는 이동 방법을 수정하려면 어떤 변경 작업을 해야 하는가?

    • ex) 아톰이 날 수는 없고 오직 걷게만 만들고 싶다면?, 태권V를 날게 하려면?
  • 새로운 로봇을 만들어 기존의 공격 또는 이동 방법을 추가하거나 수정하려면?

    • ex) 새로운 로봇으로 지구의 용사 선가드를 만들어 태권V의 미사일 공격 기능을 추가하려면?
  • 기존의 코드 내용 수정 발생(OCP 위배)
  • Atom 클래스의 move 메서드, TaekwonV 클래스의 move 메서드 기능의 중복

새로운 로봇에 공격/이동 방법을 추가/수정하는 경우

  • 새로운 로봇에 기존의 공격 또는 이동 방법을 추가하거나 변경하려고 하면 문제 발생

  • 다른 클래스와의 특정 메서드 중복 사용

  • 시스템 변경에 따른 기존 모든 코드 수정

해결책

  • 무엇이 변화되었는가? 에 중점

  • 변화된 것을 찾은 후 캡슐화

    • 이동 방식: move()
    • 공격 방식: attack()
  • 외부에서 구체적인 이동 방식, 공격 방식을 담은 클래스 은닉화

  • 공격, 이동을 위한 인터페이스 생성, 실현할 클래스를 만듦

  • 클라이언트: 연관 관계를 이용해 이동 기능과 공격 기능의 변화 포함(Robot 클래스)

  • MovingStrategy, AttackStrategy 인터페이스를 포함해야 함

개선된 설계, 구현

클래스클래스 설명
RobotRobot 클래스, 이동과 공격을 실행하는 메서드, 이를 상속받아 구체적인 로봇을 만듦
Atom, TaekwonVRobot 클래스를 상속받아 실제 로봇을 구현
<<interface>> AttackStrategy각 로봇이 취할 수 있는 공격 방법에 대한 인터페이스
PunchStrategy, MissileStrategy각 공격 방법을 실제로 구현함
<<interface>> MovingStrategy각 로봇이 취할 수 있는 이동 방법에 대한 인터페이스
WalkingStrategy, FlyingStrategy각 이동 방법을 실제로 구현함
  • 구체적인 이동 방식과 공격 방식이 MovingStrategy, AttackStrategy 인터페이스에 의해 캡슐화

    • 향후 이동 방식, 공격 방식의 변화를 수용할 수 있음
  • 새로운 공격 방식 개발 시

    • AttackStrategy 인터페이스가 변화에 대한 일종의 방화벽 역할을 수행해 Robot 클래스의 변경 차단
  • OCP(Open-Closed Principle) 만족하는 설계

    • 새로운 기능 추가 시, 기존의 코드에 영향을 미치지 않음
  • 외부에서 로봇 객체의 이동 방식과 공격 방식을 임의대로 변경해야하는 메서드 생성

    • setter를 통해 공격, 이동 방식 메서드를 정의해서 필요에 의해 변경 가능(집약 관계)

Robot 추상 클래스

public abstract class Robot{
    private String name;
    private MovingStrategy movingStrategy;
    private AttackStrategy attackStrategy;
    
    public Robot(String name){
        this.name = name;
    }
    
    public String getName(){
        return name;
    }
    
    public void move(){
        movingStrategy.move();
    }
    
    public void attack(){
        attackStrategy.attack();
    }
    
    public void setMovingStrategy(MovingStrategy movingStrategy){
        this.movingStrategy = movingStrategy;
    }
    
    public void setAttackStrategy(AttackStrategy attackStrategy){
        this.attackStrategy = attackStrategy;
    }
}

Atom 클래스

public class Atom extends Robot{
    public Atom(String name){
        super(name);
    }
}

TaekwonV 클래스

public class TaekwonV extends Robot{
    public TaekwonV(String name){
        super(name);
    }
}

MovingStrategy 인터페이스

interface MovingStrategy{
    public void move();
}

FlyingStrategy 구현체

public class FlyingStrategy implements MovingStrategy{
    public void move(){
        System.out.println("I can fly.");
    }
}

WalkingStrategy 구현체

public class WalkingStrategy implements MovingStrategy{
    public void move(){
        System.out.println("I can only walk.");
    }
}

AttackStrategy 인터페이스

interface AttackStrategy{
    public void attack();
}

MissileStrategy 구현체

public class MissileStrategy implements AttackStrategy{
    public void attack(){
        System.out.println("I have Missile and can attack with it.");
    }
}

PunchStrategy 구현체

public class PunchStrategy implements AttackStrategy{
    public void attack(){
        System.out.println("I have strong punch and can attack with it.");
    }
}

Client 클래스

public class Client{
    public static void main(String[] args){
        Robot taekwonV = new TaekwonV("TaekwonV");
        Robot atom = new Atom("Atom");
        
        taekwonV.setMovingStrategy(new WalkingStrategy());
        taekwonV.setAttackStrategy(new MissileStrategy());
        
        atom.setMovingStrategy(new WalkingStrategy()); // 이동 전략을 날아간다는 전략으로 설정
        atom.setAttackStrategy(new PunchStrategy()); // 공격 전략을 펀치를 구사하는 전략으로 설정
        
        System.out.println("My name is " + taekwonV.getName());
        taekwonV.move();
        taekwonV.attack();
        
        System.out.println();
        
        System.out.println("My name is " + atom.getName());
        atom.move();
        atom.attack();
    }
}

Reference

  • 정인상,채흥석, 『JAVA 객체 지향 디자인 패턴』, 한빛미디어(2014.4.2), 166p~179p
profile
시행착오, 문제해결 그 어디 즈음에.

4개의 댓글

comment-user-thumbnail
2022년 3월 27일

포스트 유익하게 잘봤습니다. ㅎㅎ 오타나 하나 있는데, 마지막 설계 사진에 AttackStrategy가 아닌 MovingStrategy로 표기되어있습니다.

1개의 답글
comment-user-thumbnail
2022년 9월 25일

덕분에 잘 이해하고 갑니다~
Robot 추상 클래스에 publlic 으로 오타 하나 나있습니다~

 publlic void attack(){
        attackStrategy.attack();
}
1개의 답글