📜 제목으로 보기

상호의존도 도시

image-20220704145850347

image-20220704145934631

image-20220704150038518

image-20220704150056987

image-20220704150138176

문제점

  1. Director 내부에서 추상체ProjectPaper를 가지고 있지만, 추상층의 정보(전략메서드, 템플릿메서드)가 부족하여, 특정형인지 확인후 작업한다.
    • instanceof는 2개이상이면 if의 문제라고 본다.
    • 개별구상체를 물어보지말고 시킬 수 있는지 확인한다. (혹시나 this로 넘어가면… 문제가 생길 수 있다.)
      • 중복로직이 존재하면, 템플릿메서드로
      • 중복로직 없으면, 전략패턴으로

image-20220704220332948

  1. FrontEnd, BackEnd내부에서 추상체 ProjectPaper를 받아오지만, 추상층에 물어보고(get) -> 필드에 저장(set)하기 위해서 if instacneof가 생긴 상태다.

    • 구상체별로 물어보지 않으려면 (getter -> setter)에서 set시키기 위해 this로 나 자신을 넘겨 시켜야 하는데
    • 나 또한, Programmer의 구상체로서, 추상체로 넘어가 -> 물어보고 set시킨다.
    • 나에게 set등의 로직을 가져 나를 넘기면서 시켜야하는 로직일 때, 내가 추상체로 넘어갈 상황이면 넘기지 말고 물어보도록 두자.
    • instanceof가 2개이상이라 if의 문제라고 본다.
    • 추상체에, 시켜서 해결하는 방법이 템플릿메서드, 전략메서드다. 하지만, 시키는 로직이 this(추상체)로 넘어갈 경우 따져야한다.
    • instanceof 2개이상이 해결이 안된다면
      1. 넘어온 추상체(paper)의 구상형들 자리에 제네릭 T를 넣어서 -> 인스턴스는 특정형을 사용할 수 있게 한다.
      2. if 2개이상으로 개별 구현로직이 생기기 때문에, 현재 class를 중간추상층 with T로 만든 뒤, 최종구상층에서 T를 개별구현시키도록 한다.

    image-20220704220242170

실습용 초기코드

해결1: 추상체 X 추상체로 인해 한쪽 구상체가 넘어오는 추상체의 instanceof 구상형(제네릭)마다 다른 개별로직(자체 추상화) 구현을 해결해보기

설명

image-20220705114815251

  • 넘어오는 추상체에게 시킬 수 없다면(그 추상체(1)가 this(N, 나도추상체)로 받아가서 더 복잡해지는 1:N관계)
    • 넘어오는 추상체에 전략메서드/템플릿메소드로 추상화로 시키기가 불가능 하다.
    • 그렇다면, Type에 따른 개별구현 로직은 나를 추상화 + 개별구현메서드를 T형으로 처리하는 수 밖에 없다.
    • 개별구현 로직이 구상층으로 넘어간다면, 나는 여러형을 T형으로 줘서, 최종구상층이 특정형을 알고 개별구현하게 한다.
  • 내부에 instanceof = 여러TypeT의 제네릭으로 대체하려면, 나는
    • 인스턴스<특정형>의 일반class이거나
    • T형에 따라 개별구현이 내부에 존재하는 구상체라면, 분신술 개별구현 익클<특정형>의 추상클래스여야한다.

image-20220704230521219

실습(템플릿 메소드 -> 제네릭)

  1. 제네릭으로 instanceof를 지우기 전에, 추상체 파라미터의 Type에 따른 개별구현 로직을 제거하기 위해, 전략객체에 추상화가 아닌 나를 추상화(중간추상층)화 해서 if 1개를 없애야한다

    • 각각의 if후 return createFrontEndProgram();의 공통로직이 존재하므로 템플릿메소드패턴으로 나를 추상클래스로 만들어 추상화해야한다.
      1. 여러 if들을 포함하는 전체로직public 템플릿메소드에 전체로직이 쌓여있는지 확인한다. -> 현재 완료
      2. 1개의 if에 대해 전체로직 내부의 개별구현로직내수용메서드 형태의 protected abstract 훅메서드로 추출한다
      3. 다른 if의 개별구현부는 주석처리if문을 삭제한다.
        • 다른 if의 개별구현 로직들은 최종구상체들이 사용할 로직으로서 주석으로 보관하여 훅메서드 근처에 옮겨둔다.
      4. 클래스에는 abstract를 달아주고, 훅메서드는 protected abstract가 달려있는지 확인한다.

    48460d16-f4bb-4c5d-b7df-0c6fcffe688f

    image-20220704232959134

  2. 1개의 if만 남기고 삭제하고(개별구현은 주석으로 챙겨놨다.) if 및 instanceof 삭제후 구상체 대신 추상체를 사용하도록 로직을 바꾼다.

    48460d16-f4bb-4c5d-b7df-0c6fcffe688

    image-20220704233430941

  3. 이제 최종추상층(익클-분신술)이 개별구현시 특정형을 알고서 && 그것을 받아 개별구현하도록 제네릭T extends upperbound처리를 해준다.

    1946016d-d5a9-4404-a7c3-edf0503f8bf0

    image-20220704234230845

  4. 이제 FrontEnd 인스턴스를 사용하던 곳에서 제네릭 + 익명클래스로 구현해야한다.

    • 제네릭을 안달고 implements 훅메서드 하면, upperbound가 T자리를 차지하니 제네릭을 먼저 달고 구현한다.

    • 개별구현은 T에 들어갈 구상체마다 다른데, 주석으로 보관해놨으니 참고한다.

    • 익명클래스는, 분신술의 원조 추상클래스의 필드를 protected로 변경하여 편하게 사용한다.

      048cc395-4237-48ce-9313-16630db6e43f

    image-20220704235111521

    image-20220704235917394

  5. 이제 추상체(paper) -> 추상체(programmer) 의 구상체(FrontEnd)이외에 나머지 구상체들(BackEnd)도 똑같이 추상화 -> 제네릭 처리 해준다.

  6. 중간추상층이된 FrontEnd와 BackEnd가 각각이 템플릿메소드로서 공통로직을 가져 -> 상위추상층을 템플릿메소드패턴 적용해서 올리기

    • 기존에 인터페이스가 상위추상층으로 있으면, 훅메서드가 안올라가므로, 추상클래스로 변경 먼저 해놓는다.

      bbc60cf7-3553-4e5b-b3d3-5a9708a182c5

    • compare file로 2개의 구상체를 띄워놓고, 구상체 1개를 선택하고 image-20220705001419625

      • public 템플릿메서드 + private내수용 훅메서드를 만들고, 각각의 메서드명을 추상화해서 올린다.

        • private메서드를 같이 members로서 같이 들고 올라가야하므로 밑에 것을 선택한다.

          image-20220705004915165

          • 훅메서드로 들고올라가는데, 이미 구현된 private메서드를 올리면, 자식은 부모를 개별구현된 상태로 남아있다

            • 반면 올리기 전에도 훅메서드(protected abstract)였던 메서드와, public 템플릿 메서드는 그대로 올라가서, 자식에게는 업섹 된다.

            image-20220705005739092

      71619253-47e6-40e1-871e-2dbdcf297ef4

      image-20220705010024732 image-20220705010034766

  7. 메서드를 올리는 FrontEnd내 메서드가 T extends ProjectPaper형을 써서 올리면, 상위추상층은 단지 upperBound로 구현하고 있으니 최종구상층이 특정형이 아닌 추상체를 써라는 에러가 나온다.

    image-20220705011136850 image-20220705011152782

    1. FrontEnd<T extends ProjectPaper>부모인 extends Programmer에 Programmer<사용 T>를 쓰도록 만들고

    2. Programmer class 자체에서는 <T extends ProjectPaper>로 정의한다.

      • 사용 T를 미리 지정해놓고 -> 자동완성 -> Change signature of to match <T>를 사용하면 된다.

      image-20220705010739708

    3. 이제 올라간 템플릿 메소드 및 훅메서드에서 추상형 대신 T형으로 정의해놓고, 최종구상층에서는 특정형을 알게 한다.

    image-20220705011009719

    378146c3-d352-47ac-a43b-04ea1cfdaf13 image-20220705011331878 image-20220705011308391

  8. backEnd도 마찬가지 처리를 해준다.

    image-20220705112755211

    • 제네릭이 제대로 되어있나 확인하고
    • 템플릿메서드는 지워주고, 훅메서드는 메서드명을 맞춰준다.

    5f3253b2-8246-445e-8868-6f80804ace9c

  9. 이제 FrontEnd, BackEnd의 사용처(Director#runProjectPaper)로 가보면

    • 자신들이 분신술로 인스턴스를 생성하면서, 특정형을 알면서 개별구현하도록 구현되어있다.

      image-20220705113305571

      image-20220705113327507

해결2: if instanceof는 [전략패턴]으로 시킬 수 있음 시키자.

설명

image-20220705115958422

  1. if instanceof는 추상체에게 물어보고, 내 내부에서 펼치는 것 -> 추상체에게 getter이후의 로직까지 시킬 수 있나 확인한다.
    • 현재 context도 같이 넘어가면 this로 넘어가는데, this가 추상체인지 확인한다. 만약 추상체라면 N:1관계를 확인한다.
    • 지금은 추상체 -> 각 구상체 -> director(this)에 영향을 주는 로직이 없으므로 메서드(추상체)로 추출 -> 이동 -> 추상체.메서드()로 시킬 수 있다.
  2. 공통로직이 충분히 있고, 틀이 잡히고, 나를 추상클래스로 + 훅 구상체class들(or익클분신술로 외부구현)을 만들거면 템플릿메소드패턴으로 추상화 / 그게 아니라 들어오는 추상체를 전략인터페이스로 간주하고 전략객체들만 만들거면 if 내부 전체를 개별로직으로 보고 전략패턴으로 추상화
    • 현재는 public 메소드내에 있지만, 공통로직이 없다고 보면 된다. deploy()는 director의 내수용 메서드이며, 추상체가 개입안하는 부분이다.
    • 구상체들로부터 추상체를 만드는 게 아니라, 내부 if 로직으로 템플릿메소드 패턴을 적용한다면, 내 자신(Director)이 추상클래스가 되어야하는데, Director를 추상클래스로 만든 이유가 없다. 들어오는 추상체에게 시키기만 하면 된다.
      • 템플릿메소드패턴의 추상클래스 + 훅 구상체들 = 전략객체주입 일반클래스 + 전략인터페이스 + 구상 전략객체들
        • director내부 내용을 익클 분신술 director로 생성하면서 구현할만한 size가 아니다.
        • 시킬 수 있으면 추상체에 시켜서 나를 추상화하면 안됨.
      • 나 자신을 추상클래스로 -> ADirector BDirector등으로 만들것이 아니라면, 템플릿메소드패턴X

실습(전략 적용)

  1. 여러 if들을 포함하는 전체로직 여러 if들내에 구상체가 사용되어 반환되는 로직을 확인한다.

    • 반환값이 2개이상이 되는 순간부터는 메서드 추출이 안되므로 반환값이 1개인 if가 있다면, 그쪽 if에서 메서드 추출한다.
    • 구상체로부터 이어지는 로직과 원래 현재class의 로직을 구분해서 if별 개별구현 부분을 확인한 뒤 메서드로 추출한다.
  2. 1개의 if에 대해 전략메서드로 처리될 개별구현 로직만 내수용메서드( 구상체 )형태로 추출한다. 전략객체가 될, 현재 파라미터의 구상체class로 move하면 전략객체.전략메서드()가 형태가 된다.

    • 1개의 전략메서드 내부 구현된 구상체class로 옮기기 위해서, 아직 추상체로 변수명을 바꾸진 않았다.

    • 내부에 사용되는 구상paper의 내용이 있다면, this로 바뀐다.

      image-20220705165759635

    08c3762f-b9ea-4f31-b52d-ac9291801455

  3. 추출한 예비전략메서드를 -> 선택된 구상체 내부로 로직을 이동시킨다.

    b1daf391-f67d-4b3c-a8a5-a005dc3582a2

  4. 옮겨간 구상체1개만의 전략메서드를 @Override를 통해 추상체(전략 인터페이스)에 오퍼레이터로 만들어주고, 해당 구상체if문을 추상체.전략메서드()형태로 바꿔준다.

    e59d391f-4887-4001-88d8-a24d686228a9

    9122c76e-dd89-4b24-ab54-7eba2d0dde84

  5. 다른 if의 개별구현부는 주석처리나머지 if문을 삭제한다

    • 다른 if의 개별구현 로직들은 최종구상체들이 사용할 로직으로서 주석으로 보관하여 훅메서드 전략메서드 근처에 옮겨둔다.
  6. 주석처리한 개별구현로직은 각각의 전략객체에 구현될 메서드에 옮겨준다. a1c1cb66-6f62-4560-87ac-da25bdb9b470

  7. 이 때, 반환값 갯수가 다를 경우, 배열을 응답값으로 활용한다.

    • 추출한 전략메서드는 1개 Program, 2번째 전략객체의 응답값은 2개 Program -> 응답형을 배열로 바꿔서 처리해준다.

    dda99355-f2fa-441f-abc6-cb5a82172db4

추가: 전략객체/템플릿구상체들의 개별구현로직을 외부로 밀도록 나 자신을 추상화

설명

  • if분기에 해당하는 전략메서드 / 훅메서드들을 특정 구상체class가 담고 있다. 해당 개별구현을 나를 추상클래스로 추상화 -> 외부에서 익명클래스로 개별구현한다면, 최후에 개별구현 로직을 client에서 DI 선택해서 해결가능한 형태를 만든다.
  • 나를 추상클래스화 해서, client에서 개별구현을 선택해서 작성하는 형태는, 개별구현로직이 달라질 때, 구상층 추가시 유용할 수 가 있다.

  • 참고

    image-20220705173409628

실습(전략객체들의 중간추상층화 -> 전략메서드 구현을 client로)

  1. 개별구현하는 구상체들을 모두 abstract 달아주기

    • 다는 순간, new 구상체();로 객체로 만들었떤 것들이 -> 단순 객체생성으로 사용 불가하게 됨.
    • 다는 순간, 필수구현이 -> 선택구현으로서 익명클래스에서 구현해도 되도록 변경된다.

    0332796c-30c4-4c17-9391-20e6f65ef31f

  2. 개별구현 로직을 복사한 뒤 -> 전략메서드 삭제 -> 생성자의 usage(alt + f7)를 통해 일반class의 객체로 생성되었던 곳(client)개별구현로직 복사해서 옮겨 구현하기

    • 추상클래스로 변화 -> 단순 객체로 생성했던 곳이 익명클래스로 구현해야하는 것으로 바뀜 -> 생성자 사용처들에 개별구현로직 복사한 뒤 구현해주기
      • 추상클래스상에 구현해서 막고 있는 전략메서드를 삭제해야 -> 구상체들 implement가 되니 구현로직만 객체생성처에 옮겨놓기
    • usage를 find Tool window로 보기 위해 alt + F7을 활용한다.

    298c4184-b479-43ca-954c-c059953eacec

    • 옮긴 뒤 context(this)가 달라지는 것을 수정해준다.
      • paper를 벗어난 상황이므로, paper의 필드를 얻으려면 this.필드 -> paper.getter()로 변경해줘야한다.

    2c37ba80-c72d-4657-ae9f-746693059809

  3. 나머지 전략객체(TxPackagePaper)도 추상화해주기

    1. abstract달기
    2. 생성자 사용처(객체 생성)에 가서 개별구현 로직 옮겨놓기
    3. 전략메서드 삭제후 생성자 사용처(객체 생성)에 익명클래스 구현하며 로직 만들기
  4. 참고

    image-20220705183543064 image-20220705184728692