>

언제나 다른 사람들보다 상태 디자인 패턴을 사용하는 것이 더 좋다고 말하는 사람들이 있습니다. 이유를 알고 싶습니다 :

<올>
  • 실제 사용 사례의 if-else에 대한 개선입니까?
  • 코드를 더 테스트 가능하게합니까?
  • 상태 패턴이 실제로 사례를 개선하는 예를 제공 할 수 있다면 if-else를 사용할 수있을 때 다음 상태 패턴의 오버 헤드를 가져 오는 이유를 이해하기 어렵습니다.

    인터넷을 통한 예만으로는이 패턴이 실제 문제를 해결하거나 코드를 유지 관리하기 쉽게 만드는 방법을 보여줄 수 없습니다.


    • 답변 # 1

      사실을 설명하기 위해 코드를 작성해야하지만 먼저 몇 가지 고려 사항을 살펴 보겠습니다. 상태 패턴을 사용하면 플로우 상태를 작성하고 해당 상태를 사용하여 어디로 가고 돌아갈 것인지 결정할 수 있습니다. 자, 객체를 사용할 때마다 현재 상태를 제어하고 내부 값을 변경하고 싶지 않다고 가정 해 봅시다. 이 경우 귀하의 주가 귀하를 대신하여 스스로를 통제하고 전화 할 때마다 귀하를 올바른 장소로 보낼 수 있습니다. 상태 패턴은 그래프를 처리 할 때 매우 유용하지만 GoF 디자인 패턴 Android 앱에서 본 다른 예를 보여 드리겠습니다.

      UML을 고려하십시오 :

      우리는 그것을 구현하려고한다.

      public interface AtmState {
          void withdraw(int amount);
          void refill(int amount);
      }
      public class Working implements AtmState {
          Atm atm;
          Working(Atm atm) {
              this.atm = atm;
          }
          public void withdraw(int amount) {
              int cashStock = atm.getCashStock();
              if(amount > cashStock) {
                  /* Insufficient fund.
                  * Dispense the available cash */
                  amount = cashStock;
                  System.out.print("Partial amount ");
              }
              System.out.println(amount + "$ is dispensed");
              int newCashStock = cashStock - amount;
              atm.setCashStock(newCashStock);
              if(newCashStock == 0) {
                  atm.setState(new NoCash(atm));
              }
          }
          public void refill(int amount) {
              System.out.println(amount + "$ is loaded");
              atm.setCashStock(atm.getCashStock()+amount);
          }
      }
      public class NoCash implements AtmState {
          Atm atm;
          NoCash(Atm atm) {
              this.atm = atm;
          }
          public void withdraw(int amount) {
              System.out.println("Out of cash");
          }
          public void refill(int amount) {
              System.out.println(amount + "$ is loaded");
              atm.setState(new Working(atm));
              atm.setCashStock(atm.getCashStock()+amount);
          }
      }
      
      
      이 시점에서 우리는 서로 상호 작용하는 두 가지 상태를 정의했으며, 자신이 다른 상태로 변경 될시기를 "알고"있으므로 객체 상태 변경시기를 처리하기 위해 컨트롤러를 만들 필요가 없습니다. 그들은 언제 변해야하는지 이미 알고 있습니다 이제 Atm 구현을 해봅시다 :

      public class Atm implements AtmState {
          int cashStock;
          AtmState currentState;
          public Atm() {
              currentState = new NoCash(this);
          }
          public int getCashStock() {
              return cashStock;
          }
          public void setCashStock(int CashStock) {
              this.cashStock = CashStock;
          }
          public void setState(AtmState state) {
              currentState = state;
          }
          public AtmState getState() {
              return currentState;
          }
          public void withdraw(int amount) {
              currentState.withdraw(amount);
          }
          public void refill(int amount) {
              currentState.refill(amount);
          }
      }
      
      
      이제 객체에 대한 두 개의 문장과 하나의 Atm 구현이 있습니다. 이제 NoCash 에 대해서만 테스트를 작성할 수 있도록 별도로 테스트 할 수 있습니다.  우리가 Working 에 대해 할 수있는 상태  상태. 보시다시피 더 세분화됩니다. 그리고 여기에 클라이언트 코드가 있습니다 :

      public class StateClient {
          public static void main(String [] args) {
              Atm atm = new Atm();
              atm.refill(100);
              atm.withdraw(50);
              atm.withdraw(30);
              atm.withdraw(30); // overdraft
              atm.withdraw(20); // overdraft
              atm.refill(50);
              atm.withdraw(50);
          }
      }
      
      

      출력 :

      100$ is loaded
      50$ is dispensed
      30$ is dispensed
      Partial amount 20$ is dispensed
      Out of cash
      50$ is loaded
      50$ is dispensed
      
      

      ATM의 상태를 처리 할 필요가 없으며 쉽게 테스트 할 수도 있습니다. if-else 문을 클라이언트 코드에 쓰지 않았을뿐 아니라, 상태를어디에 있어야하는지상태로 작성했습니다. 당신의 클라이언트. 고객은 모든 전화에 대한 정답을 받아야합니다.

      이전에 말했듯이,이 테스트는 훨씬 쉬워졌습니다. 이제 모든 상태에 대해 별도의 소규모 테스트를 수행 할 수 있습니다. 논리는 이해하기 쉬운 곳에 분산되어 있으며 코드를 이해하기가 더 쉽습니다.

      도움이 될 수 있기를 바랍니다.

    • 답변 # 2

      1) 항상 그렇듯이 일반적인 것이 더 낫습니까? 유형 질문은 의견의 문제 일 수도 있고 그렇지 않으면 "의존"이라고 대답 할 수도 있습니다. 실제 사용 사례의 예로는 여러 다른 프로세스 또는 스레드에 대한 제어 인 단일 인스턴스 객체가 있습니다. 프로세스는 상태에 따라 오브젝트를 폴링하고이를 기반으로 특정 로직을 처리 할 수 ​​있습니다.

      주차장을 고려하십시오. 폴링 될 때 차고가 가득 찼거나 가득 차 있지 않다는 것을 나타내는 폴링 프로세스 및 전체 시간에서 꽉 찬 시간으로 또는 가득 찬 시간에서 가득 찬 시간으로 변경 될 것으로 예상되는 시간을 알려주는 상태 머신이있을 수 있습니다. 일부 요인 (예 : 예약). 그런 다음 인바운드 트래픽, 아웃 바운드 트래픽, 잠재 고객에게 주차 공간, 주차 예약 시스템, 계절/학기/연간 패스 등을 알려주는 입구의 모니터를 모니터링하는 다른 프로세스가 있습니다. 차고가 가득 차 있는지 여부에 관계없이 다른 모든 것과 독립적입니다.

      2) 명시 적으로 상태를 설정할 수있는 객체는 쉽게 테스트 할 수 있습니다. 이것을 테스트하려는 상태로 만들기 위해 일부 로직을 조롱하는 것과 비교하면 후자는 더 (인간적인) 오류가 발생하기 쉽습니다.

    • 답변 # 3

      It is an improvement to if-else in real world use case ?

      만약 당신의 if-else가 간단한 비즈니스 로직을 포함한다면, state-design 패턴을 사용하는 것은 이치에 맞지 않습니다. 그러나 복잡한 비즈니스 로직이있는 경우 코드를 분리하고 유지 보수에 도움이되므로 상태 설계 패턴을 고소하는 것이 좋습니다. 또한 새로운 상태를 추가하려면 클래스로 갈 필요가 없으며 새 클래스를 만들고 런타임에 주입 할 수 있습니다.

      와이즈 비즈

      상태가 다른 클래스에 해당하기 때문에 하드 코딩되지 않았으므로 런타임에 삽입 할 수 있습니다. 즉 테스트 사례에 따라 조롱 할 수 있습니다. 따라서 단위 테스트가 더 쉬워집니다.

    • 답변 # 4

      if-else-clauses의 한 가지 문제점은 시간이 많이 걸리고 다른 코드를 변경해야하기 때문에 다른 상태를 추가하기가 매우 어렵다는 것입니다. 클래스. 메인 메뉴, 게임 루프 및 완성 된 화면으로 게임을 개발하려고한다고 상상해보십시오. 상태 패턴이 없으면 update-method 또는 draw 메소드와 같이 코드의 매우 다른 위치에서 프로그램의 현재 상태를 확인해야합니다. 네 번째 상태 (예 : 설정 화면)를 추가하려면 여러 가지 클래스의 코드를 수정해야합니다. 이상적이지 않습니다.

      그러나 State-pattern을 사용하면이 문제를 매우 우아하게 해결할 수 있습니다.

      Does it make code more testable ?

    관련 자료

  • 이전 Angular를 사용할 때 HTML"값"필드가 미리 채워지지 않습니다
  • 다음 python - SQLAlchemy에서 NULL을 기본값으로 삽입 중지/제거