📜 제목으로 보기

도메인이 쌓이고 난 후 이름 리팩토링

Game(서비스같은)Test로 Game.메서드들 테스트 -> 구체적으로 클래스명 정하기

  • 현재 Game.start() 이후 응답되는 상태 객체 state.draw()들만 테스트하고 있다.
    • Game에서 카드 2장받아 start를 하면 -> state를 반환한다?
  • Game대신… 상태패턴의 state가 시작되기 전을 의미하는 Ready의 준비를 .start( 카드1,카드2)받는 식은 어떨까?
    • Ready가 start하면 state를 응답해준다?

26) Game.start() -> Ready.start()로 이름 리팩토링

  • 이름 리팩토링은 해당 파일로 가서 class명을 붙잡고 F2 -> RenameTest도 같이 바꾸도록 select All선택 후 Okay해줘야 바뀐다.
    • 클래스명 클릭 -> F2 -> RenameTest 에서 alt +atab 2번 한 뒤 okay enter

image-20220319163331839

중요) 상태변화패턴이 시작되는 [최초 state가 나오기 전 상태]로서 Ready라는 이름으로 시작 -> 이것 역시 State의 카테고리인 상태객체로 바꿔보기

27) Ready도 스태틱 로직메서드를 위한 class가 아니라 State로 만들어보자

상카(추상체) 구현을 시작으로 카테고라이징 된다.
  1. 현 class상태에서 State를 구현하여 카테고라이징 image-20220319164747862

    image-20220319164830707

    • impl해야하는 메소드를 가장 위로 올려, State임을 나타내주자 image-20220319164915891
[잘못설계] Ready 상태는 start(card1, card2)를 활용하여 Hit or Black상태임을 상태값으로 가지고 있다?
  • Hit, bust 등 처럼, 이전 정보cards + 추가정보card정보 업데이트후 -> 업데이트된 정보로 객체 생성을 위해 상태값형태로 cards를 가지고 있었다.
  • Ready의 경우, 재료인 card1, card2가 들어와서처음 초기화되어 시작되며, cards정보를 통한 업데이트된 Ready를 만들 일 없이 한번 만들어지고 끝이다?
    • 다른 State처럼 cards를 받는게 아니다?
    • 그러면 State와 동일한 상태객체일까? 고민이 된다.
  1. Ready는 어떻게 되었든 card1, card2를 재료로 받아 가공되어 내부에 가지는 상태값을 가지게 될 것이다.

    image-20220319171332626

  2. 기존 메서드를 보니 재료를 받았지만, 가공은 State로 될 가능성이 높다.

    • 재료 가공을 State로 해서 상태값으로 가지고 있자 image-20220319171408090

    • 복사해놓고 활용하면 더 쉬울 듯

      • 생성자는 재료를 받아서 this.상태값=을 초기화해줘야하는 의무를 가진다. 그 상태값은 메서드로 치면 State들이다. image-20220319171558671 image-20220319171715171

        image-20220319171852427

        image-20220319172013783

문제점: 아직 state로 가기전의 상태인데, 초기화로 Hit 나 Blackjack상태를 가지게 된다. -> 메소드의 역할만 하고 있고 상태객체가 아니게 된다.

중요) Ready라는 시작 상태는 고유하며, 생성시 아무재료(정보)도 없이 시작되며 -> draw트리거메서드에 의해 바뀔 수 있다.

  • Ready는 생성시 Card1, card1를 받는게 아니라 재료가 없는 빈 생성자로 시작해야한다.
    • 1장 씩 draw하면서 아직 ready인지 물어보고
    • 2장을 draw했을 때, hit or blackjack 상태로 바껴야한다. by draw메서드에 의해

my) 인페를 상카로 뽑아 카테고라이징 했다면, 구현체의 단독 상태, 메서드 개발은 자유롭다 -> 규칙에 맞는 메서드만 오버라이딩해서 개별구현하면 된다.

중요) 시작 State도 마찬가지로 다른 구현체(상태)로 넘어갈지 판단에 필요한 정보 cards를 상태값으로 가진다 -> 재료로 받진 않지만, 추가정보를 받아서 채우면서 판단 -> 상태값인 cards의 갯수에 따라 현재상태인지 -> 다른 상태로 넘어가야할지 판단할 수 있다.

28) 재료없이 생성되는 객체라도 -> 내부에서 빈 재료로 초기화하는 상태값을 가질 수 있다.

참고) 일급을 빈재료로 넣어야할 때 -> this.value = new 일급();으로 일급생성자에 재료를 빼고 넣어주고 -> 일급으로 가서 [재료받는 것을 주생성자this]로 하여 -> 재료없이 내부 빈재료로 초기화하는 부생성자를 추가 생성해줘야한다.
  1. Ready의 재료없이 내부 빈재료로로 상태값을 만드는 생성자를 먼저 선언하고 -> 상태값도 만들어주자. image-20220319172913990

  2. 이 때, 필요한 재료는 안받아오지만, 필요한 내부 빈 재료빈 일급컬렉션이다…

    image-20220319173911108

    image-20220319173925948

  3. 일급컬렉션에 재료받아 생성되는 주생성자를 this로 활용하여 그 위에 재료없는 부생성자를 추가로 만들어주자.

    • 상태값 및 재료가 원래 있던 놈들인데 없는 부생성자 만들시 -> Select None으로 생성하면 된다. image-20220319174338019 image-20220319174348273 image-20220319174410747

    • new ArraysList<>()로 빈 재료를 넣어주면 된다.

        public final class Cards {
              
            private final List<Card> value;
              
            public Cards() {
                this(new ArrayList<>());
            }
      
  4. draw는 아직 처리안되어있으니 return null로 State를 응답해주자. image-20220319174522072

29) Ready도 draw(card)를 한다.

내부 일급상태값.add()는 보이기에만 add시 기존 상태값+업데이트정보로 -> 업데이트 상태값으로 새 일급을 반환해주는 업데이트 일급 응답함수다. 이 업데이트된 일급으로 다음 상태를 판단한다.
  1. 업데이트된 상태값으로 새 일급 반환받기

     @Override
     public State draw(final Card card) {
         //1. 다른 상태에서의 draw처럼 (1) 현재 정보들 + (2) 추가 정보를 가지고 내부업데이트해서 불변 새일급을 반환받는다.
         final Cards currentCards = this.cards.add(card);
        
         return null;
     }
    
  2. 업데이트된 상태값으로 -> 일급이니 메세지를 보내서 다음 객체를 판단한다

30) Ready가 가능한 상태를 생각해본다 -> 1장씩 받으니 Ready 그대로 or 2장 다받으면 Hit or Blackjack -> 업데이트된 상태값으로 isReady()로 물어봐야한다.

image-20220319175235076

image-20220319175256697

image-20220319175308263

  1. 아직 카드갯수가 2보다 작은 1개 상태의 ready상태라면, 업데이트 된 상태값으로 -> new 상태객체(상태값)를 만들어 응답해주자.

    image-20220319175442163 image-20220319175546047

참고) 늦게 생성됬어도 재료받는 생성자가 주생성자라는 개념을 가지고 가자 -> 기존의 재료없는 생성자는 this를 활용해서 부생성자로서 재정의 해줘야한다.
  1. 아직 재료없이 생성되는 Ready 생성자만 정의된 상태이니 생성자도 만들어줘야한다.

    • **재료를 받게 되는 순간부터 늦게 생성됬어도 재료받는 생성자가 주생성자라는 개념을 가지고 가자 **

    image-20220319175756224 image-20220319175838182

참고) 부생성자에서 주생성자 활용하여 정의: 기존 = 우항; 코드들을 복사하고 this()내부로 복붙한 다음, 가공해서 주생성자의 파라미터에 맞춰주면 된다.
  1. 재료받은 주생성자가 생겼지만, 이놈은 외부에서는 호출안될 것 같으니 private으로 처리해주고 부생성자를 this로 다시 정의해주자

    image-20220319175931804 image-20220319175945055

    image-20220319180345232 image-20220319180359571

    image-20220319180429064

image-20220320000818623

리팩토링과 함께

상태로서의 Ready를 Test하기

기존 테스트들이 Ready.start( card1, card2)로 많이 만들어졌기 때문에 일단은 살려둔 상태다.

image-20220319180717651

31) 상태로서의 Ready 테스트하기

  1. 기존에 카드 2장을 받아서, 상태를 만들어주는 정적메서드의 주체자 Ready였다.

     public class ReadyTest {
        
         @Test
         void hit() {
             // given & when : hit
             final State state = Ready.start(Card.of(Suit.SPADES, Denomination.TWO),
                                             Card.of(Suit.SPADES, Denomination.JACK));
        
             // then
             assertThat(state).isInstanceOf(Hit.class);
         }
    
참고) 코드 수정이나 개발은 기존 코드보다 위에, 기존코드를 복붙해놓고 새 기준 코드를 작성한다
  1. 2장 받아 상태반환 메서드 -> 재료없이 최초 상태로서 출발하는 상태객체의 코드로 변경하려면 기존코드를 남겨두고, 위쪽에 복붙해서 수정한다.

    image-20220320001000678

중요) 최초 시작상태 객체는 재료를 안받고 생성되며, 상태 변환 트리거 메서드를 호출하면서 다른 구현체 상태객체가 되는 조건까지 재료없이 내부 빈 재료로 초기화시켜 만든 상태값을 통해 확인하면서 진행하면 된다.
  1. Ready(Class)로 시작하는 .start() 메서드호출 대신 직접 재료 없이 생성하여 최초 상태가 시작된다.

    • 과정

      image-20220320001344766 image-20220320001535624 image-20220320001602351

중요) 상태객체.트리거메서드()경우의 수 구현체를 다 받을 수 있는 추상체를 응답하므로 -> 계속 체이닝으로 해서 호출이 가능하다.
  1. 상태변화 트리거인 draw()를 호출하면서 내부에서 확인하면서 다른 상태로 간다. image-20220320001847469

    • 2번 draw()가 될때까지 내부에서 확인한다
    • 상태변화 트리거 메서드는 추상체로 응답을 하는 덕분에, 체이닝으로 여러번 호출이 가능하다. image-20220320001919701 image-20220320002053061
     @Test
     void readyHit() {
         // given & when : ready -> 2 -> ready -> 10 -> 2장뽑는 순간 Hit상태(or Blackjack)로  최초 Ready객체 탈출된다. 
         final State state = new Ready().draw(Card.of(Suit.SPADES, Denomination.TWO))
             .draw(Card.of(Suit.SPADES, Denomination.JACK));
        
         // then
         assertThat(state).isInstanceOf(Hit.class);
     }
    
중요) 최초Ready상태객체 + 트리거 했는데도 아직 Ready상태인 것도 테스트

image-20220320002513933

@Test
void ready() {
    // given & when : ready -> 2 -> ready
    final State state = new Ready().draw(Card.of(Suit.SPADES, Denomination.TWO));

    // then
    assertThat(state).isInstanceOf(Ready.class);
}

32) 기존 테스트코드를 전체 리팩토링 ( Ready.start -> new Ready.draw().draw()로)

일단 메서드의 통합테스트에서 -> 특정 객체 테스트로 좁혀들어갔으니 -> 개별 상태 객체들도 자기테스트로 테스트를 옮겨가도 된다.

image-20220320003820678 image-20220320003827411

  • 테스트 가져가기 (ReadyTest -> HitTest)

    image-20220320003854330

  1. 다시 Ready.start( 재료1, 재료2) -> new Ready() 재료없는 최초 상태객체로 시작하도록 리팩토링

    • 기존 돌아가는 코드의 위에 복붙 후 수정
     @Test
     void hitHit() {
         // given & when : ready -> draw 2,10 -> hit -> draw 1 -> hit
         State state = new Ready().draw(Card.of(Suit.SPADES, Denomination.TWO))
             .draw(Card.of(Suit.SPADES, Denomination.JACK))
             .draw(Card.of(Suit.SPADES, Denomination.ACE));
         //State state = Ready.start(Card.of(Suit.SPADES, Denomination.TWO), Card.of(Suit.SPADES, Denomination.JACK));
         //state = state.draw(Card.of(Suit.SPADES, Denomination.ACE));
        
         // draw호출이 가능해서
         assertThat(state).isInstanceOf(Hit.class);
     }
    

33) 개별 상태객체부터 시작하는 Test로 고치기(Ready시작 안해도 됨)

중요) 상태값을 가지는 객체라면, 그 상태값을 만드는 재료만 넣어주면, 그 정보를 가공하여 반영된 객체를 생성할 수 있다.
  1. 사실 특정객체Test는 Ready최초시작객체부터 시작할 필요가 없다.

    • **객체 = 최초시작객체가 아니라면, 대부분 재료를 받아 -> 가공 -> 상태값으로 가지는 로직을 가지므로 Hit객체부터 재료를 줘서 만들어서 테스트하면된다. **

    • hit 만드는 테스트부터 만들자.

    image-20220320005716930

    image-20220320005929062

    image-20220320010132915

참고) 객체 생성시, 재료를 넘겨줄 때, 사용성을 고려하여 생성자 추가는 해도 괜찮다. -> new단일객체()도 귀찮은데 2개이상 생성하면서 List.of()로 묶어줘야한다? -> 클라이언트 배려한 가변인자 파라미터 빨간줄 생성
리팩1) 외부에서 인자전달시 List.of()로 묶어서 전달한다? -> 인자는 콤마로 + 파라미터는 가변으로 수정 by Ctrl+F6(change signature)

image-20220320010357887

  1. 인자에 new 생성자(List.of(객체1, 객체2)) 형태라면

    • new 생성자( ` 객체1, 객체2 ) **로 **List.of()의 묶어주는 과정을 제외하여 빨간줄 생성`한 뒤, 생성자를 추가해주자. image-20220320010503702 image-20220320010511932 image-20220320010531246

    • 카드 2개로 인식하지만, 우아한 파라미터 개선의 change signature ctrl+F6으로 가변인자 파라미터로 바꿔주자 image-20220320010653812 image-20220320010850753 image-20220320010900966

      image-20220320010945036 image-20220320011004532

    • 부생성자일 것이니 위치를 위로 옮겨가서 this()를 활용한다.

      image-20220320011110461

    • 배열 -> List로 바꿔줄려고 배열.stream을 썼으나…

      image-20220320011212315

참고) 가변인자는 밖에서 없애줬떤 List.of를 그대로 쓰면 된다. stream대신 -> 내부 List.of()로 처리

image-20220320012126329

public Cards(final Card... cards) {
    this(List.of(cards));
}
@Test
void hit() {
    final State state = new Hit(new Cards(Card.of(Suit.SPADES, Denomination.TWO),
                                          Card.of(Suit.SPADES, Denomination.JACK)));

    assertThat(state).isInstanceOf(Hit.class);
}

34) 카드 2장으로 new Hit() 시작하여 hit -> hit 테스트

image-20220320013041964

  • 2장으로 Hit를 만들고
    • 체이닝으로 트리거메서드를 호출해했지만
      • 가독성이 엉망이다.
리팩3) 가독성을 위한 긴 파라미터부분을 -> 위에 변수로 추출

image-20220320013257384

  • 선택된 부분이 윗줄에 변수로 추출되어야한다.

    • 파라미터 추출은 메서드의 파라미터로 이동된다. 조심

    image-20220320013356374

      @Test
      void hitHit() {
          // 2,10 으로 hit -> draw 1 -> hit
          final Cards cards = new Cards(Card.of(Suit.SPADES, Denomination.TWO),
                                        Card.of(Suit.SPADES, Denomination.JACK));
          State state = new Hit(cards);
        
          //when
          state = state.draw(Card.of(Suit.SPADES, Denomination.ACE));
        
          //then
          assertThat(state).isInstanceOf(Hit.class);
      }
        
    

리팩4 & 중요) 많이 쓰이는 Test속 일급재료인 상수기반 단일객체Fixtures클래스에 모아두고 static import해서 단일객체가 객체지만, 상수처럼 갖다쓰자.

중요) Test에서 자주쓰는 단일객체(일급의 재료)를 Fixures클래스에서 상수로 선언하여 모아두자.-> new 단일() or 단일.of() / 단일.from() 등 인자에서 호출되는 단일객체 생성자(정펙매) 호출부분을 ctrl+alt+C객체를 상수추출한 뒤, F6 -> public(psf)으로 Fixtures 클래스로 옮겨주자.
  • cf) psf = public static final -> 다른데서 갖다쓰는 상수 -> Test Fixture아니면 쓸일 없을 듯 -> 상수라도 각 도메인내부에서 쓰기!
  • cf) pr sf = pr ivate static final -> 일반 클래스내 상수
  1. Fixtures클래스를 만든다.

    • breadcrums(c+a+;)를 이용해서 같은 테스트내에서 편하게 만들 수 있다.

    image-20220320235212699 image-20220320235244656

  2. 상수가 아니라 new 단일() or 단일.of() / 단일.from() 등 인자에서 호출되는 단일객체생성자를 추출해와야한다.

    • 오른쪽에 복제가 아닌 1개 창으로 띄우기 ctrl + \ (복제) -> 왼쪽으로 넘어오기(tabmover의 c+a+s+[) -> 왼쪽 창끄거나 다른창으로넘어가기

    image-20220320235532920

  3. 직접 Fixutre에서 상수로 선언하지말고 상수로 추출 -> F6으로 상수옮겨가기를 해보자.

    image-20220321002801708

    • ctrl+alt+ C의 상수추출

    image-20220321002833164

    • 상수 부분을 클릭F6으로 이동
      • 실수로 Card 등의 Class부분을 잡고 F6을 하면, class이동이 되어버리니 조심

    image-20220321002916980 image-20220321002926777

    image-20220321002945848

    • 갖다 쓸 상수(public sf)로 가는 것이기 때문에 Public도 지정해준다.
      • 안해주면 default생성자가 되어버린다.

    image-20220321003004803 image-20220321003032343

    • Fixtures클래스로 public으로 옮겨갔으며 + 기존 있던 Test에서는 Fixtures.상수로 사용되어진다. image-20220321003150571
참고) 테스트용 갖다쓰는 객체상수 클래스 Fixtures는 Add on-demand static import를 통해 편하게 현재클래스내 상수처럼 쓸 수 있게 한다.
  1. 상수전용 클래스 Fixture를 static import로 생략하고 상수(SPADE_TWO)만 사용할 수도 있다. image-20220321003304399

    image-20220321003313078

    image-20220321003416088

  2. 상수객체가 정상 단일객체럼 행동하는지 해당 객체 테스트에서 객체상수 isSamaAs 생성한 객체 테스트를 해보자

    • 기존 테스트

        @Test
        void of() {
            final Card card = Card.of(Suit.HEARTS, Denomination.ACE);
              
            assertThat(card).isSameAs(Card.of(Suit.HEARTS, Denomination.ACE));
        }
      
    • 사용되는 단일객체를 -> 상수객체로 뽑아서 옮겨놓고 상수로 가져오자.

      image-20220321003729070 image-20220321003745221

      image-20220321003805797

      image-20220321003815981

      image-20220321003822240

      image-20220321003840933

  3. 나머지 사용되는 단일객체들도 다 상수화 -> Fixutre에 public 상수로 옮기기 해주자 image-20220321004835602

     public class Fixtures {
         public static final Card SPADE_TWO = Card.of(Suit.SPADES, Denomination.TWO);
         public static final Card HEART_ACE = Card.of(Suit.HEARTS, Denomination.ACE);
         public static final Card SPADE_JACK = Card.of(Suit.SPADES, Denomination.JACK);
         //...
     }
    
참고) 객체 -> 상수 만들다가 기존 Fixtures에 뽑아 놓은 객체상수 발견시 -> ctrl+H로 일괄변경해주기. -> 의심되는 객체는 ctrl+shit+FFixtures에 만들어놓은게 없나 살펴보기
  1. 상수 만들었다, 이미 만들었떤 것이 나온다? -> 찾아바꾸기로 한번에 다 바꿔주자.

    image-20220321005105517

    image-20220321005204244

    image-20220321005223832

    image-20220321005245707

  2. Fixture 목록에 없는 것들이 나타나면, 또 뽑아서 바꿔준다. image-20220321005520101

  3. 이미 만들어놓은 것 같은데 확인하고 싶다면, -> 블럭 + ctrl+shift+F에서 Fixtures에 걸리는게 있나 훔쳐보기 image-20220321010033926

    • 있다면 windows에서 클릭해서 해당 Fixture상수 복사해와서 -> ctrl+H에 복붙여넣고 찾아바꾸기

      image-20220321010215245 image-20220321010240706