Java 디자인 패턴-07.State

(Java 디자인 패턴 스터디 모집 중 : https://github.com/bluedskim/javaDesignPatterns)

패턴 다이어그램

State “어떤 객체의 상태에 따라 행위가 변경되는 경우 유용한 패턴” (출처:https://refactoring.guru/design-patterns/state)

해결하려는 문제

  1. context의 상태에 따른 처리가 구분되는 경우 해당 구분로직을 context에 하드 코딩하게 되면 경우 확장성이 떨어진다.

특징/용도

  1. 상태를 클래스화 하고 해당 동작들을 넣는다
  2. 원래의 객체(context)는 현재 상태 객체의 참조를 가진다
  3. context는 2개 이상의 상태를 동시에 가질 수는 없다

고려사항

  1. State 패턴은 Strategy 패턴과 유사하다. 주요한 차이점은 상태객체들은 서로의 존재를 아는 반면 Strategy패턴에서는 Strategy간의 관련성은 없다는 것이다(https://refactoring.guru/design-patterns/state)

클래스 다이어그램

State 패턴(김동석)

소스

  1. Context : 상태를 가지고있는 객체(Package.java)
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    package net.dskim.desingpattern.state.solution;
    
    /**
     * 주문 객체
     */
    public class Package {
    	private PackageState currentState = new OrderedState();
    
    	public void setState(PackageState newState) {
    		this.currentState = newState;
    	}
    
    	public PackageState getState() {
    		return this.currentState;
    	}
    
    	/**
    	 * 상태 변경시 알려줄 대상 목록 반환
    	 * @return
    	 */
    	public String getNotifyGetterList() {
    		return currentState.getNotifyGetterList();
    	}
    }
  2. State
    • OrderedState.java
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      package net.dskim.desingpattern.state.solution;
      
      /**
       * 상태 객체-주문
       */
      public class OrderedState implements PackageState {
      	/**
      	 * 알림 수신 대상자 목록 반환
      	 */	
      	@Override
      	public String getNotifyGetterList() {
      		return "shipper";
      	}
      		
      	@Override
      	public String toString() {
      		return "OrderedState";
      	}
      }
    • DelieveredState.java
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      package net.dskim.desingpattern.state.solution;
      
      /**
       * 상태 객체-배송완료
       */
      public class DeliveredState implements PackageState {
      	/**
      	 * 알림 수신 대상자 목록 반환
      	 */
      	@Override
      	public String getNotifyGetterList() {
      		return "orderer";
      	}
      
      	@Override
      	public String toString() {
      		return "DeliveredState";
      	}
      }
    • ReceivedState.java
       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      
      package net.dskim.desingpattern.state.solution;
      
      /**
       * 상태 객체-수령완료
       */
      public class ReceivedState implements PackageState {
      	/**
      	 * 알림 수신 대상자 목록 반환
      	 */	
      	@Override
      	public String getNotifyGetterList() {
      		return "orderer,manager";
      	}
      
      	@Override
      	public String toString() {
      		return "ReceivedState";
      	}
      }
  3. client : StateTest.java
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    package net.dskim.desingpattern.state.solution;
    
    import static org.junit.jupiter.api.Assertions.assertEquals;
    
    import org.junit.jupiter.api.Test;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j
    public class StateTest {
    
    	@Test
    	void stateTest() {
    		Package pkg = new Package();
    		
    		log.info("state={}", pkg.getState());
    		assertEquals("shipper", pkg.getNotifyGetterList());
    
    		pkg.setState(new DeliveredState());
    		log.info("state={}", pkg.getState());
    		assertEquals("orderer", pkg.getNotifyGetterList());
    
    		pkg.setState(new ReceivedState());
    		log.info("state={}", pkg.getState());
    		assertEquals("orderer,manager", pkg.getNotifyGetterList());
    
    		/* //CancelState를 추가하는 경우에도 Package 소스를 수정할 필요가 없다
    		pkg.setState(new CancelState());
    		log.info("state={}", pkg.getState());
    		assertEquals("shipper,manager", pkg.getNotifyGetterList());
    		*/
    	}
    }

참고