📜 제목으로 보기

  • 참고 유튜브 : https://www.youtube.com/watch?v=navJTjZlUGk
  • 정리본: https://github.com/LenKIM/object-book
  • 코드: https://github.com/eternity-oop/object
  • 책(목차) : https://wikibook.co.kr/object/

상속과 확장(11장)

  • 11장에 대한 코드의 변형이 많을 것이다.
    • 코드가 많이 나올 것이다.
  • 11장의 앞부분은 solid원칙 or solid원칙 확장 이야기
    • 객체지향을 위한 억지스런 코드들도 등장함

point of point

  • 11장부터 끝까지 후반부
    • 실제 JVM, c++컴파일러가 어떤 형식으로 실제 컴파일 결과를 만들어내는지
    • 그 시스템을 우리가 어떻게 이용해서 객체지향을 구현하는지
    • 주 기법은 포인터의 포인터 = 레이지 바인딩 직접 그 대상을 알지 않고, 그 대상을 알고 있는 놈을 알고 있어서 물어봐서 알기
  • 메모리에 올라갈 때는 linked list 그 이상도 그 이하도 아니다.
    • 인터페이스 - 추상클래스 - 구상클래스 - 메서드로 찾아들어간다는 것
      • 찾는 메소드가 얘한테 없으면 쟤, 쟤한테 없으면 얘 등 찾아들어가는 linked list다
    • 우리가 개발할 때는, 사람들이 이해할 수 있는 블럭들을 줘야한다.(linked list로 일렬로 연결하면 안됨)
      • 객체지향적으로 사고할 수 있는 공통적인 사고의블럭(틀)을 먼저 줘야, 그 안에서 블록을 이용해 자유롭게 구현이 가능해진다.

상속(inherit)

  • 유산을 물려 받는 것
  • 상속은 이미 사망한 사람한테만받을 수 있다.
    • 객체지향에서 상속이라는 말이 어울리지 않는 이유
    • 살아있는데 물려주는 경우 = 증여
  • 상속했다? = 유산은 가지고 있지만, 망자와는 교감을 할 수 없다

증여(bestowal)

  • 증여 해준 쪽과 외적(» 내적) 상호작용할 수 있다.
    • 상속과 증여는 모두 40% 세금을 뗀다
      • 증여가 아까울 것 같지만, 아파트의 경우, 가격이 오른다고 가정해서 아들이름으로 증여후 사면 40% 세금은 아무것도 아니다.
  • 증여받고도 괴롭히면 싫다.
    • 오피스텔 물려줬는데, 또 와서 용돈달라고 하는 자식.. 증여한 것으로 먹고 살아야하는데…
  • 재산 외적으로 교감은 환영한다
    • 자식이 놀러가자고 하면 좋다. 근데… 위에처럼 증여해줬는데도 내적으로 상호작용하면 싫다.

확장(extend)

  • 원래 A가 있었는데, 상속받은게 아니라 덧붙여야 확장이다

    • 프로그래밍에선 확장이 extend 외에 + overriding, overloading도 있다.
    • 확장 : 덧붙이기외에 뜯어내기 등 구조변경, 용도변경 등을 다 포함한 단어
  • 프로그램 짤 때, 자동차에 날개를 달면 안된다. 확장수준이 아니다.

    • 처음부터 따로 작성되어야한다. 이게 힘들다. 어느 순간 따로 만들어야겠어가 나와야한다. 이 판단을 할 줄 알아야함
    • 확장은 점진적인 변화만 인정해준다

유산을 물려받지 말 것

  • 점진적인 변화만 인정한다. 부모로부터 물려받지 마라
    • 오버라이드 하지말라는 뜻이다.
    • 물려받을 수 있는 유산 2가지
      • 프로텍티드 하지말라는 뜻이다.
      • super 쓰지말라는 뜻이다
      • public은 논외다.

대리역할을 하지 말 것

  • Type만, 한 카테고리에 있음만 나타내면 된다.

오히려 확장하는 쪽이 부분 책임만 질 것 -> 가장 이상적인 extends관계 -> 점진적이면서 부수적인 주인공이 아닌 놈이 되어야함

  • 수동적인 관계처럼 보이지만, 원래 그놈이 모든 일을 다하고, 확장하는 쪽은 딱 1가지 일만 하도록한다
  • 객체지향에서 확장된 쪽은 굉장히 부수적인 책임만 가져야한다
    • 점진적 + 부수적이어야한다. 주인공이면 안된다.

2가지 나쁜확장

나쁜 확장 1: (생성자, 기능)super는 나쁘다.

  • super는 다 나쁘다
    • super의 구체적인 작동
      • 생성자: 생성자 체인으로 바로 super를 사용
      • 나머지: super.으로 사용 -> 부모의 메서드를 부르는 것
약간만 보태면 될 것 같아서…
  1. result를 해결하기 위해 base라는 클래스를 만들었다. image-20220412204200433

  2. result2라는 문제를 다음날 받았는데, 어제만든 base에서 약간만 보태면 될 것 같다 -> extends

    • 문제점: result2를 result의 부분집합으로 판단했다
    • 문제점2: result를 불변한다고 본 것

    image-20220412204220761

    • **어떤 문제의 부분집합문제라고 봤던 result는, 현실에서 불변이 아닐 수 있다. **

    image-20220412204347756

    • 문제가 변했다고 base를 바꾸려할 것이다…

    image-20220412204421999

    • 이 순간 result2는 base가 달려저서 더이상 문제해결이 안된다.

    image-20220412204512039

super를 쓰면 반드시 망가진다. 기존 문제는 불변이 아니다. -> 확장을 부분집합형으로 해결하는 방법은 다 망가지게 되어있다. (약간만 보태서 확장하면 해결될 것 같은 사고방식은 망가진다) -> 점진적+원래 역할외 부수적인 역할만 해야한다.

나쁜 확장 2: (기능, 메서드)override는 나쁘다

  • 미묘해서? 나쁘다.
    • 내가 여보를 사랑해
    • 나의 자식이 여보를 사랑해??
      • 깔려있는 어마무시한 context를 알고 있기 때문에
  1. base의 기능호출 -> 동그라미 모양(=Type ex> integer)으로 나온다.

    • 객체지향은 Type으로 말을 하므로 작동결과를 Type으로 알 수 있다.

      image-20220412204941903

  2. base를 @Override하면, 응답값 = Type = 나오는 모양은 같다.

    • 오버라이딩하면, 네모 모양으로 나오지 않을 것이란 것을 안다

    image-20220412205133622

    • 동그라미형으로 Return될 것이다.

    image-20220412205206277

    • 개발자들은 여기서, 나는 오버라이딩해서 부모의 context를 유지할 수 있을 것이라고 생각한다.
      • 부모의 응답Type을 보고서, 나도 오버라이딩한 다음 응답Type만 맞춰주면 될 것이라고 생각한다.
  3. Type모양, 카테고리만 맞춰준다고 해서, 부모의 context를 다 충족한 하며 + 추가 확장기능이 생긴다고 말할 수 있을가?

    • 예를 들어, 과목 전체 평균을 내주는 base에 대해, override이후 과목 전체 평균 + 과목별 평균도 내어주도록 확장했다?
      • base는 보안성 때문에 일부러 과목별 평균 안내고 있던 상태인데?
      • base의 최종 Type만 override한다고해서, 내부 context와 다른 것들과의 관계를 다 알수도, 재현할 수도 없기 때문에 override를 쓰면 안된다.
바보가 아니므로 부모(응답)Type을 맞춰줄 순 있지만, 내부 사정 + 외부여파의 context를 정말 다 파악해서, 같은 context를 유지할 수 있을까? 못한다 -> 오버라이드가 성공하는 경우는 없다 -> 어느 모듈은 나혼자가 아니라 내 내부 + 외부 관계된 context들로 정의되기 때문에 -> extend는 override하지말고 써라

좋은 확장

조건1: super 대신 부모생성자는 인자받지X + 부모메서드는 final, private, abstract protected를 써라

image-20220412210602281

  • final, private으로 자식이 못쓰게 하거나
  • abstract면서 protecetd으로 자식이 쓰되 부모는 동작X -> 기능X -> context X면서 Type만 정해준다
    • super function 사용X
    • super Type만 사용됨.
  • super는 메서드외에 부모생성자 호출로도 사용되는데
    • 부모생성자가 인자를 안받으면, 자식은 호출할 일이 없어진다고 한다.
  • 인터페이스는 이 조건1의 2가지를 강제한다
    • 추클을 쓸 뗀, 이 2가지 제약을 걸어주자.

조건2: 부모 메서드에 3가지

image-20220412210607561

  • 조건1에서 메서드에 걸어둔 3가지를 고수하면, 오버라이드를 할 수도 없다.

부모메서드에 final + private + abstractprotected 3가지를 외우자. 모던랭귀지(코틀린)는 내장하고 있다.

우리가 상속을 사용하려면, 부모메서드가 final or private or abstract protected로 만들고, 생성자는 인자를 받지마라 -> 만약 지킬수 없는 상황이라면? -> 이때서야 상속을 포기하고, 합성해라 -> 지킬 수 있다면 상속을 많이 써도 된다.

image-20220412211115221

상속을 쓰면, 저번 수업 마지막 템플릿메서드vs전략패턴의 차이 -> 상속을 쓰면, 클래스조합폭팔은 피해갈 수 없다. -> 피하기 위해서는 has-a모델 = 합성으로 바꿔야한다. -> 합성은 끝없는 확장을 위해 열어두는 방법이다. 대충 상속 + 3가지 처리만 해주자.

코드로 좋은 확장 보기

Plan

image-20220412211805689

  • 부모가 자식용으로 열어줄 메서드는 abstract proected를 만들어야하므로 부모클래스는 abstract class만 가능하다.

    • 3가지 조건(final, private, abstract protected) 중에 마지막 조건인 abstract protecetd를 쓰려면, 부모클래스는 abstract class일 수 밖에 없다.
      • final, private만으로도 해결가능하면 abstract클래스 아니여도 된다.

      image-20220412212108947 image-20220412212113050

  • plan이라는 요금제도 안에서 call생성자가 아니라 final 메서드로 받도록 병합시키도록 하고 있다.

    • 부모클래스는 생성자로 인자를 받으면 안된다. -> 따로 final, private 메서드를 파서 받아라

      • plan에서 생성자로 받으면 더이상 상속구조를 못만들고 끝난다고 생각해야한다. 생성자가 인자가 없으면 그래도 괜찮다. 하지만, 인자를 받는 생성자인 순간 상속구조를 못쓴다고 생각하자.

      image-20220412212347679

  • calculateFee()라는 메서드는 바깥쪽에 템플릿 메서드로 제공하고 내부에서 같은이름으로 자식이 개별 구현할 abstract protecetd추클을 사용함으로써 (여러)자식이 부모를 쓰는게 아니라 부모가 자식을 알고 쓰는 구조가 된다.

    • my) 템메패턴을 쓰면, 자연스럽게 의존성이 역전된 좋은 상속이 된다.
    • 자식들은 내부에서 전체 calls가 아니라 개별 call별로 작은 역할로서 수행해주는 부분적인 역할만 가져 내려간다

    • 확장에서는 상속받는 자식들은 작은역할만 하도록 계산하도록 해줘야한다. 되독이면 쪼여야한다.
      • 확장시 최소한으로 줄여서 시켜라
  • 속성은 다 private 메서드는 final + abstract protecetd + 인자받는 생성자 안 존재함.

PricePerTime (extends Plan)

image-20220413141031290

  • 정책을 확장하여 일정 시간별로 금액을 부여하는 정책
    • 인자로 얼마를 부여할지, 몇초마다 부여할지를 입력받는다
      • 예, 1분에 18원
  • 본인의 상태만 관리한다.
    • 부모가 인자받는 생성자가 없기 때문에 -> 좋은확장으로서 super에 대한 의존성이 없다
    • 자기자신의 상태외에는 관리하고 있찌 않다
  • 상속받은 메서드는 (abstract protected) 템플릿 & 추클 메서드라서 -> super.에 대한 의존성이 전혀 없다

  • 즉, 자신의 필드관리만관리(no super) + 추클구현으로 넘어오는 인자1개만 결합한 메서드만 있어서, 상속써도 사고가 안난다

좋은 확장의 자식메서드 판별법: @Override protected인지 확인하여 -> 부모의 템메 + abstract protected 메서드를 구현했는지 확인한다.

image-20220413141019227

@Override protected에 실패했다면 -> 상속패턴을 쓰면 안된다.
부모에서 private필드+final or abstract protected / 자식에서 @Override protected가 아닌 이상엔 확정버그라고 판단하면 된다.
  • 단위테스트도 필요없다.

NightDiscount (extends Plan)

image-20220413141813273

  • 낮가격, 저녁가격, 몇초에 한번 부과할지를 받는데,
    • 22 시간도 받아야하는데 하드코딩했음.
  • 자기자신의 상태만 생성자로 받아서 관리한다
  • 하나의 콜에 대해 자신의상태 + 넘어온 call로만 계산함
    • 부모의 의존성을 줄였다.
    • 할게 조금이니까 부모의 사정이 안궁금하다.

확장의 요령이자 상속의 경우의 수: 자식이 조금만 참여하도록 -> 부모가 대부분을 끌어안고, 자식한테 쪼금만 위임할 수 있는 경우만 상속이 유리 -> 그외에는 상속을 쓰면 안된다!!

  • 차이가 굉장히 쪼금해서 자식별로 조금만 구현하고, 대부분은 부모가 먹는 경우에만 상속이 유리하다.

나머지는 class를 쓰면 안된다. class에는 역할(메서드)이 들어가기 때문에 -> 인터페이스를 쓰는 항목으로 바뀌니 이왕할거 구현체가 다 역할을 하도록 해야한다. 즉, 추클이 거의 다하고 일부만 자식에게 주던지 vs 아예 배째고 인터페이스 쓰든지

  • 추클인데, 어중간하게 일하면서, 어중간하게 넘길 때 문제가 발생한다.
    • 다 넘긴다 -> 인터페이스
    • 안넘기고 조금만 준다 -> 추클으로 상속
객체지향에서는 극단적으로 책임을 넘기만, 우리는 그 책임을 열라게 잘게 쪼개서 안정적으로 만들어야한다.

합성

  • 템플릿메서드기계적으로 전략패턴의 전략객체로 만들 수 있으며, 그 방법이 상속모델 -> 합성모델로 바꾸는 것과 마찬가지다

    • 판단은 조합폭팔이 일어날 것 같을 때 기계적으로 바꾸면 된다.

    • 연습은 상속이 보이면 조합으로 바꾸기(합성)를 해보면 된다.

      • 평소에 조합으로 바꾸는 연습을 많이 하자.

      • 재귀함수를 for루프로 <-> for루프를 재귀함수로 맘대로 바꾸듯이 말이다.

코드로 좋은 확장(상속) -> 조합으로 변형해보기

Plan

  • 기존: abstract class를 상속한 자식들은 템메 내부의 calcCallFee(call)의 작은 책임만 나눠가지도록 되어있었다.

    image-20220413144020973

  • 소유모델: 상속해서 자식들이 처리할 문제들을 전략객체가 처리해줄 것이다.

    image-20220413144230856

[부모] 소유모델=전략패턴, 합성으로 바꾸면서 변하는 부분
  • abstract class -> 그냥 normal class로 돌아간다
  • 자식들로 위임되는 메서드 (abstract protected)의 역할 -> 선택되어 받아와, 소유하게 될 전략객체가 역할을 한다.
normal class vs final class 선택해야한다. -> 상속대신 조합써놓고, 상속가능하게 부모를 final class가 아닌 normal class로 열어두었다?

image-20220413150009357

  • 상속(확장) 대신 합성을 적용한 상태지만, final을 쓰지 않았다 -> plan를 확장한 다양한plan을 만들 수 있게 상속을 허가한 상태다.
    • 미래의 이 class는 상속될 가능성이 있다.고 본 것이다 하지만 아까 말했던 좋은 상속 3가지 조건이 만족되고 있어야한다.
final을 안달았다 = 다양한 xxx로 확장될 상속가능성( extends Plan을 할 class생성 가능성)을 열어두었다 -> 좋은상속 3가지 조건이 지켜지고 있는지 확인해야한다. -> 규칙 1개라도 못지키면 그냥 final을 class에 걸어야한다.
  • 만약 좋은 상속의 3가지 조건인 private + final + abstract protected + 인자안받는 생성자의 조건이 안지켜지고 있다면, final로 막아야한다
얘는 normal class인데 final걸지 않아도 된다? 왜?

image-20220413150537554

  1. 부모에 (인자받는) 생성자가 없다
  2. 모든 필드, 메서드가 private, final을 만족하고 있다.
  3. 아니면 메서드는 abstract protected여야한다.

final을 안달아줘도 되는, 다양한 확장가능한 클래스다.

우리가 class에 final 안걸고 열어두는 것이 버릇인데, 그럴려면 사실은 좋은확장 원칙3가지를 지킨 애들만 final 없이 열어 둘 수 있다.
우리가 짠 class들을 검토해서 좋은 확장 원칙을 안지키면 final을 걸어두자. 기계적으로 걸어도 된다. 에러의 원인은 그곳이다. 상속(확장)됬는데, 그 전 것의 context를 못지키거나 불변취급했는데 변해서 문제가 생긴다.
  • Plan은 좋은상속의 조건/원칙을 만족시키는 클래스라서 열어둔 것이다.

  • 이제 상속없이, calc를 runtime시 주입만 해주면, 상속없이 알아서 처리된다.

전략객체를 받는 인터페이스는 내부에 전메/추클 1개가 Type만 지정해주면, 함수형인터페이스로서 완성된다.

image-20220413151905647

pricePerTime (extends plan -> impl 전략인페)

좋은상속의 자식은 -> 구현체(전략객체)로 바꿀 때 변화가 거의 없다
좋은상속일 때 자식(구현체) 코드

image-20220413152150230

전략패턴에서 전략객체(구현체) 코드

image-20220413152307274

[자식] 소유모델=전략패턴, 합성으로 바꾸면서 변하는 부분
  • extends 부모 -> implement 전략인페
  • @Override protected 메서드 -> @Overried public 메서드
    • 바뀌는 부분이 거의 없다
    • 원래 impl될 때 필요한 안전성이 좋은 상속 원칙 3가지에서 확보된 상태였기 때문

NightDiscount

image-20220413152726772

image-20220413152732456

  • 역시 마찬가지다.

포워딩

image-20220414154117369

  • 여기서 컨텍스트란 this or 변수 or 어떤 상태를 공유하는 것
    • 상태공유의 대표적인 예 ex> 꼬리무는 재귀함수
      • 여태껏 곱했던 것들을 넘겨주는 -> 업데이트된 상태를 외부생성자로 넘겨 받아 공유됨(어그리게이션)
    • 합성객체에 아무것도 공유 안하는 경우 : 포워딩이라 부른다.
      • 공유도 안하는데 뭔가를 시킨다? 위임(델리게이션)이라고 부른다.

my) 위임: 전략객체에게 중요 상태값(컨텍스트)이 아니라, 유틸함수에 필요한 재료 넘겨주는식만이루어진다.

plan

image-20220414154451857

  • 전략객체는 일을 위임받아서 하는데, result(결과)를 합쳐가고 있다. 하지만, result를 공유하진 않고, this도 안준다. call이라는 상태만 넘겨준다.

    image-20220414154641773

    • 하지만, 이 call이라는 것은, plan이 연산이나 업무처리시 공유되는 context는 아니다.
      • 값으로서 참조데이터만 넘어간 것뿐이지, 공유되는 컨텍스트 없이 유틸함수처럼 전략객체를 호출하고 있다.

연결되는 합성객체

데코레이터 패턴이라고도 부른다.

  • 하나의 객체 -> 인스턴스화 될 때, 자기만의 메모리를 가진다.
    • 인스턴스의 객체 속 필드만큼 메모리를 가진다.
    • 필드는 얼마나 가질 수 있을까?
      • 클래스 정의하기 나름이다
      • 팔드를 컬렉션으로 정의했다면? 배열<Object>라면, 하나의 인스턴스는 메모리는 무한인가?
    • 많은 사람들이, 컨설팅시 객체를 하나의 작은 컴퓨터라 부른다.
      • 우리가 남발하는 인스턴스 1개가, 옛날컴퓨터보다 용량(메모리)가 클 수 있다.
      • 우리는 캡슐화와 추상화를 통해 문제를 해결하기 때문에, 컴퓨터 1대가 문제를 해결해주는 식으로 생각한다.
    • 그렇다면, 객체 2개 -> 컴퓨터2대(서버2대)를 가지고 협력하면서 문제를 해결하려면?
      • 독립적으로 부르면 상관없지만, 협력해서 한다면?
      • 완전히 독립된 메모리공간에 있는 2 객체(컴퓨터2대)의 어떻게 협력할지, 협력관계를 정의해줘야한다.
        • 나는 너의 뭘 쓸거야,
        • 너는 어떤 경로로 나에게 리턴해줘. 나는 그것을 가지고 또 뭘 쓸거야
  • 연결된 합성객체 -> 독립되어있는 모듈끼리지만 병합된 결과를 주고받기 위한 인터페이스를 정한다는, 행위+개념이다.
    • 2개를 연결했다면 -> 또 다른 것과 2개씩 연결할 수 있다.
    • LinkedList와 연결된 합성 객체or데코패턴or체인오브리스폰서빌리티or composite패턴or …으로서
      • 하나의 context를 공유해서, 공유된 context를 계속진행시키는 개념을 계속 쓴다.
  • 중급 개발자를 가르는 기준이 된다.

재귀함수와 객체의 연결 차이

image-20220414160407785

  • 일반적인 재귀함수의 구조
    • 네모1개 = 함수
      • 인자를 받아들이는 곳
      • 리턴 포인트
    • 2번함수가 3번함수에게 리턴한 인자로 호출 -> 3번 함수는 인자를 받아들여서 수행

image-20220414160535380

  • 객체지향에서는 객체 != 함수이므로 위와 같은 모습을 보인다.
    • 하나의 객체속 특정메소드에서 -> 다른 객체가 발생하는게 아니라 다른 객체의 메소드로 보낸다.
    • 리턴할 때도 특정메소드의 위치로 가서 보낸다.
      • 그냥 인자/리턴으로 보내는게 아니라, 객체 속 메소드들끼리 보내고 받고 한다.
  • 연결되는 합성객체는 재귀함수의 확장판이라 볼 수 있다.

    • 재귀함수: 다 같은 함수들이라서, 부르는 함수의 포인터를 알고 있다. a,b,c,d 다른 함수여도 상관없다.
    • 연결된 합성객체들:
      • 같은타입 객체의 같은 메소드들 서로 불러도 된다.
      • 다른타입 객체의 특정(다른) 메소드들 서로 불러도 된다 -> 우리가 배웠던 객체간 메세지
  • 중요한 것은 시작객체의 메서드가 -> 다른 객체의 메서드를 정확하게 알고 있다는 것이다.

    image-20220414161138817

    • 객체가 메소드를 통해서 메세지를 보내려면 최소한 지식은 메세지 받을 객체 Type과 그 메소드에 대해서는 알고 있어야한다.
      • 객체는 형으로 말해서..?

image-20220414161428346

  • 가장 왼쪽의 클래스는 특이한 클래스(객체)다. 진입하는 entry point 클래스(진입점 클래스)

    image-20220414161554592

    • 애초에 모양자체가 특이함.
      • 왼쪽에서 오는 것을 받지도 않고, 오른쪽으로 주기만 함.
      • 바깥쪽으로 return도 안함.
    • java의 main함수처럼 진입점은 어디든 존재한다.
  • 진입점에서 -> 메세지를 주고 받기 시작하는데, 재귀함수처럼 연결된 객체들이 다 같은 Type의 객체임을 인식후 메세지를 주고 받는 경우를 배운다.
    • 다 같은 Type으로 인식되어 메세지를 주고 받으면 뭐가 좋을까? 연결된 4개 객체에 대한 지식이 1개의 지식만 있으면 된다.
      • 원래 4개 서로 다른 객체라면 객체Type과 해당메서드를 최소한의 지식으로 알아야했으니 X4배의 지식이 생겼다.
    • 다 같은 형이니까 형끼리 통신시 -> 1개의 지식만 필요한 데코레이터 패턴이다
  • 데코레이터 패턴에서는 같은Type을 상속 or impl하고 있는 모든 객체들(my)구현체들)이 통신에 참여하면
    • 이 객체는 뭔지 모를지라도, 데코레이터 인터페이스를 따른 객체(구현체)다 == 특정메서드를 특정할 수 있다 == 연속으로 대화할 수 있다

코드로 보는 데코레이터 패턴

Calculator

  • 기존 좋은상속 -> 합성으로 바꾼 전략 인터페이스 image-20220414162720490
  • 데코레이터 패턴을 적용한 전략 인터페이스 image-20220414162546527
부수적 책임(모) -> (아니면 도로서) 포괄적 책임으로 승격시켰다.
  • 상속계층을 잃어버린 이상에 -> 부모역할을 하던놈이 더 많은 역할을 가져봤자 책임만 분산된다.

    • 따라서, 적은 책임의 좋은상속자식 -> 그대로 책임만 합성한 전략패턴 -> 객체에 책임을 부을 수 있을만큼 전략객체에 부어넘겨주는 데코레이터패턴
  • call1개에서 Set<Call> 콜집합으로 책임이 늘어난 것이다.

  • 갯수에 따라 무게가 증가한다. 1개 알고 있는 것이랑 그 컬렉션, 여러개를 알고있는 것이랑 무게가 다르다. 책임이 늘어난 것이다.

  • aggregation용 context(my)누적, 업데이트되는 값)의 전달이 추가되었다.

    • 기존 Plan -> 보니 결과값들을 모아주는 변수도 넘어간다.

      image-20220414170744032

    • 책에서는 afterCalculator와 그냥 Calculator로 2개를 통해서 aggregator를 수집하도록 설명해놓았지만, 여기선 귀찮아서 합친 것이다.

      • 메서드 2개 -> 메서드1개 인자2개로 바뀔 수 있다.
      • 인자가 적은 함수가 좋은 이유 -> 인자가 많아지면, 거기에 aggregator가 개입될 가능성이 높아진 것
      • 여러인자를 쓰는 것도 싫은데, 함수 2개를 쓰는게 더 싫은 이유: 순서가 지식이 된다. 메서드의 순서는 컴파일러에 안걸려서 에러를 못찾는다.
        • 순서형 메서드들 분할 -> aggregator를 합쳐서 메서드1개로 만들어서 트랜잭션을 자꾸 만들려고 노력한다.
          • 학습용 코드라서, 순서형 메서드로서 2개가 나온 것이시지, 컴파일에러에 잡히게 만드려면, 1개의 메서드에 인자 2개를 넣어서 처리해야한다.

Plan

기존의 plan과 바뀐 plan

image-20220414172507395

  • calulator가 1개의 call만 처리하는 부분적인 역할을 가지고 있었으나

image-20220414172723275

  • 이제는 니가 하다하라고 전체 call를 다 밀어넣고, 누적을 시작하기도 전에, 누적 aggregator의 초기값을 넘겨줘서 모든 책임을 위임받는다.
    • my) 넘겨받은 전략객체 속 특정 메서드누적연산의 초기값 + 상태값 전체를 메서드로 넘겨주면, 책임이 많이 줄어든다.
    • 이제부터는, 전략객체를 구상한 구상클래스가 모든 책임을 진다.
  • plan의 줄어든 책임을
    1. call 컬렉션(상태값)을 관리한다.
      • 관리한 상태값을 넘겨줘서 연산은 전략객체가 시행하지만, 넘겨주기전까지 관리는 plan이
    2. 전략객체.특정메서드()에 call컬렉션을 전달해주는 것
  • 계산하는 모든 책임은, 이름 그대로 Calculator가 다 가져갔다.
    • my) 연산 값 1개만 전메로 만들지말고 -> 상태값/누적연산의 초기값 등 모든 연산필요 값들을 파라미터로 넘겨받아 모든 로직을 전메로 넘긴다.
책임을 다 넘긴 후에는, 문제 발생시 2가지를 나눠서 볼 수 있다.(올인된 책임으로 잘개 쪼개기 SRP)
  • 계산이 잘못이 잘못되면 Calculator를 보면 된다
  • 계산기의 투입이 잘못됬거나 or 잘못된 계산기가 투입됬다면, Plan쪽을 보면 된다.

  • 설계란, 큰 책임을 졸라게 쪼개되, 쪼갠 책임마다 각각이 올인되도록 만들어 SRP(단일책임원칙)만 준다.
    • my) 전략패턴, 합성으로도 모든 책임을 넘길 수 있다.
    • 전략당한 부모쪽 = 업데이트되는 calls를 상태값 관리 + 전략객체 Calculator를 관리
      • 원래는 calls관리가 내 책임
      • Calculator는 위임을 위해, 참조포인터로서 알고 있는 것임.
    • plan이 가지고 있던 책임을 쪼개서 전략객체 Calculator가 계산에 관련된 모든 책임을 먹었다.