📜 제목으로 보기

상태객체의 재료검증은 -> 시작후 자기내들끼리 만드니 -> 패키지파서 default로 몰아 내부생성만 가능 except 최초시작객체 -> 검증이 필요없게 된다.

재료(카드2장)에 대한 각 상태별 예외처리를 해줘야한다? -> 구현체들의 생성자 접근제한자를 default + 패키지 따로 챙기기로 막아, 접근된 코드에서만 만들어지도록 제한하기 (최초시작상태 객체Ready제외)

my+중요) default 접근제한자는 같은 패키지내에서 만 접근 가능해진다 -> 테스트 외 다른 곳에서는 생성못하게 패키지를 실시간으로 1개파서 생성의 안전성을 보장해준다.

중요) Ready최초시작객체만 public(코드에서 생성가능) 제외 나머지 구현체 상태객체들은 오로지 트리거 메서드 내부 응답시만 생성되도록 패키지에 파서모으고 + default 생성자로 막아 -> 패키지 내부에서만 생성 except Ready최초시작객체만 public 밖에서 생성 -> 외부에서 이상한 재료가지고 생성하는 검증이 필요없이 안전하게 패키지내 서로에 의해서만 생성 + 시작은 Ready객체에서

35) 각 상태객체들을 state패키지를 파서 모으고 -> 외부에서 생성되는 Ready를 제외하고default제한자로 바꿔서 -> 테스트를 제외하곤 외부에서 생성X

  1. 패키지를 만들어 추+구현체들을 모으고, 시작Ready를 제외한 다른 구현체들을 패키지내에서만 생성될 수있게 생성자를 default(접근제한자 삭제)해보자.

    • 몰기 전 image-20220321110721438

    • 패키지 1개에 몰기

      image-20220321110751326 image-20220321110854948

중요) 패키지 1개로 파서 몰 때, Test도 같은 이름으로 파서 몰아줘야, default가 작동된다.

image-20220321111529699 image-20220321111554810

image-20220321111617851

중요) 우리가 코드짜면서 외부에서 잘못된 재료로 객체Hit 등을 생성할일은 없다. 클라이언트가 실수하므로 강제로 패키지내에서만 생성되도록 Ready제외 접근제어자를 삭제해서 default로 만들어 외부생성X내부에서만 돌면서 생성되도록 바꿔주자
my) 특정 객체에서부터만 시작하는 구현체들?? -> 패키지 파서 모으고 1개만 public으로 열어두고 나머지는 default
  1. 시작Ready만 public으로 그대로 둔다. image-20220321111919002

  2. 나머지 상태객체들은, state패키지내부에서만 생성가능하게 접근제한자를 삭제해준다.

    image-20220321112022349

    image-20220321112029789

     public final class Hit implements State {
        
         private final Cards cards;
        
         Hit(final Cards cards) {
             this.cards = cards;
         }
        
    
참고) 생성자 정의 안해주면,아직 (재료를 가지고) 생성 한번도 안해봤었다? 상태이며 , 기본이 public 생성자 상태이다. 유틸클래스는 이것을 막아주기 위해 private 빈 상태로 재정의를 해줬던 것이다.
  1. **만약, 생성자 없다? -> 확인해보니, public으로 열려있다. -> 나중에 Bust정의할 때,default로 닫아줘야한다. ** image-20220321112318647

    image-20220321112747910

  2. 이제부터는 new Hit( 재료카드2장)으로 생성할 땐, state패키지내 메서드에 의해 생성되어, 외부 잘못된 카드로 인한 생성은 검증이 필요없이 안전한 상태가 된다. image-20220321113347144

  3. Ready만 열어줬다고 해서, Ready시작만 되는 것은 아닌 상태임. image-20220321113519794

Hit(카드받기 가능)에서 -> Stay안받아의 상태 추가 개발

가능한 상태 다시 생각

  • 첨 카드 2장만 받고 최대(11+10)으로 bust는 불가능하고 -> blackjack이 최대 높은 사태다
    • ready +2장
      • blackjack
      • hit +1장
        • bust
        • blackjack : 블랙잭은 2장일때만 가능이다.
        • hit
      • hit +stay(): hit상태에서 +1장이 아니라 테스트에선 생략하는 view로 물어보고 -> 추가 메서드를 통해 stay상태로 갈 수 있다.
        • stay

35) 기존 트리거 메서드외에 직접 추가한 메서드로 특정상태로 갈 수 있다. (hit -> draw()말고 stay()) -> view에서 물어보고 갈 case를 -> 바로 메서드 호출()하여 case로서 가버리자

참고) Test에서는 inputView의 물어보는 과정을 아예 생략하고 그 이후 case로 진행해 나간다. ex> stay할거냐? 안물어보고 바로 stay()로 가는 메서드를 호출 -> 테스트에서 빨간줄로 호출하며 메서드 + 상태객체 다 만들자.
참고) public외부에서 Ready시작객체로 시작해야하지만, Test에서는 패키지명 동일 -> default 생성자 사용가능 -> 바로 특정 구현체 객체 생성 가능~!
  1. hitTest에서 -> 빨간줄 Stay상태객체로 가는 빨간줄 메서드를 만들자.

    image-20220321120622644

     @Test
     void hitStay() {
         // given: hit(2,10)
         State state = new Hit(new Cards(SPADE_TWO, SPADE_JACK));
        
         //when: draw(X) -> stay() -> Stay
         //state = state.draw(SPACE_TEN);
         state = state.stay();
        
         //Stay객체가 나와야함.
         assertThat(state).isInstanceOf(Stay.class);
     }
    
참고) Test에서 클래스 생성시 -> 빨간줄 -> Create Class -> 패키지명 확인후 tab + ↓ + ↑프로덕션 명시해주느 단축키 외우기
  1. 구현체에서 상태변화 메서드 stay()이전에 Stay클래스 부터 만들어준다.

    image-20220321121039058

    • 단축키 tab + ↓ + ↑ 를 활용해서 프로덕션으로 빠르게 경로지정해주자.

image-20220321120755223

  1. 상태객체의 class를 생성했따면 -> impl State부터 image-20220321121137273

    • 추상체 구현후 generate -> im을 검색해서 추상체 구현메서드를 오버라이딩이랑 구분해서 빠르게 정의해주는 버릇들이자.

      image-20220321121300969 image-20220321121315812

      image-20220321121333609

      image-20220321121144978

36) Stay클래스 State구현 후 공통 구현메서드는 일단 나중에! Test에서 하던 .stay()부터 개발

  1. 일단 구현해야하는 메서드는 두자. image-20220321121441337

대박) 구현체 메서드 개발시 -> 기본 공통 추메/전메로 올리고 -> 나머지들도 구현해주는데 호출불가시 Thr illegalState -> 호출불가 너무많다? -> 구현체들 impl시 중복되는 코드들을 <부모로써 먹어 자동구현 해주는> 중간 카테고리를 추클(추메/전메를 다 필수구현안해해도 되는 성질)로 추가한 뒤, [추상체-impl->중간 추클] + 그 아래 구현체들을 [추클-extends->구현체]로 연결을 바꾼다.

중요) 추상체변수로 받은 상태에서의 특정 구현체(Hit)에서 불러야하는 메서드(.stay()) 개발은?? 그냥 생성하면 추상체의 추메/전메로 생성되어 -> 나머지 구현체들도 다 구현해야하는… 강제성을 가지게 된다. -> 일단 원하던 특정 구현체에서만 추메/전메를 impl하여 구현 -> 이후 다 구현해줘야하는데, 호출불가 상태에서는 thr예외처리로 해결

  1. hit상태에서 빨간줄 .stay();를 호출하면 Stay를 생성해줘야한다 image-20220321121505018

  2. 그냥 생성해주면.. 추상체의 추메/전메로 정의되어 -> 나머지 구현체들이 구현해야한다

    • 현재는 Hit에서 .stay()만 필요한 상태인데???
      • 일단은 추메/전메로 올려놓고 -> 다시 Hit로 가서 구현해준다.

    image-20220321121711266

중요) 특정구현체에만 필요한 메서드 (hit에서 .stay())라도, 추상체 변수상태의 추메/전메로 생성된다 -> (여러구현체 다 빨간색 impl요구 된 상태지만) 특정구현체로 먼저 가서 추메/전메를 구현해준다. -> 이후 호출할 수 없는 구현체는 thr illegalState 예외처리로 해결한다
  1. Hit용 메서드가 -> 추상체State의 추메/전메로 올라와 버리는 바람에 구현체 다 빨간줄상태지만, Hit부터 가서 추메/전메를 구현해서 정의해준다. image-20220321121943577

     public interface State {
         State draw(final Card card);
        
         State stay();
     }
    
  2. 원래 해당 메서드가 필요했던 특정구현체Hit로 먼저 가서 구현까지 해준다. image-20220321122032423 image-20220321122205137 image-20220321122217098

    • 호출시 바로 Stay상태가 되어야하므로 객체 생성해서 건네준다.

    image-20220321122317770

     @Override
     public State stay() {
         return new Stay();
     }
    

정리용)

37) 나머지 구현체들도 강제로 구현해야한다. 호출불가 상태객체에서는 thr illegalState 처리

  • hit를 제외하고

      @Override
      public State stay() {
          return new Stay();
      }
    
    • Blackjack이 stay가능?(X)

    • Bust이 stay가능?(X)

    • Ready이 stay가능?(X)

      • Stay이 stay가능?(X)
      • 다 구현후 thr illegalState 처리

      • alt+insert -> impl검색 -> stay()구현 -> thr 처리
        @Override
        public State stay() {
            throw new IllegalStateException();
        }
      
  • 이제 hitStay에서 .stay()를 테스트할 수 있다.

      @Test
      void hitStay() {
          // given: hit(2,10)
          State state = new Hit(new Cards(SPADE_TWO, SPADE_JACK));
        
          //when: draw(X) -> stay() -> Stay
          //state = state.draw(SPACE_TEN);
          state = state.stay();
        
          //Stay객체가 나와야함.
          assertThat(state).isInstanceOf(Stay.class);
      }
    

###

중요) 특정구현체용 메서드를 추상체에 올린후->구현->나머지 처리했더니 대부분이 사용안하고 예외처리다? -> [중복코드를 부모로써 먹어주는 중간 카테고리 추클]을 [추상체-impl->추클]로 추가한 추상화를 하고 그 아래 구현체들을 [추클-extends->구현체]로 연결한다.

  • 기존 상태

    • State(I, 첫 상카, 추상체) - Ready포함 여러 구현체들(final Class)

    image-20220321123702029

38) 추메/전략을 구현하는 구현체or구현체들을 제외하고 나머지 대부분의 구현체들이 -> 구현하면안되서 illegalState처리하는 그룹이 많아지면, 중복코드를 부모로써 먹어주는 추클로 중간 카테고리를 끼워넣되, 구현체들의 구조 상카impl-> 중카extends -> 구현체로 바뀐다.

중요) 구현체용 단독메서드를 위에서 올려서 구현하다보니, 일부 구현체들은 안쓰는 코드로서 코드중복 -> 특정 구현체들 묶음으로 묶어 코드중복을 없애줄 중간카테고리로 추상화하는 생각 + 중카는 추클로 추가한다. -> 이름을... 구현체들 왜?? 묶는지로 생각해서 작명해주자.

중요) 상카-추상체에서 타고 내려오는 메서드들의 코드중복만 추클이 먹어줄 수 있다. -> 구현체들이 interface 공통인 추메/전메 이외에 다른 것을 구현했어도, 공통인 추메/전메에서 중복되는지만 확인해서 중간 카테고라이징해준다.

중요) 특정 메서드의 중복발생이지만 -> 중간 추클로 코드중복 제거시 -> 상카의 전메/추메의 목록을 다 보면서 중간 카테고라이징 한다
public interface State {
    State draw(final Card card);

    State stay();
}
  • stay()에 의해 코드 중복이 발생했지만, draw()도 같이 중복이 일어나는지 살펴서 중간 카테고라이징 한다.

  • Hit만 stay된다. -> 나머지 구현체들은 다 예외처리로 구현되어 중복이 발생했다.

    • Ready는 stay예외처리는 똑같지만, draw에서 코드 중복이 안일어난다.

    • Blackjac, Bust, Stay는 이미 다 끝난 최종 상태객체로서 더이상 트리거 메서드 모두(draw, stay) 다 호출 불가 -> 모든 추메/전메의 구현이 중복된다.

        @Override
        public State draw(final Card card) {
            throw new IllegalStateException();
        }
              
        @Override
        public State stay() {
            throw new IllegalStateException();
        }
        }
      
  1. 중간에 중간카테고리추상클래스로 추가하고, 상위카테고리 State를 imple해줘야한다.

    • 중간 카테고리 이름은? .stay() 못해서 thr처리 된 이유?를 생각해보면 Finished로 이미 끝났기 때문이다.
      • 사실 Stay는 이미 stay를 호출해서 끝난 것
    • State(최초상카, I) <—> 구현체들(final Class) 사이에 들어가 특정 구현체들 묶음으로 묶어 코드중복을 없애줄 중카 Finished를 끼워놓자 image-20220321134435979
    • 네오는 인터페이스에 정의한 필수 구현 추메/전메를 default메서드라고 불렀다.
중요-용어) 추메/전메로 타고 개발한 구현체용 메서드가 추메/전메로 정의된 default메서드로서 구현체들에게 중복코드를 계속 imple시킨다면, 상카 <-> 구현체들사이 [중복코드를 부모로써 먹어주는 중간 카테고리] 추클로 추상화를 고려한다.
  1. 구현체중복된 예외처리 코드부모로써 먹어서 자동구현해주는 추상클래스를 중간카테고리로 추가한다.

    • 이미 stay로 가기엔 hit가 아닌 끝난 상태라서 .stay()를 호출못해서 예외처리코드로 코드 중복이 되는 상태이므로 중간 카테고리 추상클래스이름을 Finished로 끝난 상태라 stay못함.으로 지어준다.
      • 여기에 Stay도 포함되어있다.

    image-20220321135729711 image-20220321135744783

중요) 상카(interface)가 존재한 상태에서 [코드중복 먹어 자동구현시킬려고 끼운 중간카테고리 추상클래스]는 상카인 interface를 impl시켜야한다
  • 중간 카테고리로서 끼어들어간 추클은 상카 State(I)를 impl해야한다. image-20220321140026403
    • 클래스와 달리, 추상클래스는 추메/전메impl 필수구현이 아니다.
    • 그래도 추메/전메를 모두 구현해놔야 기존 상카 구현체들로서 모든 추메/전메를 받아먹게 된다

중요) 상카(I)를 impl한 클래스와 달리, 상카를 impl한 추상클래스는 상카의 추메/전메가 필수 구현이 아니다. (cf.abstract를 지우면 필수구현하라고 뜸) -> 추상체 중 원하는 메서드만 구현할 수 있기 때문에, 구현체들에서 발생하는 코드 중복의 [특정 메서드]만 대신 구현이 가능해진다. 하지만, 일단 모든 추메/전메를 impl구현해야 -> 하카 속 자식들이 [원래 목적인 상카Interface의 구현체=상태객체]로서 성질이 유지될 것이다. + 중카 추클을 안끼워놓은 상카 구현체(상태객체들)이랑도 상호 호환되어야하는데, 중카 추클 있는데, 상카의 추메/전메가 없는 구현체(상태객체)가 되면 의미가 없다 -> 중복제거 특정메서드 + 모든 상위 카테고리 추상체의 추메/전메 모두 받아먹을 수 있도록 -> 중카 추클은 상카 인터페이스 impl시 모든 추메/전메를 impl구현해야만 한다.

  • 추메/전메를 가진 상카 인터페이스를 impl했지만, 전메/추메/default메서드 구현하라고 빨간줄이 안뜬다
    • 중간 추클은 구현체가 아니다. 필수 구현 안해도 된다. -> 그러나 중카 추클 추가 전에 상태 Interface의 구현체역할이 더 중요하므로 추메/전메도 같이 받아먹어야한다. image-20220321140255553
  • abstract를 지우면, 인터페이스의 구현체로서 필수 구현해야한다. image-20220321140319405

  • 추클은 구현안해도 되지만, 추클 중간에 끼워넣기 이전에 interface의 구현체 성질(상태객체)가 먼저다!
중요) 잔소리말고, 코드 중복 제거를 위한 중간카테고리=추클은 -> 기존 구현체 성질 유지를 위해서, 무조건 [필수는 아니지만 모든 추메/전메 impl구현] 하자!

39) 중간 추클은 구현체가 아니라서 필수구현 안해도 된다. -> 이 성질을 이용해서 특정메서드만 구현가능하지만, 일부 구현체그룹 중복발생 메서드stay()이외에 공통 추메/전메인 draw()도 impl해서 같이 중복처리를 해주는 버릇을 들이자.

참고) 구현체들의 중복코드를 추클로 올릴 땐, impl로 껍데기는 구현 -> 내부 코드는 중복되는 코드를 가진 구현체에서 직접 복사 붙혀넣기해서 대신 구현해주는게 젤 좋다.(100%중복되니까)
  1. 현재 hit를 제외한 구현체들에서 stay() { 예외처리 }부분만 코드 중복이었지만, 상카의 구현체로서의 역할유지를 위해 모든 추메/전메를 impl해서 카테고라이장 하려고 해보자.

    image-20220321144834958

    image-20220321144845341

    • 일부구현체 코드 중복은 stay()에서만 발생했지만, 모두 impl해서 살펴보자

      image-20220321144856454

중요) 특정 메서드의 코드중복 -> 다른 공통메서드들도 같이 살펴 볼 것!!
  1. 살펴보니, stay뿐만 아니라 draw에서도 공통적으로 코드 중복이 일어나는 클래스들이 발견되었다. image-20220321145043551

  2. 중복코드는 직접 impl구현하지말고, 구현체 1개에서 다 똑같은 중복코드를 복붙해오자 image-20220321145147557 image-20220321145319809

    image-20220321145340379

     public abstract class Finished implements State {
         @Override
         public State draw(final Card card) {
             throw new IllegalStateException();
         }
        
         @Override
         public State stay() {
             throw new IllegalStateException();
         }
        
         //blackjack, bust, stay 중복 -> 중간 추클을 extends하도록 변경해야함.
     }
    
my) 뜬금포: 중간 카테고리는 특정 상태가 많을 것이다. 상카는 전체 대명사에서 시작 -> 중카는 대명사에서 이어지는 상태 형용사?! ( State -> Finished(state)의 형용사)
  1. 이제 중복코드를 가진 중간 카테고리 아래 구현체들이 코드 중복을 대신 구현해주는 부모로서 중간 추클(Finished)을 extends하도록 변경해준다.

    • 코드 중복 하위 카테고리 -> blackjack, bust, stay

    • 가장 상위 카테고리 상위카테고리 추상체 State를 impl -> 코드중복 먹어주는 중간 추클 Finished를 부모로서 extends로 변경

    image-20220321145719168-

    image-20220321150635378

중요) extends가 보인다면, extends로 부모(추클)의 중복코드 받아먹는 중 -> 내부 코드는 제거된 상태로 먼저 생각하자!!

중요) impl한 구상체에 @overriding필수구현 추메/전메 구현중이라고 생각 + cf) impl한 추클이라도 필수가 아닌 것 같지만, 모든 추메/전메를 구현해야 의미가 있다

중요) extends한 자식내부에서 @overriding이 있다면, 부모가 중복코드 먹어 구현해주는 데도 @또 고쳐쓴다로 해석하자. + 원래는 딴짓 못하게 부모인 추클 메서드 [모두impl]하여 정의시 final로 막아야한다

  • **extends가 보이면, 부모가 중복코드를 먹어 대신 구현해 내려주는 중이라고 생각하고 **

    • 추상체 인페의 impl하는 구상체에서의 @overrding == 필수 추메/전메 구현중은 지우고, extends중복먹어 내려주는 것을 받아먹자

    image-20220321151232007

    image-20220321151239026

      public final class Blackjack extends Finished {
        
          private final Cards cards;
        
          Blackjack(final Cards cards) {
              this.cards = cards;
          }
      }
    
중요) extends를 치는 순간부터, 부모가 중복코드 내려주는 중이니, 생략된체 받아먹자. 생각 -> 만약, 자식으로 받아먹기 이전에, 구현체로서 impl + @Overriding의 추메/전메 필수 구현이 있었다면 -> 삭제하여 자식으로서 받아먹고, 구현체로서 받아오는 역할은 추클(중카)에게 맡기자

image-20220321151525524

image-20220321151536080

image-20220321151550350

  • 나머지 2개 중간카테고리 아래자식들도 다 extends로 중복코드 받아먹고 코드는 제거하자 image-20220321151439749


image-20220321151633317

public final class Bust extends Finished {
}

  • Stay도 이미 Finished된 상태로 draw/stay예외처리하는 코드 중복이 발생하니

    • extends 중간추클(부모) -> 중복코드 받아먹기 -> impl + @overridng 추메/전메 필수구현은 삭제

    image-20220321151755767

    image-20220321151803775

    image-20220321151809324 image-20220321151817312

      public class Stay extends Finished {
      }
    

extends로 중카=추클=부모가 중복코드 구현해주고 받아먹는 중인데, 자식들이 중복코드 받아먹는 와중에 extends + @또 고쳐쓸 수 있다면? 불안한다 -> 추클이 중복 코드 impl구현 부모 역할시모든 상카 추메/전메 impl구현할 때 -> final or abstract를 달아줘서 중복코드 받아먹는 자식들이 딴짓 못하게한다.

40) 중복코드 구현 추클 부모(중간 카테고리)는 내려줄 때, 자식이 extends후 받아먹기만 하고, @고쳐쓰는 것은 못하도록 -> 추클 메서드에는 final (or abstract)를 미리 달아서 정의해놓는다.

중요) 특정구현체 메서드 개발에서 비롯된, 코드중복 제거용 추클(중간 카테고리)이 예의상 추메/전메를 모두 구현해서 코드중복을 해결해줬는데, 추클이 중복제거할라고 impl 구현한 추메/전메에는 final (or abstact)을 달아서, 받아먹는 자식들이 딴짓(extends받아먹은 후 @또 고쳐쓰기)못하게 막자
  1. 상카 구현체들 중 특정 카테고리의 추메/전메의 중복코드를 구현해주는 추상클래스에서는 자식들이 extends로 받아먹게만 하고, extends후 @또 고쳐쓰기 못하게 final or abstract를 달아주자.

    • my) 추메가 중간에서 중카로서 중복제거 역할로서 끼워진다면

      • impl 상카(추상체,interface)할 때부터 모든 추메/전메 impl구현

        image-20220321211947565

        image-20220321211957532

        image-20220321212004729

      • 추클은 부모로서 자식들이 중복코드 받아간다 -> 추메/전메impl 직후 ->자식을 가진 부모라면 내부 final or abstract부터 정해주기

        image-20220321212127531

      • 추클의 2가지 작업(모든impl 구현 + final or abstract 달아주기)가 구현체(자식될 놈)들 중 1개에서 중복코드를 복붙해와서 메소드 구현해준다.

        image-20220321212255907

        public abstract class Finished implements State {
            @Override
            public final State draw(final Card card) {
                throw new IllegalStateException(); // 구현체 블랙잭한테 복사해온 중복코드
            }
              
            @Override
            public final State stay() {
                throw new IllegalStateException(); // 구현체 블랙잭한테 복사해온 중복코드
            }
        }
      

image-20220321212908113

중요+정리) 코드 중복을 먹어서 자식에게 제공해주는 추클 끼워넣을 시, 해줘야하는 2가지 작업
  1. 기존 상위카테고리로서 interface의 모든 추메/전메를 구현한다
    • 그래야 기존 상카(interface) 추메/전메<—> 구현체들(Class) 추메/전메 필수 구현의 관계가 유지된다.
    • 추상클래스는 상카(interface)를 impl해도 추메/전메 필수구현이 아닌 상태다.
      • 그래도 생각말고, 모두 impl해주자
  2. impl 추메/전메의 모든 구현 직후 추클은 중복먹어 내려보내주는 부모인데 자식들이 가져가서 고쳐쓰거나 딴짓 못하게 final or abstract으로 메서드들 막아주기

41) 중카 추클 끼워넣기로 중복제거 완료후 -> stay로 다시 돌아와서 -> 도메인 테스트를 해준다. 개발기능: Stay상태객체 개발 및 hit이외에 나머지는 .stay() 호출안되는 예외처리

  1. hit-> Stay를 테스트하는 와중이었다 image-20220321153755903

  2. Stay 도메인이 개발되었고, .stay()호출 기능도 개발되었다.

    1. hit Stay는 통과했다.
    2. 나머지 state(blackjack, Bust, Stay) -> .stay() -> 예외발생 테스트
      1. 굳이 Stay
중요) new 구현체 () 생성시에는 항상 변수를 추상체로 바꿔서 받아주자!
  1. new 구현체 생성시, 변수추출하는데, 변수형은 추상체로 바꿔받자 image-20220321154131678

    image-20220321154146688

     @Test
     void stay() {
         final State stay = new Stay();
     }
    
참고) 호출시 예외발생하여 종료 테스트 -> assert문 내부에서 호출하기~
  1. 이미 끝난 상태로서 Stay상태에서느 .stay() 호출시 예외발생하고 종료되어야할 때

     @Test
     void stayStay() {
     	// stay -> .stay() 호출 불가
         final State state = new Stay();
        
         // stay에서 stay()하면 안됨 -> 예외발생되서 종료되야 정상 -> assert문에서 호출
         assertThrows(IllegalStateException.class, () -> state.stay());
     }
    
  2. 추가로 Stay상태에서 .draw()호출해도, 이미 끝난 상태로서 예외발생하고 종료해야한다.

     @Test
         void stayDraw() {
             // stay -> .draw(카드1장) 호출 불가
             final State state = new Stay();
        
             // stay에서 stay()하면 안됨 -> 예외발생되서 종료되야 정상 -> assert문에서 호출
             assertThrows(IllegalStateException.class, () -> state.draw(SPADE_ACE));
         }
    

중요) 중복코드 구현체들을 위해 중카(추클)을 하나만 끼워넣으면, Layer가 안맞다 -> 중복없는 구현체들도 Layer용 중카 추클을 끼워넣어준다

image-20220321153217131

  • 현재 Finished만 중간 카테고리 (추상클래스 Layer)를 가져서 Layer구조가 안맞다.
참고) Stay class에 final을 안붙혀줬더니 다이어그램상 *모양이 없다

image-20220321153532050

image-20220321153538417 image-20220321153549762

image-20220321153614436

42) Ready.start() 부분을 모두 리팩토링 -> 돌아가서 회색메서드가 되면 안전삭제

중요) 메서드 테스트, 통합테스트는 개별적인 도메인 테스트와 달리, 객체들이 합쳐졌을 때 잘 작동하는지 확인할 수 있다. -> 여러 객체들을 복합적으로 테스트 but 자기맘

State객체의 모든 상태객체들은 결국 판단 및 새 객체응답판단시 뿐만 아니라 -> 결과 보여주기 or View에 넘겨주기 위해 정보(상태값) 뿐만 아니라 getter조회메서드까지 무조건 가지고 있어야한다. -> 전체 공통 메서드이므로 추메/전메로서 interface에 올려서 개발시작하며 중간카테고리가 존재한다면, 속하는 구현체(자식들)이 코드중복인지 아닌지 한번더 살핀다

43) 모든 상태객체는 결국엔 정보값에 + [getter메서드]를 가져야한다. 메서드이므로 interface에 올려서 개발시작하자 -> getter는 getXXXX보다는 필드명 or 일급명을 소문자로 네이밍하자

참고) .getValue() 대신 상태응답값(returnType)과 함께 .인스턴스변수(상태값)명() or .일급상태값()으로 getter메서드를 네이밍 해보자.
  1. 모든 상태객체마다 상태값(Cards)를 조회하는 메서드를 정의하기 위해 상카 추상체 State에 추메/전메로 올려서 개발시작한다 image-20220321221216272

     public interface State {
         State draw(final Card card);
        
         State stay();
        
         Cards cards();
     }
    
    • Cards는 상태값 일급컬렉션이다.

중요) 구현체들의 공통메서드를 추가할 땐, 최상위 상카 인터페이스에 추메/전메로 올린 뒤 -> 구현체 개별구현인데, 인페---추클---구현체들로서 중복코드제거용 중카가 껴있다면? 해당하는 자식(구현체)들이 코드 중복인지 아닌지 먼저 살핀다

image-20220321222659023

중요) 중간에 추클로 코드중복제거해서 묶인 구현체들(자식들)은 이제 무조건 통해서 오는 추클의 자식들이 됬으므로, 항상 개별 구현 전에, 묶어서 부모에서 중복 먹일 수 있나 없나부터 먼저 생각해야한다.
중요) 중카, 추클은 필수구현이 아니지만, 중간자식들의 구현체 성질 유지를 위해 모든 추메/전메impl 해야만 했다. 추메/전메에 새 메서드가 올라갔어도 [중카, 추클은 필수impl아니라서 놔둬도 빨간줄 안날줄 알았는데...] -> 자식들이 빨간줄로 화나있다. -> 살펴보니, 중카-추클이 impl안한, 상카-추메/전메에 대해서, 모두 구현안해주면, 자식들이 화를 난다.

image-20220321223640338

image-20220321223712312 image-20220321223720935

  • 하위 자식들(구현체) 3개를 임시로 삭제하면, 추클에서는 에러 안난다. image-20220321223907993

  • 이번엔, 추클이 impl구현한 메서드2개 중 1개를 삭제했더니 -> 자식들이 화를 낸다.

    image-20220321224027419 image-20220321224047557

중요) 결론적으로, 중카 추클은 추메/전메의 모든 impl구현이 필수가 아닌 것 같지만, 모두 impl안해주면, 자식들이 에러가 난다.
  • 추클 자체에러는 없지만, 자식들이 위에서 화를 내고 있다. -> 추가되는 전메/추메는 모두 구현해줘야한다. image-20220321224319871

    image-20220321224304975 image-20220321224336153

중요) 뭐야.. 중카 추클이 모두impl안해도 화내던 자식들에서 개별 impl구현해도 된다?!!

  • 중카 추클 Finished에서 .cards() 구현안해서 자식들이 화난 상황 image-20220321224607724

  • 화난 개별 자식들에서 빨간줄 제거를 위해 impl해보았더니, 에러가 사라진다. image-20220321224634041 image-20220321224644878

  • 인터페이스가 상위 인터페이스 - 구현 추상클래스 -의 자식들 중 하나인 blackjack을 추클 구현impl이 없다면, 직접적인 구현체들처럼 실제 구현으로 받아들인다.

    image-20220321225512346 image-20220321225528421

정리 그림

image-20220321225348112

45) 새롭게 상카 추메/전메에 올린 getter(.cards())에 대해, 구현체들 개별 필수구현전에, 중간카테고리 추클하위 자식(구현체)들 Blackjack, Bust, Stay에 중복이라면, 추클에서 정의해줘서 받아먹게 해준다. -> `중카 추클은 코드중복을 위해 묶여있기 때문에…

중요) 새로운 메서드가 상카-인터페이스부터 타고 추가될건데, 중카-추클에 걸린다면, 추클의 목적상 하위자식들의 코드중복이므로, 중복되는 상황인지 보고 중복되면 중카에 vs 중카-추클에 구현안하고 개별 자식들에게 책임전과하여 개별구현도 가능하니 중복 안되면 중카-추클에서 정의하지말고 내려오는 추메/전메에 대해 중카-추클 무시 맨 끝 자식들에서 개별구현으로 구현책임 전과해도 된다. 반대로 여러 구현체에서 전체 중복이면 -> 상카 인터페이스 직전에 중카-추클을 끼워넣어 전체 중복코드만 먹일 수 있다. ex> 이후 Started가 그 예시
  • getter인 cards()메서드는 3개 자식들에 대해 중복적으로 필요하다.

    • 그렇다면, 중카 추클에서 중복코드로서 대신 구현해서 -> 자식들이 개별구현안해도 받아먹을 수 있게 해줘야한다
      • 추클에서 필수아니라고 새로생긴 추메/전메를 impl구현 안해놓으면, 자식들에게 필수 구현으로서 전과됨을 참고하자

    image-20220321230027733 image-20220321230046417

중요) getter를 구현할 때 필요한 상태값(인스턴스변수)도 있어야한다. getter는 중복이라서 추클에 대신구현으로 정의했는데, 상태값 -> 자연스럽게 따라오는 [(최초시작 아니라면 -> 여기선 끝난 상태객체 3개) 외부에서 재료를 받아 최초 상태값을 초기화해줄 재료받는 생성자] 정의도 중복이다! -> 추클에선 자식들(구현체) 생성자 중복을 protected로 정의해줘야, 구현체(자식들)이 자기들 public생성자에서 <주생성자 대신 부모것을 주생이라 여이고> super() == 중복된 코드를 갖다써서 외부에서 재료를 받아 가공할 수 있게 된다.

46) getter메서드가 끝난상태객체에서도 공통필수메서드면 -> 상태값도 모든 구현체들에게 필수이다. -> getter에 대한 상태값들도 중복코드로서 추클에서 정의해주자.

중요) 상태값(인스턴스변수) = 필드를 생성할 땐, 가공전 raw상태값을 재료로 받는 or 최초시작이면 재료없이 내부 빈 상태값으로 초기화해주는 생성자 2개를 먼저 생각한다.

  1. 현재 getter인 cards()에 쓸 상태값을 가져야하는 상황이 왔다면, -> 가공전raw상태값 재료 생성자 or 내부 빈재료 생성자를 먼저 선언해주는 버릇을 가지자.

    image-20220321231836431

    • 생성자 선언전에 가공전raw상태값을 재료로? 재료없이 내부에서 빈 상태값으로? 먼저 선택한다.
      • 끝난 최종상태객체이므로 무조건 직전까지 업데이트된 상태값을 재료로 받아 가공하여 생성되는 객체다
중요) 추클속 코드중복 제거용 생성자<외부에서 쓰는 구현체.메서드()와 달리 [자식 내부에서 주생this()쓰듯이 부모super()를 갖다쓰도록 하기 위해 -> protected로 선언한다
my) 추클, 부모에서 생성자 중복코드는 protected로 선언해줘야, 자식들이 주생성자를 this()로 갖다쓰듯이, 부모생성자를 super()로 갖다써서 자기들 생성자를 정의한다.

image-20220321232247541 image-20220321232502720

image-20220321232610077

public abstract class Finished implements State {

    protected Finished() {
    }
  • 참고로 파라미터도 바꿀꺼면, 접근제한자도 같이 ctrl+F6의 change signature로 바꿀 수 있다.

  • 추클의 생성자를 change signaturepublic or proteced상태로 생성자 리팩토링 한다면 -> extends 자식들에게 자동으로 직접구현까지 해준다

    • public으로 추상클래스(부모)에서 생성자 리팩토링 예시

      image-20220321233719799 image-20220321233727763

중요) 생성자를 정의할 땐, 미리 생각해둔 초기화할 상태값 생각후 -> 가공전 raw상태의 외부 재료를 생각해서 기입한다
  • 상태값은 일급인 Cards를 가질 예정이다 -> raw상태의 일급Cards가 들어올만한게… 그냥 직전 상태객체의 Cards가 들어와 만들 것임.

    • 생성자의 파라미터 변경은 ctrl+F6의 change signature를 계속 연습하자. image-20220321233258246

      image-20220321233310402 image-20220321233920049

        public abstract class Finished implements State {
              
            protected Finished(final Cards cards) {
            }
      
    • change signature로 생성자를 수정하면, 추클 -> 자식들에 super()로 내부에서 직접 갖다쓰도록 코드까지 짜준다.

      image-20220321234003969

      image-20220321234012589

      image-20220321234026863

    • 근데… Cards cards의 재료를 받는 생성자를 protected로 리팩토링 했는데 -> 재료안받는 생성자로 정의해줬다 -> 삭제해도 될듯..

      • 삭제하고 -> 직접 생성자 다시 생성해줌

      image-20220322014957302 image-20220322015005155

      image-20220322015020183

중요) 상태값을 정의해야한다면, 무조건 생성자부터 -> (재료로 가공후 ) 생성자 내부에서 this.예비상태값 = 에 초기화 하면서 -> 빨간줄로 예비상태값 자동 생성/선언되도록 해주자.

  1. 생성자가 완료되었으면, 생성자 내부에서 상태값을 초기화화면서 상태값을 선언해준다 image-20220321234435858 image-20220321234444845

    image-20220321234523067

    image-20220321234528377

     public abstract class Finished implements State {
        
         private final Cards cards;
        
         protected Finished(final Cards cards) {
             this.cards = cards;
         }
    

47) 재료받는 생성자 -> 상태값 초기화 -> 상태값 선언이후, getter메서드를 완성해준다.

image-20220321234619687 image-20220321234630778

@Override
public Cards cards() {
    return cards;
}

image-20220322011841181

getter메서드 완성시 (1) 나가는 값이 불변객체하냐 안하냐에 따라 Dto없이 + public으로 열어두기 가능해지며 + (2) 추클내 메서드라면 자식딴짓 방지용 final/abstract를 정해주자

48) getter로 응답되는 값이 불변객체(현재 불변 일급컬렉션)이라면 public으로 열어두기가 가능하며 -> dto도 없어도 된다

참고) 암기 : getter정의시 return 불변객체라면, public열어두기 가능 + dto없어도 됨

image-20220322011947642

추클내 메서드라면 final or abstract
  1. 추클의 메서드가 getter가 아니더라도 중복코드제거용으로 추가된다면

    • final/abstract를 결정해주자

    image-20220322011850896 image-20220322011859784

     @Override
     public final Cards cards() {
         return cards;
     }
    

49) 추클의 중복제거 중간카테고리에 속하지 않은 개별 구현체(Hit, Stay)들에도 추메/전메(getter) 직접 구현해주기 + if 상태값도 없으면 return 상태값 -> 생성자부터

image-20220322012406397

Ready(최초상태객체)도 .cards()의 getter로 상태값을 보여줘야할까? -> 필요없으면 thr처리를 하면되므로 구현체라면, 일단은 필수로 impl해줘야한다.

image-20220322012506290

image-20220322012616521

중요) getter를 짠다고 해서 view에서 보여주는 방식을 생각하지마라
  • 딜러 카드를 view에서 1장만 보여준다고해서, 도메인을 설계할 때 반영되면 안된다.
Ready는 재료없이 빈 재료로 최초 생성되는 상태객체로서 getter로 외부에 보여줄 필요 없는 것 같지만, 1장 만 받아서 아직 다른 구현체 상태객체로 가기 전단계도 있다. -> 선택이다. -> 무조건 2장으로 다채워지기 전까지 cards를 외부에 보여주지말자 vs 1장 받았을 때 보여줘도 될 것 같다 —> 여기선 1장 일때 보여줘도 되니까, Ready에서도 getter가지도록 설계해보자.

image-20220322014520647

  • 1장을 가져도 못보게할려면 예외처리하면 된다.
Hit에서도 getter인 cards구현

image-20220322015709551

새롭게 추가된 추메/전메(getter) 구현 후 기존 중간카테고리-추클(Finished)을 포함한 다른메서드/중카를 무시하고 오로지 모든 구현체의 특정메서드 1개 중복제거를 위한 추가 [중카-추클] 더 위쪽에서 집어넣기

image-20220322090908046

중요) 기존에 이미 중간카테고리=추상클래스=구현체들 중복코드 제거용이 있음에도 -> 새로운 추메/전메 1개의 중복코드 제거를 위한 추클을 더 위쪽에 끼워넣는다

중요) 만약, getter같이 ALL 중복 메서드가 추가되었다면, 기존 중간카테고리=추클로 묶인 구현체자식들을 포함한 새 중카=추클을 더 위쪽에서 한번 더 먹어준다고 생각하자

중요+정리) 새롭게 추가된 모든 구현체의 추메/전메의 중복코드 제거를 위한 중간카테고리 - 추상클래스 추가 후 할일: 1 abstract달기 2 인페를 impl후 <자식들이 아직 구현안하고 있으면 전과되서 화내니> 상카(인페) 모든 추메/전메 impl하기 or <자식들이 이미 개별 구현하고 있는 상황 == 현재 묶이는 범위가 아니라면ex.중카의 중카추가 상황> 기존 추메/전메는 그대로 전과시켜놓고 새로 추가된 중복되는 추메/전메만 impl구현하기 3 추메메서드 impl되었다면, 자식들딴짓못하게 막기 4 만약 getter등 상태값을 필요로 하는 메서드다? -> 인페가 중복이어도 못먹어주던 상태값 중복 + protected생성자 중복도 고려해서 먹어주기(추클로 묶이는 놈들은 대부분-> 정보를 가진놈이니 상태값+생성자는 거의 같이 묶일거라고 본다.) 5 중복코드 특정 구현체 1개에서 가져와 복붙해주기 6 구현체들(여기선 중카도 포함)을 인터페이스 구현체 -> 추클의 자식으로 바꾸면서, 중복코드들 제거해주기

my) 메서드명 중복이 우선 -> 인터페이스 —> (모든or일부) 구현체들의 (특정)메서드 내용까지 중복 발생 —> 추상클래스 —> 추클 중복코드제거시 인터페이스가 못하던 상태값과 그에 필요한 생성자도 중복제거를 고려해줄 것
  • getter cards()모든 구현체+ 추클(자식들->추클에서 먹기)이 중복이다
    • 이럴 경우, 다 묶고 싶어서 새로운 중카-추클을 더 위쪽에 집어넣는다.
  • 중간 카테고리는 상위카테고리 State(대명사)의 특정 상태로서 형용사로 네이밍하면 편하다
    • State <— 형용사Started —> Finished + 아직 덜 끝난 개별구현체 상태들
public abstract class Started implements State {
}
중카추클1) abstract (or final)을 class앞에 달아주기
public final class Started {
}

image-20220322093339157

중카추클2) 인터페이스 impl후 구현체들+기존중카추클이 기존 메서드들에 대해 이미 구현 전과되어서 개별구현 중인 추메/전메는 그냥 두고 -> 새롭게 추가되어 impl(개별)구현없이 중복 되어있는 메서드에 대해서만 중복제거용으로 먹어주기

image-20220322094429464

  • 추클은 impl하라고 빨간줄 안뜨니 직접 챙겨서 해야한다. image-20220322094509392

    image-20220322094519188

  • 추클 impl시 이미 자식들or기존 중카 추클에서 전과되어 개별 구현하고 있는 내용은 그대로 둔다.

image-20220322094442066

  • **stay+draw는 이미 전과 개별구현 중이다. + 전체중복도 아니라서 고려대상도 아니다. **
    • 아래로 묶일 녀석들(구현체+@중카추클)에 대해 모두 중복되는 메서드만, 추클에 정의한다

image-20220322093845472

image-20220322094643172

중카추클3) 추클에 impl되는 메서드들은 자식들에게 내려주는 놈들이니 final or abstract를 달자

image-20220322094837233

중카추클4) 추클이 메서드 먹어주는 와중에, 상태값이 필요로하면 -> protected생성자와 상태값도 중복코드인지 확인하고(대부분 중복일 듯) -> 추클에서 먹어준다.
  • 현재 기존 추가된 중카=추클인 Finished에서도, 3개 구현체->자식들에 대해, 상태값과 생성자는 중복이라서 중복코드로서 추클이 먹어준 상태

    image-20220322094300599

  • 여기(Started)에서도 모든 상태객체는 모두 getter를 가지며 -> 그에 따라 상태값+생성자 역시 모두 가질 것이니 -> 상태값과 그에 필요한 protected생성자를 추클에 정의해주자

    1. 상태값을 생각해서 -> 생성자에 가공안된 raw상태값을 파라미터로 받아주도록 정의

      image-20220322094950958 image-20220322095057156 image-20220322095110397

    2. protected생성자에 따라서, 필드는 빨간줄 자동생성 해준다. image-20220322095206360 image-20220322095212638

중카추클5) 중복코드 구현체 중 1개 복붙해오기 (여기선getter + protected생성자 + 상태값이라 라 간단해서 복붙하는 것은 생략됨)

image-20220322095328044

image-20220322095342665

public abstract class Started implements State {

    private final Cards cards;

    protected Started(final Cards cards) {
        this.cards = cards;
    }

    @Override
    public final Cards cards() {
        return cards;
    }
}
중카추클6) << 기존 추클 >> -> 개별 구현체 순으로 기존 상카-인페를 impl하던 구현체에서 -> 끼어들어가는 새로운 추클을 부모로 extends하면서, 내려받을 중복코드를 삭제해주자
기존 중간카테고리가, 기존 연결을 끊고 더 위쪽에 생긴 새로운 중간카테고리를 부모로서 받아와 보자.
  • 기존 구조

    image-20220322105350380

  • State - Finished의 관계인 impl State을 끊고 -> impl State를 하고 있는 새로운 카테고리 Started를 끼워놓고 기존 추클의 부모 추클로서 extends new중카(추클)해주자

    image-20220322105439515 image-20220322105558778 image-20220322105608570 image-20220322105632340

중요) 추클은 protected의 생성자 중복(+상태값)도 정의해서 내려준다. -> extends하는 구현체성질 자식들은 주생성자호출 하듯 생성자 정의를 super()로 중복코드를 갖다쓴다

image-20220322111404310

  • 에러 해결할 때, 자식 생성자 내부에 super()를 써서, 부모생성자를 this주생성자처럼 사용하여 -> 상태값 초기화 중복을 해결해라고 한다.
중요) protected생성자를 내려주는 추클을 extends를 하는 순간, 생성자 없으면 super()로 생성자도 갖다쓸거다 or 이미 자식에 생성자 있었다면, (내려주는 super생성자 vs 기존 생성자가 [상태값 초기화 부분에서 부딪혀] 빨간줄 나는데) -> this.상태값 = 초기화 ] 대신, [내려주는 생성자super()를 주생성자로 생각]하여, 외부재료를 가공해서 넣어주자라고 생각하자. -> 내려주는 생성자가 부딪힌다면 상태값 초기화 부분이 내려주는 생성자 vs 기존 생성자가 부딪히니 -> 기존 생성자에서 super()를 활용해 초기화하여 해결한다.
  • 기존 추클이, 새로운 추클을 부모로 extends했다. image-20220322110858008

  • 그 순간부터 **중복코드 메서드 + 중복코드 protecetd생성자를 super()로 내려준다고 생각해야하며 **

    • 기존 생성자가 있다면, 내려주는 생성자 vs 기존 생성자가 가장 중요한 역할인[상태값 초기화 부분]에서 부딪힌다.
암기) 부모가 내려주는 생성자 vs 기존 생성자의 해결은 자식의 생성자 내부에에서 super()를 this()처럼 사용하여 상태값 초기화 역할의 중복을 해결해준다.
  • 부모의 protected생성자를 super()로 내려주고 있다면,
    • 자식 자체 생성자가 존재하긴 하나 상태값 초기화는 super에게 미룬다.
    • protected를 통해 호출가능해진 부모가 내려주는 생성자 super()를 자식 자체 생성자 내부에서 서서
    • 물려받는 생성자 vs 본인 생성자의 충돌인 상태값 초기화는 super()에 양보하고, 외부에서 재료받아 가공하는 것만 자식생성자에서 해줘서 충돌을 피한다.
암기) 추클을 받는 기존 추클도 역시, protecetd생성자 정의된 추클을 extends 한다면, 자식으로 자신 생성자 안에서 [스스로 상태값 초기화 하던 부분 this.value= value는 삭제] 하고 -> 부모내려주는 super()로 상태값 초기화 해줘야하는 것은 동일하다.
  1. protected생성자는 추클로서 자식들에게 내려보내는 중인데, 다시 자기가 extends추가된 중카=추클에서 protected생성자를 super()로내려받는 꼴이 되었다.

    • 기존에 자식 생성자들 속에서 protected로 물려주면 호출되는 super()로 상태값 초기화부분만 차지한다 image-20220322203437239

    • 현재

      • 그 위에 중카 추클또다시 protected로 생성자초기화 부분을 대신하도록 super()로 물려준다
      • 추클을 받은 기존추클도 super()로 상태값 초기화해줘야한다

      image-20220322204410574

중요) protected생성자를 내려 받는 자식으로서 super()를 호출해서 상태값 초기화로 바꿔줬다면? 부모 추클이 protected생성자만 가지고 있겠냐? 부모 추클이 상태값도 같이 가져서 자기꺼는 자기가 초기화하겠지 -> 그러면 부모한테 private 상태값도 정의되어 있으며 <그 상태값도 물려받으나 private으로 호출만 못할 뿐이다 -> 그래서 상태값 초기화를 super()에 맡기는 것

  1. 기존 추클도 내려받아보자.

    • 스스로 상태값 초기화하는 부분 삭제 image-20220322204553386

      image-20220322204605388 image-20220322204610682

    • protected생성자를 내려 받는 자식으로서 super()를 호출해서 상태값 초기화

      image-20220322204749223

암기) protected생성자를 물려받아, super()로 상태값 초기화를 대신한다? -> 부모 추클에서 protected가 초기화해주는 상태값도 private이든 protected든 물려받는다 -> 자식들이 중복코드로서 정의안한다 -> super()로 넘겨서 초기화하는 상태값은 부모에게 물려받는 상태값이니 -> 자식에게 선언될 필요없이 삭제한다.
  1. protected 생성자를 물려받아, 상태값 초기화를 super()에 맡겼다면, 호출가능(protected)하든 호출안되고 숨어있든(private) 상태값도 자연스럽게 같이 내려받으니, 자식에게선 삭제해줘야한다.

    image-20220322205347881 image-20220322205543053 image-20220322205552435

  2. 애초에 중복코드로서, 추클을 끼워넣어 먹어주려던 대상인 getter메서드도 구현체 중1개가 가지고 있는 중복코드로 간주하여 삭제해준다. image-20220322205634139

    image-20220322205716539

     public abstract class Finished extends Started {
        
         protected Finished(final Cards cards) {
             super(cards);
         }
        
         @Override
         public final State draw(final Card card) {
             throw new IllegalStateException();
         }
        
         @Override
         public final State stay() {
             throw new IllegalStateException();
         }
     }
    

50) 중카 추클을 끼워넣어서 중복코드 제거했던 [기존 추클 + 상카 구현체1(Hit) + 상카 구현체2(Stay) ] 중 구현체1,2 들의 중카 자식으로 변환처맇 해보자.

중요) 추클 자식으로서, 자신의 생성자 내부지만 상태값 초기화를 위해 재료를 super()에 넘긴다?? 그에 해당하는 부모가 내려주는 상태값도 같이 존재하며, 보이지 않지만 그것을 그 내려주는 상태값을 초기화 -> 보통 private으로 선언해서 내려주므로 호출 불가 -> 상태값을 가지고 있었다면 중복코드로서 제거한다
  1. 기존 인터페이스의 구현체 -> 그 사이에 끼워진 중간카테고리 추상클래스의 자식으로 바꿔주기

    • Hit

    image-20220322210458575 image-20220322210510485

  2. 생성자 -> 상태값까지 가진 추클을 extends하면

    • 자신의 생성자 내부에서 상태값 초기화를 super()에게 맡긴다
    • 사실 상태값 초기화 재료가 super()넘어갔다는 말은 보이지 않지만, 상태값도 같이 내려오고 있다
      • 상태값을 가지고 있었다면 중복코드로서 제거한다

    image-20220322210852707 image-20220322210905055

    image-20220322210916316

51) 사실 protected생성자->super() + 보이지 않는 상태값 -> 기존상태값 중복코드로서 삭제 가 중요한게 아니라 getter인 cards()의 중복제거가 목적이었으며, 그 목적코드를 구현하는데 있어서, 추클에도 상태값->생성자까지 정의해줘야해서, 물려줄 수 밖에 없었다.

  • 중복제거 목적 코드 삭제 image-20220322211114377
  • 기존에서는 직접 상태값을 선언해서 사용하였으나, 추클 자식으로서 물려받다보니 기존 코드에 깔려있는 호출불가한 상태값 cards는 어떻게 처리할 것인가?
    • 부모 속 private 상태값을 protected로 호출가능하게 풀지말고 유지하되-> 상태값을 응답해주는 getter or 타 메서드()를 호출해서 처리한다

대박) 추클의 자식클래스가 물려받아 안보이는 && private이라 호출불가한 상태값을 호출가능한 곳인 부모에서 상태값 응답(getter) or 변형 상태값을 응답해주는 메서드를 통해, 자식에서 상태값이 필요한 곳에 조달해준다. -> 상태값과 더불어 getter도 같이 물려받아야하며, 네이밍을 상태값()로 정의해서 받으면 -> 자식의 상태값 자리 ->상태값()로 바꿔주면 된다.

52) 상태값을 부모private을 물려받게 된다면, 기존 상태값을 사용하는 자식내 메서드들은 -> 부모 내에서 호출해서 상태값을 응답해주는 메서 메서드 또한 물려받으면 된다. = 부모내 상태값 응답 getter메서드까지 물려받는다.

중요) 자식이 부모에게 물려받는 priavate상태값을 자식내부에서 받는 방법은? getter까지 물려받는다 -> 부모내에서, 부모가 가진 private상태값을 응답해주는 메서드를 정의하고 -> 그것을 자식이 물려받아 호출하면 된다. + 상태값이 cards였으면 cards()로서 getter네이밍해서 자식의 cards -> cards()로 바꿔주기

my +중요) 어차피 상태값 + 생성자가 물려진다면 -> 중복코드가 상태값 or 중복코드가 상태값이 필요한 메서드(추클에 정의해줘야함)라서 어쩔수 없이 상태값+생성자도 물려줌 —> 중복코드가 상태값이 필요메서드라서, 상태값+생성자도 추클에 정의되어 물려준다면 -> 상태값 이름으로 getter도 같이 정의하고 -> 물려지게 해서, 기존 자식들이 쓰던 상태값들을 상태값()의 getter로 대체하게하자

  1. 부모에게 물려받게 되어, 기존 상태값을 삭제한 상태 image-20220322212324333

  2. 부모 속 private 상태값은 물려받더라도 자식내에서 호출할 수가 없다.

    • 부모내에서 가진 상태값을 응답해주는 메서드를 정의하고 그것을 자식이 물려받아 호출하면 된다.

    • 즉, getter까지 같이 물려받는다

      • 기존 상태값 호출 cards
      • 부모에서 물려받는 부모내 상태값 응답 매서드 cards()
        //final Cards currentCards = cards.add(card);
        final Cards currentCards = cards().add(card);
      

      image-20220322212616794

  3. Ready도 똑같이 처리해준다.

    image-20220322213029056

    image-20220322213043081

    image-20220322213058614 image-20220322213106752

암기+중요) 자식이 주/부 생성자가 있더라도, 부생성자는 받는 재료만 다르지 내부에서는this()로 주생성자에 의존하므로 -> 생성자 물려주는 추클 extends후에는 주생성자만 -> super() 만 바꿔주면 된다. this()를 사용하는 부생성자는, this()에 이미 변경된 주생성자(내부에super())를 이용할 것이기 때문에 그대로 두어도 된다.

image-20220322213258767

  • 부생성자는 this()를 쓰고 있어도 바꿔줄 필요없다 -> 그 this()가 주생성자며, super()를 통해 부모물려받는 상태값을 초기화하는 코드로 바껴있을 것이다.

    image-20220322213340732

    image-20220322213422788

    • 부생성자는 그대로 바뀐 this() 주생성자를 그대로 사용하면 된다.
  • 생성자를 super()를 사용하도록 물려받았다면 -> 상태값도 중복으로 물려받은 것이니, 기존 자체 상태값 삭제

    image-20220322215940315

    image-20220322215931991

중요) 자식이 이미 자체 상태값을 사용하고 있는 상황이라면 -> 부모야. 상태값 물려받은 후 자체 상태값 삭제할 부분을 대체할 getter 상태값()도 같이 물려줘라를 생각해야한다.
  • 자식의 자체 상태값을 지워서 나타나는 빨간줄은 부모야 이미 상태값 사용하던 자식들이 있다면, getter메서드도 같이 물려줘라

    image-20220322220353515

    image-20220322220400714

중요) getter물려주다가 private상태값+protected생성자도 물려줌 -> 구현체 레벨들이 super()를 쓰도록 생성자 재정의 하다가 접근제한자가 바뀔 수도 있다 -> 상태객체는 Ready(public) + 중카 추상클래스(proected)를 제외하고 default생성자를 확인해주자.

my+중요) 상속/추상화 관련 문제에서 생성자 열린상태(접근제한자) 및 재료 확인은 다이어그램으로 하자

  • 생성자만 on 시킨 뒤,
    • 추상클래스들 -> 물려줄 생성자가 있다면 노란색좌물쇠의 protected가 맞는지
      • 생성자 물려준다? -> 상태값도 물려준다 -> 자식들이 자기꺼 쓰고 있다면 부모에서 정의한 getter도 물려주기
    • 중간or최종 상태객체들 -> 검은색 점의 deafult가 맞는지
    • 최초 상태객체(Ready) -> pubic 및 상황에 따라 재료없는 생성자(부) + 이후 트리거에 의해 다음 상태갈 수 있도록 재료받는 생성자(주)2개를 가지는지
  • 다른데서 생성되면 안되고, 상태객체 -> 상태객체의 트리거에 의해서 변해야한다.

  • 일단 구현체레벨의 상태객체들 생성자를 재확인해보자

    image-20220322221136423

    • bust는 어쩌다보니… public으로 바껴있네 -> default로 생성자 접근제한자 변경

      image-20220322221202287 image-20220322221259454

        public final class Bust extends Finished {
                  
            Bust(final Cards cards) {
                super(cards);
            }
        }
      
    • Ready는 재료안받고 내부에서 빈재료로 최초 시작하는 생성자 하나만 public image-20220322221354442

53) Stay는 재료없이, stay부터 시작하도록 테스트를 했었었네? 최종상태객체로서 다른카드에서 -> 트리거 -> 메서드 내부에서 재료받아 생성되야하므로 -> 재료받는 것으로 테스트도 수정

image-20220322221528920

image-20220322221537218

public final class Stay extends Finished {

    Stay(final Cards cards) {
        super(cards);
    }
}

중요) 각 상태가 정보를 받아 상태값으로 유지하는 이유? (1) 현재 상태객체 속에서 가진 상태값으로 if 현재상태값.is다음객체인지()를 물어보고 맞으면 new 다음상태객체( 다음상태객체를 만족하는 현재 상태값정보를 가지고 만들어줘야하기 때문에)

my+깨닮음+중요) 생성자에 재료가 없다면 항상 똑같은 new 객체()만 만들 수 있다. 하지만 ` 상태값을 (가공전이라도 괜찮으니) 생성자의 재료로 받게 되면 -> new 동일Type의 새로운 객체( 를 업데이트된 상태값를 재료로` ) 만들 수 있게 되며

image-20220323012004189 image-20220323012325392

my+깨닮음+중요) 현재 상태값의 업데이트 by 메서드()외부 추가정보가 필요하다면, 메서드(파라미터)를 활용할 수 밖에 없다. 현재 상태값을 사용가능한, this 객체내 메서드내에서, 메서드 파라미터 == 외부 추가정보를 받는 곳을 활용해서 -> 추가 정보를 활용해 상태값 업데이트를 하고 -> 새 객체를 생성해서 응답해주면 된다.

image-20220323012517627

image-20220323012928287

my+깨닮음+중요) 만약, 업데이트된 상태값을 가졌어도, 동일Type의 새객체가 아닌 다른Type의 새객체를 생성할 수도 있다 -> 어떤Type이든 해당 정보를 재료로 받는 생성자만 있으면, 상태값업데이트=새객체를 생성할 수 있는 능력을 가지게 된다. -> 동일Type이든, 다른Type이든 해당 상태값을 재료로한 생성자를 가진 객체라면, 상태값 업데이트 로직을 가진 메서드내에서, [if 업데이트된 상태값이, 생성자는 허락된 다른 객체Type으로 갈 수있는 상태값이됬니? ]를 물어본다면, 다른Type객체로 응답되어 [업데이트 객체 생성 메서드 뿐만 아닌 객체변환의 메서드]도 될 수 있다.

  1. draw( 추가정보 card1장)은 if문이 없으면 추가 정보로 업데이트된 new Hit객체 반환 메서드d일 뿐이다.

     @Override
     public State draw(final Card card) {
         final Cards cards = cards().add(card);
        
         return new Hit(cards);
     }
    
  2. **하지만, 업데이트된 상태값에 [if문으로 다른객체가 될 수도 있는 상태값으로 업데이트 됬니?]물어본 뒤, [같은 상태값을 재료로 받는 생성자]를 가진 다른Type의 객체로도 변할 수 있다. **

    1. if 업데이트된 상태값이 -> 다른객체로 변할수 있는지 물어본다.

    2. 아니라면, 업데이트된 상태값으로 -> 같은객체의 새객체를 생성하도록 한다.

       @Override
       public State draw(final Card card) {
           // 4. (+추가정보를 활용해) 업데이트된 상태값으로  -> 새객체를 생성후 응답해주면, 객체 업데이트 메서드가 완성된다.
           final Cards cards = cards().add(card);
              
           if (cards.isBust()) {
               return new Bust(cards);
           }
              
           return new Hit(cards);
       }
      
my+깨닮음+중요) 다른Type객체의 new 객체( 현재객체의 상태값 재료 ) 가 보여도, 아~ 업데이트된 상태값으로 -> 비슷한or같은 상태값을 공유하며 - 상태값을 재료를 받아 생성한 객체라서 그 객체로 생성가능하구나. -> if로 물어봐서 다른Type객체 생성후 -> 응답을 통해 외부에서 넘어갈 수 있구나 정도로 생각한다.
중요+요약) 같은 상태값 + 상태값을 재료로 받는 생성자를 가진 다른 Type 객체상대방 상태값 업데이트시 언제든지 넘어갈 수 있다. ( 같은Type의 업데이트 된 새객체 생성은 당연한 것임)
  • Ready는 같은 상태값을 공유하는 구현체-레벨의 상호교환가능한 여러 상태객체들같은 재료(가공전?/직접 상태값)를 받는 생성자를 가진다 -> 같은 상태값을 가지는 객체들로 해석 한 뒤

    • if문으로 물어보면서, 같은 상태값 && 같은 상태값을 재료로 받는 생성자를 가진 객체를 생성하여 응답해서 넘어갈 수 있다.

    image-20220323112059592

    image-20220323112416748

중요) 중간, 최종상태 객체는 Ready or 다른객체에서 현 객체 내부에서 상태값 업데이트후에 -> if 상태값.is다른거니?() 판단후에 넘갈 수도 있므로 같은 상태값 && 그 상태값을 재료로 받는 생성자를 가지고 있는 상태여야한다.

  • 기존 테스트 오류 1 : Cards가 아니라 개별 카드 2장을 받아? -> 지금 통일된 상태값의 재료로 받고 있어! image-20220322221709094 image-20220322221751015

  • 기존 테스트 오류 2: 재료가 없어? 중간상태 or 최종상태객체가? -> Ready빼고는 같은 재료를 생성자로 받아서 넘어갈 수 있는 상태다.

    image-20220322221552607 image-20220322221842574

현재까지 다이어그램 -> 추상화 레벨이 안맞다

image-20220322222115823

  • 현재 Fininshed의 중간카테고리만 있어서 다른쪽과 추상화 레벨이 맞지 않은 상태이다

    image-20220322222631961