특정 조건에서 특정 행동을 하는 상황에서는 우리는 흔히 분기문을 사용한다. 만약에 특정 조건이 추가된다면 조건문을 수정해야한다.
그 과정에서 OCP
원칙을 지키지 못 하게 된다. OCP
원칙을 지키면서 조건문을 추가하는 방법은 없을까?
State 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(상태 패턴)은 객체가 내부 상태에 따라 행동을 변경할 수 있도록 하는 디자인 패턴 중 하나입니다. 이 패턴은 객체의 상태를 캡슐화하고, 각 상태를 클래스로 표현하여 객체의 행동을 위임합니다. 이로써 객체는 상태가 변함에 따라 동적으로 다양한 행동을 수행할 수 있습니다.
-chatGPT
이제 Person
클래스를 수정해보자.
먼저 우리는 객체의 상태를 캡슐화해야한다. 쉽게 말하면, 객채가 내부적으로 어떤 상태를 가지고 있고 해당 상태에서 어떤 행동을 취할지를 숨기겠다는 뜻이다.
위와 같은 구조로 객체의 상태를 가져갈 것이다. 구현해보자.
public interface PersonState {
void eat();
}
public class Good implements PersonState {
@Override
public void eat() {
System.out.println("피자를 먹는다.");
}
}
public class Bad implements PersonState {
@Override
public void eat() {
System.out.println("죽을 먹는다.");
}
}
public class VeryBad implements PersonState {
@Override
public void eat() {
System.out.println("굶는다.");
}
}
PersonStatus
라는 상태에 대한 인터페이스를 만들고, 각각의 상태를 정의하고 해당 상태에는 어떤 행동을 할 지를 정의한 구체 클래스들을 만들었다.
이제 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();
}
}
Person
의 상태를 정의하는 final
변수가 사라졌다.Person
의 상태를 String
으로 가지고 있지 않고 캡슐화된 PersonStatus
로 가지고 있다.eat()
에 더이상 조건문이 존재하지 않고 어떤 행동을 할 것인지 PersonStatus
에게 위임한다.코드를 이렇게 수정하면 상태와 그에 맞는 행동을 추가하기 위해서 Person
클래스에 더이상 손을 대지 않아도 된다. 그리고 Person
이 어떤 상태에 어떤 행동을 하는 지는 각각의 구현체를 찾아가면 되기 때문에 유지보수에도 용이하다.
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();
}