📜 제목으로 보기

  • 분기문 자체를 enum에 [찾을 때 지연실행될 로직]으로서 매핑할 수 있으며, 이미 시그니쳐가 정해진 함수형인터페이스를 이용해 돌면서(values().stream.filter내부) 지연 수행(.test)될 분기문을 [가상인자+람다식으로 실시간 외부 구현]으로 매핑해야한다

로또 구현

  1. 도메인 및 객체 산출이 어렵다면 controller부터 정제된 input을 받고, output도 받환해야하는 service부터 출발한다.

    • 로또부터? 로또번호부터? 로또 게임부터? 로또 서비스부터?
      • 정제된 입력을 받아 구현할 수 있다면 서비스부터
    • 블랙잭 카드부터? 블랙잭 게임부터? 블랙잭 서비스부터?
      • 정제된 입력은 이름 밖이고 카드를 사람당 랜덤 2장 뽑아야해서.. 그걸 지서
        • 2장 받는 카드부터
  2. 서비스부터 짠다면, input -> output까지 메인 흐름을 생각해야한다.

    • 입력1: 사용자의 로또번호 + 보너스번호 입력
    • 입력2: 당첨번호 입력
    • 출력: 당첨등수 응답

    image-20220728162510238

  3. 정제된 input -> string을 split등 전처리를 다 끝낸 원시형 + 컬렉션(List.of() - 어차피 불변객체 넣어줄테니까, 조작불가능 얕은복사 사본)을 사용해서 만들어준다.

    • 아직 도메인 객체 추출을 못했으니, 원시형 + 컬렉션으로 작성한다

      image-20220728163518798

  4. 테스트할 메서드는 무조건 응답하도록 먼저 작성하며 이 때, 응답값은 [넣어준 인자에 대한 case값을 응답]을 해줘야한다

    • 만약, 로또번호 vs 당첨번호 인자 입력을 1등 번호로 예시case로 넣어줬다면, 그 인자 case에 맞는 1등이 응답값으로 반환해야한다(아무거나 반환X)
    • 인자가 3개이상이면, 엔터쳐서 줄바꿈해준다.

    image-20220728164141495

  5. 테스트 성공시 refactor -> 다음case (2번째부턴 일반화함)를 준비한다.

    image-20220728164441770

  6. 2번째 case를 넣기 전에, 1case 인자를 그대로 사용한 체 2메서드를 복붙 생성하고 이 상태로 로직을 짜면서 if등을 활용해 1case 통과하도록 짜도 된다. 1case통과 시하면 2case에 맞는 인자로 변경하여서 본격적으로 짜도 된다.

    image-20220728170621819

  7. 새로운 case(2번째라서 일반화)를 추가하되, 2번째 case추가 == only 1case -> 일반화로서 일반화 로직 추가 -> 2메서드로 작성한다

    1. 서비스는 상태값이 없으므로 지역변수에 결과값이 나올 것인데, 1case가 통과하도록 if를 걸어주며 짠다.
    2. 컬렉션 vs 컬렉션 비교는 반복문 + 요소vs컬렉션 비교 + 업데이트 지역변수가 사용된다.

    image-20220728170431531

  8. 1case가 통과되도록 짠 뒤 -> 2case(일반화 케이스)로 짠다

    • 이 때, ValueSource(고정 값 1개)나
    • CSVsource(고정 값 2개이상)나
    • MethodSource(객체 이상)을 활용해서 경계값 케이스 위주로 확인한다.

    image-20220728175850278

    • 통과되면 2메서드로 기존테스트 돌린 뒤 대체
  9. 매칭5개가 2등이 아니라, 매칭 5개 + 보너스볼이 2등이고, 5개는 3등이다.

    • 만약, 6-5-4 순으로 내려온다면 (7-매칭갯수)만 return하고 분기는 없어질텐데
      • 예외적인 상황이 발생하므로 if분기를 가지고 가야한다.
    • 중간에 예외를 처리하기 위해 if분기로 보너스볼 매칭까지 확인해야한다.

    image-20220728180710783

    image-20220728181122812

  10. service 메서드를 통해 정제된input에 도메인 지식이 쌓였으면, SERVICE에서 input중 가장작은 단위의 input부터 예외발생해야하는 case를 작성하고 -> 도메인에서 검증로직을 작성한다. 이후 그다음 input단위인 컬렉션에서 예외발생 case를 작성하고 -> 도메인에서 검증로직을 작성한다.

    1. service에서 가장작은단위 input에 대한 예외 case를 작성한다.

      image-20220728183332991

    2. 원시값에 대한 검증을 위해 검증이 필요한 원시값new 때려서 도메인객체로 포장한다.

      1c4cb04e-9b9e-4cc7-b508-8104852a4357

      image-20220728190348563

      image-20220728190403725

    3. 현재 1개인자만 포장해줬기 때문에 컴파일에러가 뜬 상황임. 먼저 수정해준다.

      image-20220728191113904

  1. 인자를 1개만 포장했다면 -> 전체 인자 다 포장한 뒤, 메서드의 파라미터도 변경해줘야한다. image-20220728191545604

    • 이 때 쓰는 것이 오버로딩이다.

      • 메서드의 파라미터 타입이나 시그니쳐가 달라졌을 때
      • 기본값을 입력해야할 때 ex> 꼬리재귀 재귀함수 최초호출 기본값 인자

      d64320e3-eb1c-4789-b252-3953a0357cf5

    • 기존의 원시값컬렉션인자 -> 객체 컬렉션인자로 가려면, 기존 메서드 내부에서 포장처리를 한번 해줘야 한다.

      1. 원시값컬렉션 파라미터 메서드 내부에서 -> 도메인 컬렉션으로 변경하여, 도메인컬렉션 파라미터 메서드를 호출하도록 한다. image-20220728192253411

      2. 원시값 로직은 아직 일급컬렉션을 도입안했다면, 로직을 그대로 도메인컬렉션 메서드 내부로 옮길 수 있다

        image-20220728192341780

      3. 다만, 다만 도메인 컬렉션(List)가 contains( 단일도메인 )처럼 원시값처럼 도메인을 사용하려면, eq/hC재정의를 해줘야한다.

        image-20220728192450626

      4. 다시 원시값으로 인자 입력이 가능해진 상태이므로 서비스 호출시 에러가 나도록, 원시값 인자 입력으로 바꿔준다

        • 만약, 도메인 인자를 입력하면 service메서드 진입전 도메인 자체에서 에러가 난다.

        image-20220728192822612

      5. 테스트가 통과되었으면 리팩토링해준다.

        83866bec-7892-4f6d-8117-b906cbc6f3ae

      6. 테스트에 도메인인자가 모두 사라졌다면, 오버로딩으로 내부 호출되는 도메인 파라미터 메서드는 private화 해준다. image-20220728193339887

    • 서비스에서 원시값의 도메인 포장 로직이 반영되었다면, 도메인 자체의 검증Test도 시행해준다.

  2. 이후 도메인 컬렉션 인자 포장시 [이미 원시값 인자 존재 + 1개의 오버로딩 존재 + 도메인컬렉션 파라미터는 필요없을 때]

    1. public 원시값 컬렉션 파라미터 메서드가 있는 상태에서
    2. 원시값 -> 도메인변환 -> 오버로딩 private도메인컬렉션 파라미터 메서드
    3. 오버로딩 private도메인 파라미터메서드만수정대상으로 삼아 2메서드를 만들고
      • 도메인 컬렉션 파라미터 -> 내부에서 일급컬렉션 변환
      • 오버로딩 private일급컬렉션 파라미터 메서드 대신, 내부에서 만든 p->변환메서드->p2(일급컬렉션) 자체를 파라미터 추출로 올려
        • 도메인컬렉션 -> 일급으로 변환을 오버로딩 내수용 메서드의 인자에서 호출 + 내부는 변환로직의 return값인 p2(일급컬렉션)이 파라미터가 되게 한다.
  3. 적용해보기

    1. 수정대상 메서드는 private내수용 오버로딩 [도메인 컬렉션 파라미터] 메서드이다. image-20220728222835121

    2. 수정 대상을 2메서드로 복사한 뒤, 내수용 오버로딩 메서드라서 여기서 테스트는 못한다. public 메서드로 테스트할껏이므로 상위메서드는 2메서드를 사용하도록 일단 바꾼다.

      36bae828-6526-4c07-ab14-0fa31695e773

      image-20220728223012491

    3. 2메서드의 파라미터를 변환하여 파라미터가 되길 원하는 값을 return하는 변환메서드(or 생성자호출)를 작성한다.

      • p1 -> 변환메서드(p1) 추출 -> 예비 p2 상태에서

      • 변환메서드를 파라미터 추출하면

        • 외부메서드호출부( 변환메서드(p1) ) -> 내부는 (p2)가 파라미터가 된다.
        • 파라미터 변환 적용하기 작전이다.
      • 변수명은 똑같이 추출해서, 잠시는 에러나더라도 같은이름으로 변경하여, 내부로직에서 변경사항 파악이 쉽게 빨간줄들어오게 해놔야한다.

        b5faea6a-1923-4e92-9dd9-eff70abd30e6

      image-20220728223805702

      image-20220728223820394

  4. 파라미터 변환(도메인 컬렉션 -> 일급컬렉션) 변경에 따라 컬렉션 에서 물어보던 것들을, 일급컬렉션 내부러 던져야한다.

    1. 일단은 빨간줄을 없애도록 작성한다.
    2. 파라미터 변경메서드로 기존테스트가 잘돌아가는지 확인한다.

    936f41b6-5089-4147-8b5c-079a63aadd39

  5. 일급컬렉션에 원하는 검증로직(중복검사 by distinct.count vs size, 갯수)를 확인한다. -> 예외발생 테스트 통과시 2메서드를 반영한다

    image-20220728225112598

  6. 이제, 도메인 객체(Lotto, 일급컬렉션)의 경계값 테스트도 작성해야햔다.

    1. 현재는 도메인 컬렉션 생성자만 있다. 원시값 컬렉션 파라미터 생성자를 추가하고 싶다.

      • 파라미터 추가인자 그대로 작성후 -> 변환 -> 오버로딩의 과정으로 추가한다고 했다.

      537c2489-1865-4ba3-9f56-77bae17e7db9

      • 만약, 원시값 배열로 입력하면 가변인자(배열)로 받는다.

      image-20220728233822464

      image-20220728233846657

  7. 서비스 start~end까지 로직이 짜여졌으면 해당 로직에 맞는 메서드명으로 변경한다.

    • start() -> match()

    image-20220729121535788

  8. 서비스getter가 보이면, 도메인 내부 로직으로서 캡슐화로 감춰야하는 로직임을 100% 생각한다.

    • 출력을 제외하고 getter는 없다고 보자.
    • getter이후가 같은형의 비교면 -> 해당형으로 책임을 위임해 옮긴다
    • getter이후가 다른형의 비교면 -> 제3형을 만들어 책임을 위임한다.
    • 같은형의 비교시 -> 내부 메서드로 돌아갈 때, 하나는 other라는 파라미터명으로 잡아서 처리해준다.
    • 일급컬렉션엔 일반 컬렉션이 못했던 책임위임을 할 수있다.
      • 내부에서 알아서 하도록 / 출력할때 빼곤, 객체에 getter를 쓰지 않고 위임한다.
  9. 같은 형 2개의 비교로직 위임이면, getter를 쓰는 하나만 타겟팅해서 위임받을 context로 잡아야한다.

    • 위임받지 않는 녀석만 파라미터에 포함되도록 하려면

      1. static에서 static으로추출된 상태로 옮기면 위임받는 객체가 this등으로 리팩토링 안되게 된다.

      2. 객체에 위임하는 로직은 메서드 추출후, static있다면 삭제해야한다. static내부 nonstatic메서드로 빨간줄이 떠도 참아야함

        • static은 공용, 상태없는 유틸메서드이므로.. 파라미터 input -> output형태라서 this등 리팩토링 안됨

        f4ac0669-9119-45b5-a82c-e1d1b2127774

        image-20220729123957410

        image-20220729124009064

      3. 같은형의 객체 2개가 파라미터에 있으면, 타겟팅할 변수를 선택하라고 인텔리제이가 알려준다.

        • private변수를 객체에 위임할 땐, Escalate -> Public으로 바꿔서 이동시켜준다.

        c158be1a-3e35-458c-9673-8d7055af69e6

        image-20220729124055503

        image-20220729124113547

        image-20220729124212538

      4. 같은형의 비교에서 파라미터에 있는 같은형은 other로 네이밍해주자.

        image-20220729124604547

    • 메서드 추출로 위임되었으면 getter()호출부를 삭제한다.

      3ae22d96-e1e6-46fd-b833-325f96165f2d

  10. 제한된 종류의 상수가 보이면 enum 값객체로 대신할 수있다.

    • 이 때, 네이밍은 의미_원래값형태로 해주면 된다.
      • 1 -> RANK_1
      • 0 -> RANK_NONE

    image-20220729130130395

    76f2edae-da52-492e-b13a-b344878608da

    • 제한된 종류의 상수를 작성한 뒤 원래값을 매핑해둔다.

      de54771b-9bef-4ac2-b3ae-ddd9a5c8e7b0

      image-20220729130728692

      image-20220729130739063

    • 응답값이 상수에서 -> 값객체enum으로 변경되었으니 테스트도 다 수정해준다.

      image-20220729131056477

  11. **enum은 **

    1. **{} : 값객체가 외부에서 파라미터로 입력되면 추상클래스로서 추상메서드를 이용해 [가상인자+람다식]에의해 수행되도록 전략객체로서 행위를 구현해놓을 수 있지만, **
    2. (): 분기별 값객체 반환분기문 자체를 값객체에 매핑해놓고, values()를 통해 매핑된 정보를 바탕으로 해당 값객체를 반환해주는 정적팩토리메서드가 될 수 있다.
  12. 현재 분기별로 생성된다. -> 정적 팩토리 메서드로서 [분기를 함수형인터페이스로 매핑하여 돌면서 지연실행될] 생성메서드를 만들고 위임해야한다.

    • 기존 : 분기별 생성을 정적팩토리메서드에 위임하기 전 확인사항이 있다. image-20220729135615639
    1. 전체 값객체(enum)생성 분기문을 위임해야한다. 그전에 해야할 것이 있다.

      1. 위임의 첫단추는 내부context로 사용하는 값은 위임객체에선 외부context가 되도록 위임객체context외에 모든 context값들을 변수로 만들어서 추출해야한다.

      2. 특히 파라미터가 제일 많은 부분을 확인해야하며, 조건식 내에서 메서드호출된 것도 값이다!!

        • boolean문안에서 여러객체를 이용한 메서드호출() -> 1개의 응답값을 가지는 값(파라미터) 1개로서 -> 1개의 외부context로 위임될 수 있도록 추출될 로직보다 더 위쪽에 미리 1개의 지역변수로 빼놔야한다.

          image-20220729143012230

        • badCase: 로직 위임전 [내부 여러객체.메서드호출()]부를 지역변수로 안빼놨을 때

          • 여러객체를 사용한 메서드호출은 어차피 1개의 값으로 사용되는데, 연관된 객체가 모두 변수로 뽑힌다.

          a5704e03-7a3a-430e-bc57-6de5e94fd817 image-20220729143737025

        • GoodCase: 로직 위임전 [내부 여러객체.메서드호출()]부를 위임로직 더 위쪽에 지역변수 1개로 응답값을 받았을 때

          710eccce-c554-4ce1-8a24-ab02c97ae2ff

          image-20220729143915942

  13. 위임할 로직 전체보다 더 위쪽에서, 위임 로직 내부 객체.메서드호출()부를 지역변수 1개로 빼놓고 메서드추출한다.

    • 정팩메 위임의 메서드명은 of라고 지으면 된다.

      8f0ec9a8-4ed4-426d-a87b-9906f425e56d

      image-20220729144338718

  14. 위임할 정팩메of를 enum에 위임한다. 66394583-63e1-49ee-8775-e3d6b1f256dd

  15. enum은 내부 분기문들을 인스턴스에 ()매핑후 돌면서 찾기로 제거가 가능하다.

    1. 각 분기문들을 values() -> filter에 걸릴 수 있게 각 인스턴스에 지연될 실행될 로직으로서 매핑해야한다.
      1. 지연실행될 로직은 함수형 인터페이스(or전략인페.전메())으로 지연호출부 정의 -> 가상인자 람다식에서 외부구현(or전략객체로 생성)의 방법이 있다
  16. 지연실행될 로직은 전략패턴이 아니라면 미리 시그니쳐가 정의된 boolean을 반환하는 Predicate함수형인터페이스를 사용하여 정의한다.

    1. 매핑될 boolean반환형 함수형인터페이스의 변수명은 condition으로 편하게 지어주자.

    2. enum에 매핑되어있는 condition을 찾아서 .test()로 지연실행할 것이다.

    3. 이 때, 람다식의 가상인자로 구성되는 분기식에 쓰이는 실제 인자들지연실행 메서드에 넣어줘야한다.

      61772918-118d-4dd2-827f-5c5d5ca82cb5

      image-20220729145725742

  17. 매핑할 함수형인터페이스는, enum의 필드로 선언해줘야한다.

    • 빨간줄 생성하면 filter속 .test()로 사용될것을 인식하여 BiPredicate변수로 만들어준다.

    fef3f129-0039-4a72-b3a9-f65123c7801b

    image-20220729150010288

  18. 값매핑 필드 삭제, 함형 필드는 생성자에서 추가한 뒤 각 분기문들을 가상인자 람다식으로 구현해야한다.

    • 이 때, 분기문이 없는 enum필드도 매핑되어야하므로 분기문을 만들어주고 매핑한다.

    image-20220729152531939

    • Bi로 정의했기 때문에, 1개의 파라미터만 쓰더라도, 파라미터가 많은 것을 따라야한다.

      b9714d6d-c5b4-4d6c-93a0-f49c5dac3019

      image-20220729153240181

      image-20220729153257506

  19. enum도 메서드 생성시 도메인 테스트를 해줘야한다.

    image-20220729213638417

    image-20220729213656362

  20. 정적팩토리메서드로 class를 객체찍는템플릿에서 능동적인 객체관리자로 승격시킨다.

    • new 생성자 : 100% 객체를 생성해야함(캐싱못함)
    • 정펙매
      • 캐싱(재사용) 가능해짐.
    1. 인자의 갯수에 따라 .of or from으로 public static 메서드를 생성한다.

    2. 기존 생성자와 동일한 형으로 key / 재사용할 객체형을 value로 해서 HashMap을 만든다. / 캐싱할 인스턴스 객체수를 알고 있다면 capacity를 저적어준다.

      • 변수는 static변수로서 상수의 map으로서 jvm돌때 미리 생성된다.

      fdf430f0-78d6-456b-8886-a66b363b0560

      image-20220731113819787

    3. 캐싱의 핵심은, 존재하면 map에서 get으로 꺼내고, 없으면 생성이다

      • map의 초기화도 미리 이루어진다.

      96c640ce-9600-4c59-ba85-20f1efb9ea44

      image-20220731114201232

    4. 없으면 key-value 넣어주기, 있으면 꺼내기는 map.computeIfAbsent( key, 람다식으로 value생성식)을 넣어주면 된다.

      • 람다식은 가상인자로 작성해야해서, key값을 그대로 넣어주면 에러남.

      e487c03a-c2da-4ec3-953c-e059eca42fb2

      image-20220731114834750

    5. 정적팩토리메서드가 완성되었으면, 기본생성자는 private으로 막아두고 각종 검증방법은 정팩메로 이동시킨다.

      c5e8e02d-0fa3-4695-a519-216388391380

      image-20220731115302033

    6. 기존 new때려서 생성한 객체들을 of로 다 수정해준다.

      image-20220731115746576

    7. 캐싱의 테스트는 식별자 일치를 확인하는 .isSameAs를 사용해서 테스트한다.

      a33cf917-9f91-4f90-9e2b-68e797d53184

      image-20220731115026661

    8. 여러 쓰레드에서 접근한다면, 미리 모두 static생성 + ConcurrentHashMap으로 변경하고 getter만 하게 한다.

      • static 컬렉션 변수의 요소들은 staitc블록으로 요소들 미리 생성하게 할 수있다.
      • static블럭이 늘어나면, 해석하는데 힘이쓰여 좋지 않ㄴ다.

      1acaf790-305c-4d20-a617-d25f7cc2352b

      image-20220731120759176

  21. 생성할 수 있는 파라미터를 추가해 생성자가 많아 견고한 클래스를 만들 수 있다.

    • 일반 생성자든 정펙매(캐싱 등) 생성자든 정팩메or생성자에 또다른 파라미터를 추가할 수 있다.
    • 상위 파라미터(더 원시) 추가는 변환 후 오버로딩을 하면 된다고 했다.
      • 참고) 더 하위 파라미터를 추가해도 마찬가지지만, 더 하위 파라미터(기존 파라미터 변환해서 만들어지는)로 변환은 변환후 -> 메서드추출 -> 파라미터로 추출로 바깥에서 일하게 만들어서 파라미터를 바꾼다고 했음.

    f63afa79-9d49-4db8-af83-ca4c73c4e9a9

    image-20220731152229083

  22. 불변 일급컬렉션을 만드는 과정

    • 멋도 모르고 컬렉션<단일객체>을 생성자에 통채로 집어넣어 만든 일급컬렉션은 사실 상태 변화(setter/add 등) -> [변화된 상태의 새 객체]를 생성해서 반환하기 위해 생겨난 생성자임을 생각해야한다.
    1. 단일객체에 대한 복수형으로 final 불변의 일급컬렉션 클래스를 생성하고, Set으로 단일객체를 보유할 빈 컬렉션을 조합한 필드를 가지고 있는다.

      be034d02-8ef8-430e-a79b-aa6981b12351

      image-20220731155154252

    2. 필드에 대해서 setter(상태변화) / getter를 고민해야한다.

      • 단일객체가 외부에서 변수로 받아져 조작되는 객체라면, 객체 자체를 파라미터로 받는 setter로 만든다.
      • 컬렉션에 대한 setter는 add/remve등이다. add로 단일객체 파라미터를 받되
      • 상태변화의 setter(add)는 상태변화된 새 객체를 반환하여 불변성을 유지한다.

      f8c3e381-7d52-40d3-ab9f-1de7c9415775

      image-20220731155645815

      1. 2번에서 상태변화된 컬렉션으로 새 객체 생성해서 반환을 하기 위해컬렉션을 통째로 받아 생성하는 생성자가 필요했음을 생각한다.
      2. 즉, 필요에 의해 객체컬렉션을 받는 생성자가 생겼음을 인지한다.
    3. 컬렉션 필드의 상태변화는 기존 컬렉션필드가 변하지 않도록 사본으로 복사한 다음 상태변화시킨 후 새객체 생성을 해줘야한다.

      • 컬렉션필드를 수정할 땐, 항상 사본복사후 연산하자.
        • 생성자를 통한 복사는 객체요소는 같은 주소를 바라봐서 요소변화가 가능한상태로 복사이다.

      62548315-caf2-4b2b-826b-6cb4a66b516b

      image-20220731162206685

    4. 기본생성자외 컬렉션 필드의 생성자가 생성되는 순간, 빈컬렉션의 필드초기화는 의미가 없어진다. 최초의 시작은 빈컬렉션으로 될 수 있도록 부생성자로서 추가해줘야한다.

      70742dde-326d-48ef-9798-c10674e600c2

      image-20220731161528409

    5. 파라미터가 없는 생성자는 부생성자이므로 파라미터가 있는 주생성자를 this로 사용해줘야한다.

      7d75e887-f5ac-44ee-b68a-2ea0f0745aaf

      image-20220731161701801

    6. 불변의 일급컬렉션에 요소를 add하는 과정은 매번 재할당해줘야한다.

      • 불변 객체를 받는 변수는 재할당이 운명이다.

      • 일급컬렉션은 getter().size()가 아니라 .size()메서드를 정의해서 쓴다.

      image-20220731163156321

참고스샷

image-20220728172531672