"전략패턴"의 두 판 사이의 차이
greenwood26 (토론 | 기여) |
잔글 |
||
(사용자 2명의 중간 판 45개는 보이지 않습니다) | |||
1번째 줄: | 1번째 줄: | ||
− | ''' | + | '''전략패턴'''<!--전략 패턴-->(strategy pattern)이란 전략을 쉽게 바꿀 수 있도록 해주는 [[디자인패턴]](design pattern)이다. 여기에서 전략이란 어떤 목적을 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제를 해결하는 알고리즘 등으로 이해할 수 있다.<ref name = 'strategy01'>정인상, 채흥석, <[https://www.hanbit.co.kr/store/books/look.php?p_code=B3400922670 자바 객체지향 디자인 패턴: UML과 GoF 디자인 패턴 핵심 10가지로 배우는]>, <<한빛 미디어>>, 2014-04-28</ref> |
== 개요 == | == 개요 == | ||
− | + | [[디자인패턴]]에서 전략패턴(strategy pattern)을 따르는 클래스는 유연하고 재사용 가능한 객체 지향 소프트웨어를 설계하기 위해 반복되는 디자인 문제를 해결하는 방법, 즉 객체는 구현, 변경, 테스트, 재사용이 쉬워야 한다는것을 기술하는 23가지 GoF 디자인 패턴중 하나이다.<ref name = 'strategy02'>전략패턴 위키백과 - https://ko.wikipedia.org/wiki/%EC%A0%84%EB%9E%B5_%ED%8C%A8%ED%84%B4</ref> 같은 문제를 해결하는 여러 알고리즘이 클래스별로 캡슐화되어 있고 이들이 필요할 때 교체할 수 있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게하는 패턴이다.<ref name = 'strategy01'></ref> | |
== 특징 == | == 특징 == | ||
=== 목적 === | === 목적 === | ||
+ | 하나의 결과를 만드는 목적([[메소드]])는 동일하나, 그 목적을 달성할 수 있는 방법(전략, 알고리즘)이 여러가지 존재할 경우에 사용하는 패턴이다. Context의 변경을 최소화하고, 인터페이스를 통해 다양한 타입을 만들어 코드를 변경할 수 있다.<ref name = 'strategy03'>문메이, <[https://meylady.tistory.com/53 전략패턴(Strategy Pattern)]>, <<개인 블로그>>, 2018-04-03</ref> | ||
+ | |||
+ | === 장점 === | ||
+ | 새로운 알고리즘이 개발되더라도 기존에 작성했던 클래스의 코드를 수정하지 않고도 이를 쉽게 추가 적용할 수 있게 한다. SOLID원칙에서 개방-폐쇄원칙(OCP)를 만족한다.<ref name = 'strategy04'>favxoe, <[https://fabxoe.tistory.com/62 전략패턴]>, <<개인 블로그>>, 2019-11-06</ref> | ||
+ | |||
+ | == 구조 <ref name = 'strategy01'></ref>== | ||
+ | [[파일:전략패턴컬레보레이션.PNG|800픽셀|섬네일|가운데|'''전략패턴''' 컬레보레이션]] | ||
+ | Strategy는 인터페이스나 추상클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법을 명시한다. ConcreteStrategy는 전략패턴(strategy pattern)에서 명시한 알고리즘을 실제로 구현한 클래스이다. Context는 전략패턴(strategy pattern)을 이용하는 역할을 수행한다. 필요에 따라 동적으로 구체적인 전략을 바꿀 수 있도록 setter 메소드를 제공한다. | ||
+ | |||
+ | == 활용 예제 == | ||
+ | === 로봇 만들기 <ref name = 'strategy01'></ref>=== | ||
+ | 이 클래스 다이어그램에서 Atom 클래스와 TaekwonV 클래스는 Robot이라는 추상 클래스의 자식 클래스로 설정했다. 이렇게 설계한 이유는 아톰과 태권V는 둘 다 attack과 move기능이 있는 로봇의 한 종류 이기 때문이다. 아톰과 태권V의 이동 기능과 공격기능이 서로 다르기 때문에 Robot 클래스에서 attack과 move메서드를 추상메서드로 설정해 자식 클래스에서 각각 정의하도록한다. | ||
+ | |||
+ | ==== 문제점 ==== | ||
+ | 기존 로봇의 공격 또는 이동 방법을 수정하려면 기존에 작성했던 Atom 클래스나 TaekwonV 클래스의 직접적으로 메소드의 코드 수정이 필요하다. 또 만약 새로운 로봇을 만들어 기존의 공격 또는 이동 방법을 추가하거나 수정하려면 분명 기존에 있던 방법임에도 불구하고 어떤 기술도 참조할 수 없고 새로운 로봇에 똑같은 내용을 또 추가해줘야 하는 상황이 발생한다. 클래스의 직접적인 코드 수정은 로봇의 종류가 많아질수록 중복 코드를 일관성 있게 유지 관리하는 일은 매우 힘들고 대단한 집중력을 요구하고 또 새로운 로봇을 추가 했을 때 매번 기존 코드를 입력하면서 발생하는 중복코드는 나중에 심각한 문제를 일으킬 수 있다. 그리고 매번 새로운 방식이 개발될수록 새로운 방식을 로봇에게 제공하려면 현재 시스템에서 관련된 기존의 모든 코드를 수정해야한다. <ref name = 'strategy01></ref> | ||
+ | [[파일:전략기존코드예제모델.PNG|500픽셀|섬네일|가운데|로봇 패턴의 클래스 다이어그램]] | ||
+ | //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("미사일 공격"); | ||
+ | } | ||
+ | public void move() | ||
+ | { | ||
+ | System.out.println("걷는다."); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Atom 클래스 | ||
+ | public class Atom extends Robot | ||
+ | { | ||
+ | public Atom(String name) | ||
+ | { | ||
+ | super(name); | ||
+ | } | ||
+ | public void attack() | ||
+ | { | ||
+ | System.out.println("강펀치 공격"); | ||
+ | } | ||
+ | public void move() | ||
+ | { | ||
+ | System.out.println("비행"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Client 클래스 | ||
+ | public class Client | ||
+ | { | ||
+ | public static void main(String[] args) | ||
+ | { | ||
+ | Robot r1 = new TaekwonV("태권브이"); | ||
+ | Robot r2 =new Atom("아톰"); | ||
+ | //r1의 이동 공격 | ||
+ | System.out.println("내 이름은 " + r1.getName()); | ||
+ | r1.move(); | ||
+ | r1.attack(); | ||
+ | System.out.println(); | ||
+ | //r2의 이동 공격 | ||
+ | System.out.println("내 이름은 " + r2.getName()); | ||
+ | r2.move(); | ||
+ | r2.attack(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | <실행결과> | ||
+ | 내 이름은 태권브이 | ||
+ | 걷는다. | ||
+ | 미사일 공격 | ||
+ | |||
+ | 내 이름은 아톰 | ||
+ | 비행 | ||
+ | 강펀치 공격 | ||
+ | |||
+ | ==== 해결책 ==== | ||
+ | 무엇이 변화할 수 있을지 파악한 후에 이를 클래스로 캡슐화해야한다. 우리가 제시했던 변화 방법중에 로봇이 추가되는 겨우는 이미 캡슐화가 되어 있으니 이동 기능과 공격기능이 변할 수 있다는 것을 인지한 후 캡슐화를 해야하는데 외부에서 구체적인 이동 방식과 공격 방식을 담은 구체적인 클래스들을 은닉해야 한다 이를 위해 공격과 이동을 위한 인터페이스를 각각 만들고 이들을 시제 구현할 클래스를 만들어야한다. 클라이언트에서는 연관 관계를 이용해 이동 기능과 공격 기능의 변화를 포함시킨다. 이 예에서는 Robot 클래스가 이동 기능과 공격 기능을 이용하는 클라이언트 역할을 수행하고, 이 클래스는 변화를 처리하기 위해 MovingStrategy와 AttackStrategy 인터페이스를 포함해야한다. 아래의 실행결과를 보면 결과자체는 전략패턴을 쓰지 않았을 때와 유사하지만 로봇이 다른 전략으로 바꾸는데 기존 클래스를 변경해야할 요소가 사라졌다. | ||
+ | [[파일:전략패턴해결책.PNG|800픽셀|섬네일|가운데|전략패턴을 적용한 해결책]] | ||
+ | |||
+ | // 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("비행 이동"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //WalkingStrategy 클래스 | ||
+ | public class WalkingStrategy implements MovingStrategy | ||
+ | { | ||
+ | public void move() | ||
+ | { | ||
+ | System.out.println("도보 이동"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //AttackStrategy 인터페이스 | ||
+ | interface AttackStrategy | ||
+ | { | ||
+ | public void attack(); | ||
+ | } | ||
+ | |||
+ | //MissileStrategy 클래스 | ||
+ | public class MissileStrategy implements AttackStrategy | ||
+ | { | ||
+ | public void attack() | ||
+ | { | ||
+ | System.out.println("미사일 공격"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //PunchStrategy 클래스 | ||
+ | public class PunchStrategy implements AttackStrategy | ||
+ | { | ||
+ | public void attack() | ||
+ | { | ||
+ | System.out.println("강펀치 공격"); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //Client 클래스 | ||
+ | public class Client | ||
+ | { | ||
+ | public static void main(String[] args) | ||
+ | { | ||
+ | Robot r1 = new TaekwonV("태권브이"); | ||
+ | Robot r2 = new Atom("아톰"); | ||
+ | |||
+ | r1.setMovingStrategy(new WalkingStrategy()); | ||
+ | r1.setAttackStrategy(new MissileStrategy()); | ||
+ | |||
+ | r2.setMovingStrategy(new FlyingStrategy()); | ||
+ | r2.setAttackStrategy(new PunchStrategy()); | ||
+ | |||
+ | System.out.println("내 이름은 " + r1.getName()); | ||
+ | r1.move(); | ||
+ | r1.attack(); | ||
+ | |||
+ | System.out.println(); | ||
+ | |||
+ | System.out.println("내 이름은 " + r2.getName()); | ||
+ | r2.move(); | ||
+ | r2.attack(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | //실행결과 | ||
+ | 내 이름은 태권브이 | ||
+ | 도보 이동 | ||
+ | 미사일 공격 | ||
+ | |||
+ | 내 이름은 아톰 | ||
+ | 비행 이동 | ||
+ | 강펀치 공격 | ||
+ | |||
+ | [[파일:로봇만들기.PNG|800픽셀|섬네일|가운데|전략패턴을 적용한 로봇만들기의 설계]] | ||
+ | |||
{{각주}} | {{각주}} | ||
== 참고자료 == | == 참고자료 == | ||
+ | * 정인상, 채흥석, <[https://www.hanbit.co.kr/store/books/look.php?p_code=B3400922670 자바 객체지향 디자인 패턴: UML과 GoF 디자인 패턴 핵심 10가지로 배우는]>, <<한빛 미디어>>, 2014-04-28 | ||
+ | * 전략패턴 위키백과 - https://ko.wikipedia.org/wiki/%EC%A0%84%EB%9E%B5_%ED%8C%A8%ED%84%B4 | ||
+ | * 문메이, <[https://meylady.tistory.com/53 전략패턴(Strategy Pattern)]>, <<개인 블로그>>, 2018-04-03 | ||
+ | * favxoe, <[https://fabxoe.tistory.com/62 전략패턴]>, <<개인 블로그>>, 2019-11-06 | ||
+ | |||
== 같이보기 == | == 같이보기 == | ||
* [[디자인패턴]] | * [[디자인패턴]] | ||
* [[모델-뷰-컨트롤러 패턴]] | * [[모델-뷰-컨트롤러 패턴]] | ||
+ | * [[템플릿 메소드 패턴]] | ||
+ | |||
+ | {{프로그래밍|검토 필요}} |
2020년 8월 19일 (수) 22:06 기준 최신판
전략패턴(strategy pattern)이란 전략을 쉽게 바꿀 수 있도록 해주는 디자인패턴(design pattern)이다. 여기에서 전략이란 어떤 목적을 달성하기 위해 일을 수행하는 방식, 비즈니스 규칙, 문제를 해결하는 알고리즘 등으로 이해할 수 있다.[1]
개요[편집]
디자인패턴에서 전략패턴(strategy pattern)을 따르는 클래스는 유연하고 재사용 가능한 객체 지향 소프트웨어를 설계하기 위해 반복되는 디자인 문제를 해결하는 방법, 즉 객체는 구현, 변경, 테스트, 재사용이 쉬워야 한다는것을 기술하는 23가지 GoF 디자인 패턴중 하나이다.[2] 같은 문제를 해결하는 여러 알고리즘이 클래스별로 캡슐화되어 있고 이들이 필요할 때 교체할 수 있도록 함으로써 동일한 문제를 다른 알고리즘으로 해결할 수 있게하는 패턴이다.[1]
특징[편집]
목적[편집]
하나의 결과를 만드는 목적(메소드)는 동일하나, 그 목적을 달성할 수 있는 방법(전략, 알고리즘)이 여러가지 존재할 경우에 사용하는 패턴이다. Context의 변경을 최소화하고, 인터페이스를 통해 다양한 타입을 만들어 코드를 변경할 수 있다.[3]
장점[편집]
새로운 알고리즘이 개발되더라도 기존에 작성했던 클래스의 코드를 수정하지 않고도 이를 쉽게 추가 적용할 수 있게 한다. SOLID원칙에서 개방-폐쇄원칙(OCP)를 만족한다.[4]
구조 [1][편집]
Strategy는 인터페이스나 추상클래스로 외부에서 동일한 방식으로 알고리즘을 호출하는 방법을 명시한다. ConcreteStrategy는 전략패턴(strategy pattern)에서 명시한 알고리즘을 실제로 구현한 클래스이다. Context는 전략패턴(strategy pattern)을 이용하는 역할을 수행한다. 필요에 따라 동적으로 구체적인 전략을 바꿀 수 있도록 setter 메소드를 제공한다.
활용 예제[편집]
로봇 만들기 [1][편집]
이 클래스 다이어그램에서 Atom 클래스와 TaekwonV 클래스는 Robot이라는 추상 클래스의 자식 클래스로 설정했다. 이렇게 설계한 이유는 아톰과 태권V는 둘 다 attack과 move기능이 있는 로봇의 한 종류 이기 때문이다. 아톰과 태권V의 이동 기능과 공격기능이 서로 다르기 때문에 Robot 클래스에서 attack과 move메서드를 추상메서드로 설정해 자식 클래스에서 각각 정의하도록한다.
문제점[편집]
기존 로봇의 공격 또는 이동 방법을 수정하려면 기존에 작성했던 Atom 클래스나 TaekwonV 클래스의 직접적으로 메소드의 코드 수정이 필요하다. 또 만약 새로운 로봇을 만들어 기존의 공격 또는 이동 방법을 추가하거나 수정하려면 분명 기존에 있던 방법임에도 불구하고 어떤 기술도 참조할 수 없고 새로운 로봇에 똑같은 내용을 또 추가해줘야 하는 상황이 발생한다. 클래스의 직접적인 코드 수정은 로봇의 종류가 많아질수록 중복 코드를 일관성 있게 유지 관리하는 일은 매우 힘들고 대단한 집중력을 요구하고 또 새로운 로봇을 추가 했을 때 매번 기존 코드를 입력하면서 발생하는 중복코드는 나중에 심각한 문제를 일으킬 수 있다. 그리고 매번 새로운 방식이 개발될수록 새로운 방식을 로봇에게 제공하려면 현재 시스템에서 관련된 기존의 모든 코드를 수정해야한다. [1]
//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("미사일 공격"); } public void move() { System.out.println("걷는다."); } }
//Atom 클래스 public class Atom extends Robot { public Atom(String name) { super(name); } public void attack() { System.out.println("강펀치 공격"); } public void move() { System.out.println("비행"); } }
//Client 클래스 public class Client { public static void main(String[] args) { Robot r1 = new TaekwonV("태권브이"); Robot r2 =new Atom("아톰"); //r1의 이동 공격 System.out.println("내 이름은 " + r1.getName()); r1.move(); r1.attack(); System.out.println(); //r2의 이동 공격 System.out.println("내 이름은 " + r2.getName()); r2.move(); r2.attack(); } }
<실행결과> 내 이름은 태권브이 걷는다. 미사일 공격 내 이름은 아톰 비행 강펀치 공격
해결책[편집]
무엇이 변화할 수 있을지 파악한 후에 이를 클래스로 캡슐화해야한다. 우리가 제시했던 변화 방법중에 로봇이 추가되는 겨우는 이미 캡슐화가 되어 있으니 이동 기능과 공격기능이 변할 수 있다는 것을 인지한 후 캡슐화를 해야하는데 외부에서 구체적인 이동 방식과 공격 방식을 담은 구체적인 클래스들을 은닉해야 한다 이를 위해 공격과 이동을 위한 인터페이스를 각각 만들고 이들을 시제 구현할 클래스를 만들어야한다. 클라이언트에서는 연관 관계를 이용해 이동 기능과 공격 기능의 변화를 포함시킨다. 이 예에서는 Robot 클래스가 이동 기능과 공격 기능을 이용하는 클라이언트 역할을 수행하고, 이 클래스는 변화를 처리하기 위해 MovingStrategy와 AttackStrategy 인터페이스를 포함해야한다. 아래의 실행결과를 보면 결과자체는 전략패턴을 쓰지 않았을 때와 유사하지만 로봇이 다른 전략으로 바꾸는데 기존 클래스를 변경해야할 요소가 사라졌다.
// 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("비행 이동"); } }
//WalkingStrategy 클래스 public class WalkingStrategy implements MovingStrategy { public void move() { System.out.println("도보 이동"); } }
//AttackStrategy 인터페이스 interface AttackStrategy { public void attack(); }
//MissileStrategy 클래스 public class MissileStrategy implements AttackStrategy { public void attack() { System.out.println("미사일 공격"); } }
//PunchStrategy 클래스 public class PunchStrategy implements AttackStrategy { public void attack() { System.out.println("강펀치 공격"); } }
//Client 클래스 public class Client { public static void main(String[] args) { Robot r1 = new TaekwonV("태권브이"); Robot r2 = new Atom("아톰"); r1.setMovingStrategy(new WalkingStrategy()); r1.setAttackStrategy(new MissileStrategy()); r2.setMovingStrategy(new FlyingStrategy()); r2.setAttackStrategy(new PunchStrategy()); System.out.println("내 이름은 " + r1.getName()); r1.move(); r1.attack(); System.out.println(); System.out.println("내 이름은 " + r2.getName()); r2.move(); r2.attack(); } } //실행결과 내 이름은 태권브이 도보 이동 미사일 공격
내 이름은 아톰 비행 이동 강펀치 공격
각주[편집]
- ↑ 1.0 1.1 1.2 1.3 1.4 정인상, 채흥석, <자바 객체지향 디자인 패턴: UML과 GoF 디자인 패턴 핵심 10가지로 배우는>, <<한빛 미디어>>, 2014-04-28
- ↑ 전략패턴 위키백과 - https://ko.wikipedia.org/wiki/%EC%A0%84%EB%9E%B5_%ED%8C%A8%ED%84%B4
- ↑ 문메이, <전략패턴(Strategy Pattern)>, <<개인 블로그>>, 2018-04-03
- ↑ favxoe, <전략패턴>, <<개인 블로그>>, 2019-11-06
참고자료[편집]
- 정인상, 채흥석, <자바 객체지향 디자인 패턴: UML과 GoF 디자인 패턴 핵심 10가지로 배우는>, <<한빛 미디어>>, 2014-04-28
- 전략패턴 위키백과 - https://ko.wikipedia.org/wiki/%EC%A0%84%EB%9E%B5_%ED%8C%A8%ED%84%B4
- 문메이, <전략패턴(Strategy Pattern)>, <<개인 블로그>>, 2018-04-03
- favxoe, <전략패턴>, <<개인 블로그>>, 2019-11-06
같이보기[편집]