상태 패턴
Last updated
Last updated
객체의 내부 상태가 바뀜에 따라서 객체의 행동을 바꿀 수 있습니다. 마치 객체의 클래스가 바뀌는 것과 같은 결과를 얻을 수 있습니다.
뽑기 기계 회사에서 의뢰가 들어왔습니다.
원하는 상태 다이어그램을 보내주었고, 우리는 그에 맞춰 코드를 제작하면 될 겁니다.
제품을 성공적으로 납품했습니다. 다행히 뽑기 회사는 제품에 만족헀다고 합니다. 하지만, 추가 요청이 들어왔어요. 10분의 1의 확률로 알맹이가 2개가 나와야 된다고 합니다.
그런데 winner라는 상태값을 하나 추가해서 수정하려고 하니, 기존의 코드를 너무 많이 고쳐야 합니다. 다른 기능 추가 요청이 더 들어온다면 그때부턴 더 힘들어지겠죠.
아, 또 추가로 리필 기능이 필요하다고 합니다. 그 기능도 추가를 해야 해요!
현재 코드가 가지고 있는 문제점
OCP를 위반합니다.(변경에는 닫혀있고, 확장에는 열려있어야 한다.)
이런 디자인은 객체지향 디자인이라고 하기 힘듭니다.
상태 전환이 복잡한 조건문 속에 숨어있어서 디버깅하기 힘듭니다.
바뀌는 부분을 전혀 캡슐화하지 않습니다.
새로운 기능을 추가하는 과정에서 기존 코드에 없던 새로운 버그가 생길 가능성이 높습니다.
상태별 행동을 별도의 클래스에 넣어두고, 각자 자기 할 일을 구현하도록 하면 어떨까? 새로운 상태(winner)가 늘어난다면 그냥 클래스를 새로 추가하면 될 수 있도록 말이죠.
각 상태클래스에서 사용할 인터페이스를 만듭니다.
10% 당첨률을 추가한 WinnerState와 refill() 기능을 추가했습니다.
SoldState에서 해결하지 않고 WinnerState를 추가한 것은 SRP에 위배되기 때문입니다. 절대적으로 꼭 지켜야할 법칙은 아니지만, 특별 행사 기간이 끝나거나 당첨 확률이 달라지거나 하는 변수에 대응하기 위해서는 나누는 게 더 좋습니다.
상태 패턴을 적용해서 수정된 코드
각 상태의 행동을 별개의 클래스로 국지화했습니다.
관리하기 힘든 if문들을 없앴습니다.
각 상태는 변경에 닫혀 있게 했고, GumballMachine 클래스는 새로운 상태를 추가하는 확장에는 열려 있도록 고쳤습니다.(OCP)
오너가 처음 제시했던 다이어그램에 훨씬 가까우면서 더 이해하기 좋은 코드 베이스와 클래스 구조를 만들었습니다.
상태를 별도의 클래스로 캡슐화하고, 현재 상태를 나타내는 객체에게 행동을 위임합니다. 그래서 상태가 바뀔 때 행동이 달라지게 됩니다.
마치 객체가 바뀌는 것 같은 결과를 얻을 수 있는 것인데, 실제로 그런 것이 아니라 구성(Composition)으로 여러 상태 객체를 바꿔가면서 사용하는 거겠죠.
상태가 늘어남에 따라 클래스의 개수는 늘어납니다. 유연성을 향상시키려고 지불하는 비용이라고 생각하면 됩니다.
상태 패턴과 전략 패턴의 다이어그램은 똑같습니다. 하지만 사용 용도가 다르죠.
State Pattern(상태 패턴)
미리 몇 가지 상태를 가지고 작업하고, 미리 정해진 상태 전환 규칙에 따라(State 인터페이스) 알아서 자기 상태를 변경한다.
상태 객체에 일련의 행동이 캡슐화 됩니다. Context(GumballMachine) 객체의 상태에 따라 현재 상태를 나타내는 객체가 바뀌게 되고, 행동도 바뀝니다.
클라이언트는 상태 객체를 몰라도 됩니다.
Context 내에 수많은 if 문을 넣는 대신에 상태 별로 캡슐화해서 상태를 바꿔준다고 생각하면 됩니다.
Strategy Pattern(전략 패턴)
어떤 클래스를 인스턴스를 만들고, 그 인스턴스에게 어떤 행동을 구현하는 전략 객체를 건네준다.
클라이언트가 Context 객체에게 어떤 전략 객체를 사용할지를 지정합니다.
주로 실행 시에 전략 객체를 변경할 수 있는 유연성을 제공하는 용도로 쓰입니다.
상속을 사용해서 클래스의 행동을 정의한다면, 유연성이 떨어지기 때문에 구성을 사용해서 실행 중에 유연하게 행동을 바꾸는 용도로 쓰입니다.
https://www.hanbit.co.kr/store/books/look.php?p_code=B6113501223