상태 패턴
- 클래스 내의 여러 메서드가 하나의 상태(멤버 변수)에 의존하여 switch 문이 만들어지는 경우
- 하나의 메서드에서 각 상태에 따라 수행하는 로직이 다른 경우
- 각 상태를 클래스로 만들고 수행하는 로직은 각 클래스에 작성한다.
- 여러 상태가 추가되는 경우 확장이 용이하고, 상태의 전환에 대해 인스턴스를 바꾸어주면 되므로 유지보수가 쉽다.
전략 패턴
- 어떤 로직에 대해 여러 알고리즘(전략, 정책)이 있을 수 있는 경우
- 상황에 따라 전략을 교체할 수 있고, 선택이 추가될 수 있음
- 각 전략이 해결해야 하는 동일한 메서드가 존재함
- 메서드에 전략에 따른 다른 수행이 있음
- 전략마다 클래스로 만들고, 각 클래스에서 해당하는 로직을 수행하도록 하는 메서드 구현
상태 패턴과 전략 패턴
- 두 패턴 모두 상속과 오버라이딩을 활용한 패턴
- 상태 패턴은 클래스의 상태(주로 변수)에 관련하여 여러 가지 수행이 있는 경우이고, 전략 패턴은 알고리즘이 여러개 존재하는 경우
전략 패턴과 상태 패턴의 공통점과 차이점
- 공통점
- 둘 다 다형성을 사용해서 Concreate class를 캡슐화 한다는 점.
- 따라서 Context Class에서 어떤 구상 클래스가 할당되었는지 관계 없이 로직을 실행하면 된다. (변경 대처에 유연하다.)
- 차이점 (정확한지는 잘 모르겠으나, 생각해본 바 적음)
- Concreate Class를 외부에서 주입(파라메터로)받아서 사용하도록 구현하면 전략 패턴, 내부에서 만들 수 있으면 상태 패턴으로 치는 것으로 보임.
- 전략 패턴 예제로 게임에서 player 객체에서 무기에 따른 공격 로직 (attack()메서드) 주입받는 것을 예시로 많이 드는데, 공격 로직은 전략 패턴으로 외부 Client에서 Player 객체에 주입받는 것이 맞는 것 같음.
상태 패턴으로 구현한다면 객체에 무기에 따른 모든 공격 로직을 생성하는 메서드를 Player 객체에서 가지고 있어야 한다는 건데, 맞지 않아 보임. - 위 예시와 반대로 선풍기의 ON/OFF, 혹은 특정 모드(상태) 에 따라 로직이 달라지는 특성을 가진 객체를 구현할 때는 해당 객체에서 그 객체가 가질 수 있는 상태 클래스(로직)을 생성하는 로직을 가지고 있는 것이 캡슐화 적인 부분에서나 맞는 것으로 보임
- 전략 패턴 예제로 게임에서 player 객체에서 무기에 따른 공격 로직 (attack()메서드) 주입받는 것을 예시로 많이 드는데, 공격 로직은 전략 패턴으로 외부 Client에서 Player 객체에 주입받는 것이 맞는 것 같음.
- Concreate Class를 외부에서 주입(파라메터로)받아서 사용하도록 구현하면 전략 패턴, 내부에서 만들 수 있으면 상태 패턴으로 치는 것으로 보임.
AS-IS
package refactoringwithstatepattern.before;
public class Player {
public static final int BEGINNER_LEVEL = 1;
public static final int ADVANCED_LEVEL = 2;
public static final int SUPER_LEVEL = 3;
private int level;
public Player() {
level = BEGINNER_LEVEL;
showLevelMessage();
}
public int getLevel() {
return level;
}
public void upgradeLevel() {
if(level == BEGINNER_LEVEL) {
level = ADVANCED_LEVEL;
}
else if(level == ADVANCED_LEVEL) {
level = SUPER_LEVEL;
}
else {
System.out.println("not support level");
}
showLevelMessage();
}
private String showLevelMessage() {
// TODO Auto-generated method stub
return "현재 래벨은 " + String.valueOf(level) + "입니다.";
}
public void play(int count) {
run();
for(int i=0; i<count; i++) {
jump();
}
turn();
}
public void run() {
if (level == BEGINNER_LEVEL) {
System.out.println("천천히 달립니다.");
}
else if(level == ADVANCED_LEVEL) {
System.out.println("빨리 달립니다.");
}
else if(level == SUPER_LEVEL) {
System.out.println("엄청 빨리 달립니다.");
}
else {
System.out.println("not support level");
}
}
public void jump() {
if (level == BEGINNER_LEVEL) {
System.out.println("Jump 할 줄 모릅니다.");
}
else if(level == ADVANCED_LEVEL) {
System.out.println("높이 jump 합니다.");
}
else if(level == SUPER_LEVEL) {
System.out.println("엄청 높게 jump 합니다.");
}
else {
System.out.println("not support level");
}
}
public void turn() {
if (level == BEGINNER_LEVEL) {
System.out.println("turn 할 줄 모릅니다.");
}
else if(level == ADVANCED_LEVEL) {
System.out.println("turn 할 줄 모릅니다.");
}
else if(level == SUPER_LEVEL) {
System.out.println("한 바퀴 돕니다.");
}
else {
System.out.println("not support level");
}
}
}
package refactoringwithstatepattern.before;
public class MainBoard {
public static void main(String[] args) {
Player player = new Player();
player.play(1);
player.upgradeLevel();
player.play(2);
player.upgradeLevel();
player.play(3);
}
}
TO-BE - 템플릿 메서드 패턴(PlayerLevel), 상태 패턴, 싱글턴 패턴 적용한 결과
package refactoringwithstatepattern.after2;
public class Player {
private PlayerLevel playerLevel;
public Player() {
playerLevel = BeginnerLevel.getInstance();
playerLevel.showLevelMessage();
}
public PlayerLevel getLevel() {
return playerLevel;
}
public void upgradeLevel() {
if(playerLevel instanceof BeginnerLevel) {
playerLevel = AdvancedLevel.getInstance();
}
else if(playerLevel instanceof AdvancedLevel) {
playerLevel = SuperLevel.getInstance();
}
else {
System.out.println("not support level");
}
playerLevel.showLevelMessage();
}
public void play(int count) {
playerLevel.go(count);
}
}
package refactoringwithstatepattern.after2;
public abstract class PlayerLevel {
protected abstract void run();
protected abstract void jump();
protected abstract void turn();
protected abstract void showLevelMessage();
final public void go(int count) {
run();
for(int i=0; i<count; i++) {
jump();
}
turn();
}
}
package refactoringwithstatepattern.after2;
public class SuperLevel extends PlayerLevel{
private static SuperLevel instance = new SuperLevel();
private SuperLevel() {}
public static SuperLevel getInstance() {
if( instance == null ) {
instance = new SuperLevel();
}
return instance;
}
@Override
protected void run() {
System.out.println("엄청 빨리 달립니다.");
}
@Override
protected void jump() {
System.out.println("엄청 높게 jump 합니다.");
}
@Override
protected void turn() {
System.out.println("한 바퀴 돕니다.");
}
@Override
protected void showLevelMessage() {
System.out.println("*** 고급자 래벨 입니다. ***");
}
}
package refactoringwithstatepattern.after2;
public class AdvancedLevel extends PlayerLevel{
private static AdvancedLevel instance = new AdvancedLevel();
private AdvancedLevel() {}
public static AdvancedLevel getInstance() {
if( instance == null ) {
instance = new AdvancedLevel();
}
return instance;
}
@Override
protected void run() {
System.out.println("빨리 달립니다.");
}
@Override
protected void jump() {
System.out.println("높이 jump 합니다.");
}
@Override
protected void turn() {
System.out.println("turn 할 줄 모릅니다.");
}
@Override
protected void showLevelMessage() {
System.out.println("*** 중급자 래벨 입니다. ***");
}
}
package refactoringwithstatepattern.after2;
public class BeginnerLevel extends PlayerLevel{
private static BeginnerLevel instance = new BeginnerLevel();
private BeginnerLevel() {}
public static BeginnerLevel getInstance() {
if( instance == null ) {
instance = new BeginnerLevel();
}
return instance;
}
@Override
protected void run() {
System.out.println("천천히 달립니다.");
}
@Override
protected void jump() {
System.out.println("Jump 할 줄 모릅니다.");
}
@Override
protected void turn() {
System.out.println("turn 할 줄 모릅니다.");
}
@Override
protected void showLevelMessage() {
System.out.println("*** 초급자 래벨 입니다. ***");
}
}
package refactoringwithstatepattern.after2;
public class MainBoard {
public static void main(String[] args) {
Player player = new Player();
player.play(1);
player.upgradeLevel();
player.play(2);
player.upgradeLevel();
player.play(3);
}
}
'java > java 리팩토링' 카테고리의 다른 글
08. 상속을 위임으로 바꾸기 (0) | 2023.02.12 |
---|---|
06. 생성자를 팩토리 메서드로 바꾸기 (0) | 2023.02.10 |
05. if-else, if-switch 문의 분류 코드를 하위 클래스로 만들기 (0) | 2023.02.08 |
04. 분류 코드를 클래스로 만들기 (0) | 2023.02.08 |
03. null 객체 사용 (0) | 2023.02.07 |
댓글