본문 바로가기
Development, Architecture/Design Pattern

[디자인 패턴] State Pattern 스테이트 패턴

by ggyongi 2021. 12. 1.
반응형

State Pattern

Introduction

게시물의 좋아요/싫어요 버튼 기능을 만들어봅시다!

Definition

스테이트 패턴을 이용하면 객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있습니다.
마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있습니다.

Structure

image
스테이트 패턴은 전략 패턴과 동일한 구조를 가집니다.

Source Code

게시물에 좋아요/싫어요 기능을 만들어봅시다.
우선 게시물 클래스를 만들어줍니다. 스테이트 패턴의 Context 클래스에 해당합니다.

package state_pattern;

public class Article {

    State state = new IdleState();

    public void setState(State state) {
        this.state = state;
    }

    public void likeClick() {
        state.like(this);
    }

    public void dislikeClick() {
        state.dislike(this);
    }
}

그리고 State 인터페이스를 만들어줍니다.
전략 패턴 때와 다른 점은 메서드의 인자로 Context 객체를 받습니다.

package state_pattern;

public interface State {
    public void like(Article article);
    public void dislike(Article article);
}

State 인터페이스를 구현하는 세가지 클래스를 만들어줍니다.
인자로 받은 article 객체의 상태를 상황에 따라 적절히 바꿔줍니다.

먼저, Idle상태에서는 좋아요 클릭시 Like상태로, 싫어요 클릭시 Dislike 상태가 됩니다.

package state_pattern;

public class IdleState implements State {

    @Override
    public void like(Article article) {
        article.setState(new LikeState());
        System.out.println("좋아요!");
    }

    @Override
    public void dislike(Article article) {
        article.setState(new DislikeState());
        System.out.println("싫어요!");

    }

}

Like 상태에서는 좋아요를 또 누르게 되면 좋아요가 해제되어 Idle 상태가 되고,
싫어요를 누르면 Dislike 상태가 됩니다.

package state_pattern;

public class LikeState implements State {

    @Override
    public void like(Article article) {
        article.setState(new IdleState());
        System.out.println("좋아요 해제!");
    }

    @Override
    public void dislike(Article article) {
        article.setState(new DislikeState());
        System.out.println("좋아요에서 싫어요로 변경!");
    }

}

Dislike 상태에서 좋아요를 누르면 Like 상태가 되고, 싫어요 버튼을 한번더 누르게 되면 싫어요가 해제되어 Idle 상태가 됩니다.

package state_pattern;

public class DislikeState implements State {

    @Override
    public void like(Article article) {
        article.setState(new LikeState());
        System.out.println("싫어요에서 좋아요로 변경!");
    }

    @Override
    public void dislike(Article article) {
        article.setState(new IdleState());
        System.out.println("싫어요 해제!");

    }

}

테스트는 아래와 같이 진행됩니다.
article에 좋아요, 싫어요 버튼을 연속적으로 무질서하게 눌렀을 때,
우리가 의도한 상태로 적절히 바뀌는 지 파악해봅시다.

package state_pattern;

public class PatternTest {

    public static void main(String[] args) {
        Article article = new Article();

        article.likeClick();     // Idle -> Like
        article.dislikeClick();  // Like -> Dislike
        article.likeClick();     // Dislike -> Like
        article.dislikeClick();  // Like -> Dislike
        article.dislikeClick();  // Dislike -> Idle
        article.dislikeClick();  // Idle -> Dislike
        article.dislikeClick();  // Dislike -> Idle
        article.likeClick();     // Idle -> Like
    } 
}
좋아요!
좋아요에서 싫어요로 변경!
싫어요에서 좋아요로 변경!
좋아요에서 싫어요로 변경!
싫어요 해제!
싫어요!
싫어요 해제!
좋아요!

Question

구조도 동일한데, 스테이트 패턴은 전략 패턴과 무엇이 다른가요?
(전략 패턴 보러가기: 여기)

구조는 동일하지만 용도에서 미묘한 차이가 있습니다.

아래는 전략 패턴의 클라이언트 부분을 가져온 것입니다.
전략 패턴에서는 클라이언트가 직접 전략 수정을 위해서 새 전략을 심어주어야 했습니다. 즉 전략 패턴은 실행 시에 클라이언트에게 전략을 변경할 수 있는 유연성을 제공하기 위한 용도로 사용됩니다.

package strategy_pattern;

public class Test {

    public static void main(String[] args) {

        Person kim = new Person(new Walk());
        kim.performMove();

        Person lee = new Person(new Bus());
        lee.performMove();

        //lee가 차를 구입 -> 전략을 수정
        lee.setMs(new Car());
        lee.performMove();        
    }
}

반면, 스테이트 패턴의 클라이언트 부분 코드를 보면, 스테이트(상태)를 직접 변경해주지는 않습니다.
단지 클라이언트는 좋아요 또는 싫어요 버튼을 누르는 것뿐이고, 그 상황에 맞게 스테이트는 Idle/Like/Dislike로 알아서 적절히 변경됩니다.
이처럼 스테이트 패턴에서는 상황에 따라 즉 객체의 내부 상태에 따라 현재 스테이트를 나타내는 객체가 바뀌게 되고, 그 결과로 컨텍스트 객체의 행동도 자연스럽게 바뀌게 됩니다.
클라이언트 입장에서는 스테이트 객체에 대해서 아무것도 알 필요가 없어지게 되는 것이죠.

Reference

책 - 헤드퍼스트 디자인패턴

 

비전공자 네카라 신입 취업 노하우

시행착오 끝에 얻어낸 취업 노하우가 모두 담긴 전자책!

kmong.com

댓글