[Design Pattern] State Pattern

노유성·2024년 1월 13일
0
post-thumbnail

들어가며

특정 조건에서 특정 행동을 하는 상황에서는 우리는 흔히 분기문을 사용한다. 만약에 특정 조건이 추가된다면 조건문을 수정해야한다.

그 과정에서 OCP원칙을 지키지 못 하게 된다. OCP원칙을 지키면서 조건문을 추가하는 방법은 없을까?

State Pattern을 이용해서 해결해보자.

No Pattern

public class Person {
    public final static String GOOD = "good";
    public final static String BAD = "BAD";

    public String status;

    public Person(String status) {
        this.status = status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public void eat() {
        if (status.equals(GOOD)) {
            System.out.println("피자를 먹는다.");
        } else if (status.equals(BAD)) {
            System.out.println("죽을 먹는다.");
        } 
    }
}

Person 클래스는 밥을 먹는다. 그런데 속이 나쁘면은 죽을 먹을 것이고 속이 좋으면은 피자를 먹는다.

eat() 메소드를 보면은 객체의 상태에 따라서 조건문을 통과해 죽을 먹든, 피자를 먹든하고 있다.

조건을 추가하자.

만약에 여기서 속이 너무너무 안 좋으면은 아무것도 먹지 않고 굶어야한다는 조건이 추가된다면 코드는 어떻게 될까?

public class Person {
 	...
    public final static String VERY_BAD = "VERY_BAD";
    
	...
 	
    public void eat() {
        if (status.equals(GOOD)) {
            System.out.println("피자를 먹는다.");
        } else if (status.equals(BAD)) {
            System.out.println("죽을 먹는다.");
        } else if (status.equals(VERY_BAD)) {
            System.out.println("굶는다.");
        }
    }
}

VERY_BAD라는 상태가 추가되고 eat() 메소드에도 변화가 생겼다. 이로써 OCP원칙이 깨지고 만약에 저 if문이 엄청나게 길어진다면? 유지보수하기가 어려울 것이다.

State Pattern

State Pattern이란 특정 상황에 어떤 행동을 할 것인지에 대해서 상황 - 행동을 매핑해서 하나의 객체로 해당 상태를 가지고 있는 것이다.

State Pattern(상태 패턴)은 객체가 내부 상태에 따라 행동을 변경할 수 있도록 하는 디자인 패턴 중 하나입니다. 이 패턴은 객체의 상태를 캡슐화하고, 각 상태를 클래스로 표현하여 객체의 행동을 위임합니다. 이로써 객체는 상태가 변함에 따라 동적으로 다양한 행동을 수행할 수 있습니다.
-chatGPT

이제 Person 클래스를 수정해보자.

PersonStatus

먼저 우리는 객체의 상태를 캡슐화해야한다. 쉽게 말하면, 객채가 내부적으로 어떤 상태를 가지고 있고 해당 상태에서 어떤 행동을 취할지를 숨기겠다는 뜻이다.


위와 같은 구조로 객체의 상태를 가져갈 것이다. 구현해보자.

public interface PersonState {
    void eat();
}

Good

public class Good implements PersonState {
    @Override
    public void eat() {
        System.out.println("피자를 먹는다.");
    }
}

Bad

public class Bad implements PersonState {

    @Override
    public void eat() {
        System.out.println("죽을 먹는다.");
    }
}

VeryBad

public class VeryBad implements PersonState {

    @Override
    public void eat() {
        System.out.println("굶는다.");
    }
}

PersonStatus라는 상태에 대한 인터페이스를 만들고, 각각의 상태를 정의하고 해당 상태에는 어떤 행동을 할 지를 정의한 구체 클래스들을 만들었다.

이제 Person 클래스를 수정해보자.

Person

public class Person {
    public PersonState state;

    public Person(PersonState state) {
        this.state = state;
    }

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

    public void eat() {
        state.eat();
    }
}
  1. Person의 상태를 정의하는 final 변수가 사라졌다.
  2. Person의 상태를 String으로 가지고 있지 않고 캡슐화된 PersonStatus로 가지고 있다.
  3. eat()에 더이상 조건문이 존재하지 않고 어떤 행동을 할 것인지 PersonStatus에게 위임한다.

코드를 이렇게 수정하면 상태와 그에 맞는 행동을 추가하기 위해서 Person 클래스에 더이상 손을 대지 않아도 된다. 그리고 Person이 어떤 상태에 어떤 행동을 하는 지는 각각의 구현체를 찾아가면 되기 때문에 유지보수에도 용이하다.

Main

public static void main(String[] args) {
    Person person = new Person(new Bad());

    person.eat();

    person.setState(new VeryBad());
    person.eat();

    person.setState(new Good());
    person.eat();
}
profile
풀스택개발자가되고싶습니다:)

0개의 댓글