OBJECT 16-1 좋은 상속을 합성으로 변경(코드스핏츠)
object 책을 강의한 코드스핏츠 유튜브 요약
📜 제목으로 보기
- 상속과 확장(11장)
- 코드로 좋은 확장 보기
- Plan
- PricePerTime (extends Plan)
- NightDiscount (extends Plan)
- 합성
- 코드로 좋은 확장(상속) -> 조합으로 변형해보기
- Plan
- [부모] 소유모델=전략패턴, 합성으로 바꾸면서 변하는 부분
- normal class vs final class 선택해야한다. -> 상속대신 조합써놓고, 상속가능하게 부모를 final class가 아닌 normal class로 열어두었다?
- final을 안달았다 = 다양한 xxx로 확장될 상속가능성( extends Plan을 할 class생성 가능성)을 열어두었다 -> 좋은상속 3가지 조건이 지켜지고 있는지 확인해야한다. -> 규칙 1개라도 못지키면 그냥 final을 class에 걸어야한다.
- 얘는 normal class인데 final걸지 않아도 된다? 왜?
- 우리가 class에 final 안걸고 열어두는 것이 버릇인데, 그럴려면 사실은 좋은확장 원칙3가지를 지킨 애들만 final 없이 열어 둘 수 있다.
- 우리가 짠 class들을 검토해서 좋은 확장 원칙을 안지키면 final을 걸어두자. 기계적으로 걸어도 된다. 에러의 원인은 그곳이다. 상속(확장)됬는데, 그 전 것의 context를 못지키거나 불변취급했는데 변해서 문제가 생긴다.
- 전략객체를 받는 인터페이스는 내부에 전메/추클 1개가 Type만 지정해주면, 함수형인터페이스로서 완성된다.
- pricePerTime (extends plan -> impl 전략인페)
- NightDiscount
- Plan
- 코드로 좋은 확장(상속) -> 조합으로 변형해보기
- 포워딩
- 연결되는 합성객체
- 참고 유튜브 : 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원칙
orsolid원칙 확장
이야기- 객체지향을 위한 억지스런 코드들도 등장함
point of point
- 11장부터 끝까지 후반부
- 실제 JVM, c++컴파일러가 어떤 형식으로 실제 컴파일 결과를 만들어내는지
- 그 시스템을 우리가 어떻게 이용해서 객체지향을 구현하는지
- 주 기법은
포인터의 포인터 = 레이지 바인딩
직접 그 대상을 알지 않고, 그 대상을 알고 있는 놈을 알고 있어서 물어봐서 알기
- 메모리에 올라갈 때는 linked list 그 이상도 그 이하도 아니다.
- 인터페이스 - 추상클래스 - 구상클래스 - 메서드로 찾아들어간다는 것
- 찾는 메소드가 얘한테 없으면 쟤, 쟤한테 없으면 얘 등 찾아들어가는 linked list다
- 우리가 개발할 때는, 사람들이 이해할 수 있는 블럭들을 줘야한다.(linked list로 일렬로 연결하면 안됨)
- 객체지향적으로 사고할 수 있는 공통적인 사고의블럭(틀)을 먼저 줘야, 그 안에서 블록을 이용해 자유롭게 구현이 가능해진다.
- 인터페이스 - 추상클래스 - 구상클래스 - 메서드로 찾아들어간다는 것
상속(inherit)
- 유산을 물려 받는 것
- 상속은 이미 사망한 사람한테만받을 수 있다.
- 객체지향에서 상속이라는 말이 어울리지 않는 이유
- 살아있는데 물려주는 경우 = 증여
- 상속했다? = 유산은 가지고 있지만, 망자와는 교감을 할 수 없다
증여(bestowal)
- 증여 해준 쪽과 외적(» 내적) 상호작용할 수 있다.
- 상속과 증여는 모두 40% 세금을 뗀다
- 증여가 아까울 것 같지만, 아파트의 경우, 가격이 오른다고 가정해서 아들이름으로 증여후 사면 40% 세금은 아무것도 아니다.
- 상속과 증여는 모두 40% 세금을 뗀다
-
증여받고도 괴롭히면 싫다.
- 오피스텔 물려줬는데, 또 와서 용돈달라고 하는 자식.. 증여한 것으로 먹고 살아야하는데…
-
재산 외적으로 교감은 환영한다
- 자식이 놀러가자고 하면 좋다. 근데… 위에처럼 증여해줬는데도 내적으로 상호작용하면 싫다.
확장(extend)
-
원래 A가 있었는데, 상속받은게 아니라
덧붙여야 확장
이다- 프로그래밍에선 확장이 extend 외에 + overriding, overloading도 있다.
- 확장 :
덧붙이기
외에 뜯어내기 등구조변경
,용도변경
등을 다 포함한 단어
-
프로그램 짤 때, 자동차에 날개를 달면 안된다. 확장수준이 아니다.
- 처음부터 따로 작성되어야한다. 이게 힘들다. 어느 순간 따로 만들어야겠어가 나와야한다. 이 판단을 할 줄 알아야함
- 확장은 점진적인 변화만 인정해준다
유산을 물려받지 말 것
-
점진적인 변화만 인정한다. 부모로부터 물려받지 마라
오버라이드 하지말라
는 뜻이다.-
물려받을 수 있는
유산
2가지프로텍티드 하지말라
는 뜻이다.super 쓰지말라
는 뜻이다- public은 논외다.
대리역할을 하지 말 것
- Type만, 한 카테고리에 있음만 나타내면 된다.
오히려 확장하는 쪽이 부분 책임만 질 것 -> 가장 이상적인 extends관계 -> 점진적이면서 부수적인 주인공이 아닌 놈이 되어야함
- 수동적인 관계처럼 보이지만, 원래 그놈이 모든 일을 다하고, 확장하는 쪽은 딱 1가지 일만 하도록한다
-
객체지향에서 확장된 쪽은 굉장히 부수적인 책임만 가져야한다
점진적 + 부수적이어야한다. 주인공이면 안된다.
2가지 나쁜확장
나쁜 확장 1: (생성자, 기능)super는 나쁘다.
- super는 다 나쁘다
- super의 구체적인 작동
- 생성자: 생성자 체인으로
바로 super
를 사용 - 나머지:
super.
으로 사용 -> 부모의 메서드를 부르는 것
- 생성자: 생성자 체인으로
- super의 구체적인 작동
약간만 보태면 될 것 같아서…
-
result를 해결하기 위해 base라는 클래스를 만들었다.
-
result2라는 문제를 다음날 받았는데, 어제만든 base에서 약간만 보태면 될 것 같다 -> extends
- 문제점: result2를 result의 부분집합으로 판단했다
- 문제점2: result를 불변한다고 본 것
- **어떤 문제의 부분집합문제라고 봤던 result는, 현실에서 불변이 아닐 수 있다. **
- 문제가 변했다고 base를 바꾸려할 것이다…
- 이 순간 result2는 base가 달려저서 더이상 문제해결이 안된다.
super를 쓰면 반드시 망가진다. 기존 문제는 불변이 아니다. -> 확장을 부분집합형으로 해결하는 방법은 다 망가지게 되어있다. (약간만 보태서 확장하면 해결될 것 같은 사고방식은 망가진다) -> 점진적+원래 역할외 부수적인 역할만 해야한다.
나쁜 확장 2: (기능, 메서드)override는 나쁘다
- 미묘해서? 나쁘다.
- 내가 여보를 사랑해
- 나의 자식이 여보를 사랑해??
- 깔려있는 어마무시한 context를 알고 있기 때문에
-
base의 기능호출 -> 동그라미 모양(=Type ex> integer)으로 나온다.
-
객체지향은 Type으로 말을 하므로 작동결과를 Type으로 알 수 있다.
-
-
base를 @Override하면, 응답값 = Type = 나오는 모양은 같다.
- 오버라이딩하면, 네모 모양으로 나오지 않을 것이란 것을 안다
- 동그라미형으로 Return될 것이다.
-
개발자들은 여기서,
나는 오버라이딩해서 부모의 context를 유지할 수 있을 것이라고 생각
한다.- 부모의 응답Type을 보고서, 나도 오버라이딩한 다음 응답Type만 맞춰주면 될 것이라고 생각한다.
-
Type모양, 카테고리만 맞춰준다고 해서, 부모의 context를 다 충족한 하며 + 추가 확장기능이 생긴다고 말할 수 있을가?
- 예를 들어, 과목 전체 평균을 내주는 base에 대해, override이후 과목 전체 평균 + 과목별 평균도 내어주도록 확장했다?
- base는 보안성 때문에 일부러 과목별 평균 안내고 있던 상태인데?
- base의 최종 Type만 override한다고해서,
내부 context와 다른 것들과의 관계
를 다 알수도, 재현할 수도 없기 때문에override
를 쓰면 안된다.
- 예를 들어, 과목 전체 평균을 내주는 base에 대해, override이후 과목 전체 평균 + 과목별 평균도 내어주도록 확장했다?
바보가 아니므로 부모(응답)Type을 맞춰줄 순 있지만, 내부 사정 + 외부여파의 context를 정말 다 파악해서, 같은 context를 유지할 수 있을까? 못한다 -> 오버라이드가 성공하는 경우는 없다 -> 어느 모듈은 나혼자가 아니라 내 내부 + 외부 관계된 context들로 정의되기 때문에 -> extend는 override하지말고 써라
좋은 확장
부모생성자는 인자받지X
+ 부모메서드는 final, private, abstract protected
를 써라
조건1: super 대신 -
final, private
으로 자식이 못쓰게 하거나 -
abstract면서 protecetd
으로 자식이 쓰되 부모는 동작X -> 기능X -> context X면서 Type만 정해준다- super function 사용X
- super Type만 사용됨.
- super는 메서드외에 부모생성자 호출로도 사용되는데
- 부모생성자가 인자를 안받으면, 자식은 호출할 일이 없어진다고 한다.
-
인터페이스
는 이 조건1의 2가지를 강제한다추클
을 쓸 뗀, 이 2가지 제약을 걸어주자.
조건2: 부모 메서드에 3가지
- 조건1에서 메서드에 걸어둔 3가지를 고수하면, 오버라이드를 할 수도 없다.
부모메서드에 final + private + abstractprotected
3가지를 외우자. 모던랭귀지(코틀린)는 내장하고 있다.
우리가 상속을 사용하려면, 부모메서드가 final or private or abstract protected로 만들고, 생성자는 인자를 받지마라 -> 만약 지킬수 없는 상황이라면? -> 이때서야 상속을 포기하고, 합성해라 -> 지킬 수 있다면 상속을 많이 써도 된다.
상속을 쓰면, 저번 수업 마지막 템플릿메서드vs전략패턴의 차이 -> 상속을 쓰면, 클래스조합폭팔은 피해갈 수 없다. -> 피하기 위해서는 has-a모델 = 합성으로 바꿔야한다. -> 합성은 끝없는 확장을 위해 열어두는 방법이다. 대충 상속 + 3가지 처리만 해주자.
코드로 좋은 확장 보기
Plan
-
부모가 자식용으로 열어줄 메서드는
abstract proected
를 만들어야하므로부모클래스는 abstract class
만 가능하다.- 3가지 조건(final, private, abstract protected) 중에 마지막 조건인
abstract protecetd
를 쓰려면, 부모클래스는 abstract class일 수 밖에 없다.- final, private만으로도 해결가능하면 abstract클래스 아니여도 된다.
- 3가지 조건(final, private, abstract protected) 중에 마지막 조건인
-
plan
이라는 요금제도 안에서call
을 생성자가 아니라final 메서드
로 받도록 병합시키도록 하고 있다.-
부모클래스는 생성자로 인자를 받으면 안된다. -> 따로 final, private 메서드를 파서 받아라
- plan에서 생성자로 받으면 더이상 상속구조를 못만들고 끝난다고 생각해야한다. 생성자가 인자가 없으면 그래도 괜찮다. 하지만, 인자를 받는 생성자인 순간 상속구조를 못쓴다고 생각하자.
-
-
calculateFee()
라는 메서드는바깥쪽에 템플릿 메서드
로 제공하고내부에서 같은이름으로 자식이 개별 구현할 abstract protecetd추클을 사용함
으로써 (여러)자식이 부모를 쓰는게 아니라 부모가 자식을 알고 쓰는 구조가 된다.- my)
템메패턴을 쓰면, 자연스럽게 의존성이 역전된 좋은 상속
이 된다. -
자식들은 내부에서
전체 calls가 아니라 개별 call별로 작은 역할로서 수행
해주는 부분적인 역할만 가져 내려간다 -
확장에서는 상속받는 자식들은 작은역할만 하도록 계산하도록 해줘야한다. 되독이면 쪼여야한다.
- 확장시 최소한으로 줄여서 시켜라
- my)
-
속성은 다
private
메서드는 final + abstract protecetd + 인자받는 생성자 안 존재함.
PricePerTime (extends Plan)
- 정책을 확장하여 일정 시간별로 금액을 부여하는 정책
- 인자로 얼마를 부여할지, 몇초마다 부여할지를 입력받는다
- 예, 1분에 18원
- 인자로 얼마를 부여할지, 몇초마다 부여할지를 입력받는다
-
본인의 상태만 관리한다.
- 부모가 인자받는 생성자가 없기 때문에 -> 좋은확장으로서 super에 대한 의존성이 없다
- 자기자신의 상태외에는 관리하고 있찌 않다
-
상속받은 메서드는 (abstract protected) 템플릿 & 추클 메서드라서 -> super.에 대한 의존성이 전혀 없다
- 즉, 자신의 필드관리만관리(no super) + 추클구현으로 넘어오는 인자1개만 결합한 메서드만 있어서, 상속써도 사고가 안난다
@Override protected
인지 확인하여 -> 부모의 템메 + abstract protected
메서드를 구현했는지 확인한다.
좋은 확장의 자식메서드 판별법: @Override protected에 실패했다면 -> 상속패턴을 쓰면 안된다.
부모에서 private필드+final or abstract protected / 자식에서 @Override protected가 아닌 이상엔 확정버그라고 판단하면 된다.
- 단위테스트도 필요없다.
NightDiscount (extends Plan)
- 낮가격, 저녁가격, 몇초에 한번 부과할지를 받는데,
- 22 시간도 받아야하는데 하드코딩했음.
- 자기자신의 상태만 생성자로 받아서 관리한다
-
하나의 콜에 대해 자신의상태 + 넘어온 call로만 계산함
- 부모의 의존성을 줄였다.
- 할게 조금이니까 부모의 사정이 안궁금하다.
확장의 요령이자 상속의 경우의 수: 자식이 조금만 참여하도록 -> 부모가 대부분을 끌어안고, 자식한테 쪼금만 위임할 수 있는 경우만 상속이 유리 -> 그외에는 상속을 쓰면 안된다!!
- 차이가 굉장히 쪼금해서 자식별로 조금만 구현하고, 대부분은 부모가 먹는 경우에만 상속이 유리하다.
나머지는 class를 쓰면 안된다. class에는 역할(메서드)이 들어가기 때문에 -> 인터페이스를 쓰는 항목으로 바뀌니 이왕할거 구현체가 다 역할을 하도록 해야한다. 즉, 추클이 거의 다하고 일부만 자식에게 주던지 vs 아예 배째고 인터페이스 쓰든지
- 추클인데, 어중간하게 일하면서, 어중간하게 넘길 때 문제가 발생한다.
- 다 넘긴다 -> 인터페이스
- 안넘기고 조금만 준다 -> 추클으로 상속
객체지향에서는 극단적으로 책임을 넘기만, 우리는 그 책임을 열라게 잘게 쪼개서 안정적으로 만들어야한다.
합성
-
템플릿메서드
는 기계적으로 전략패턴의 전략객체로 만들 수 있으며, 그 방법이상속모델
-> 합성모델로 바꾸는 것과 마찬가지다-
판단은 조합폭팔이 일어날 것 같을 때 기계적으로 바꾸면 된다.
-
연습은 상속이 보이면 조합으로 바꾸기(합성)를 해보면 된다.
-
평소에 조합으로 바꾸는 연습을 많이 하자.
-
재귀함수를 for루프로 <-> for루프를 재귀함수로 맘대로 바꾸듯이 말이다.
-
-
코드로 좋은 확장(상속) -> 조합으로 변형해보기
Plan
-
기존: abstract class를 상속한 자식들은 템메 내부의
calcCallFee(call)
의 작은 책임만 나눠가지도록 되어있었다. -
소유모델:
상속해서 자식들이 처리할 문제들을 전략객체가 처리
해줄 것이다.
[부모] 소유모델=전략패턴, 합성으로 바꾸면서 변하는 부분
- abstract class -> 그냥 normal class로 돌아간다
- 자식들로 위임되는 메서드 (abstract protected)의 역할 -> 선택되어 받아와, 소유하게 될 전략객체가 역할을 한다.
normal class vs final class 선택해야한다. -> 상속대신 조합써놓고, 상속가능하게 부모를 final class가 아닌 normal class로 열어두었다?
-
상속(확장) 대신 합성을 적용한 상태지만,
final을 쓰지 않았다 -> plan를 확장한 다양한plan을 만들 수 있게 상속을 허가
한 상태다.- 미래의 이 class는 상속될 가능성이 있다.고 본 것이다 하지만 아까 말했던 좋은 상속 3가지 조건이 만족되고 있어야한다.
final을 안달았다 = 다양한 xxx로 확장될 상속가능성( extends Plan을 할 class생성 가능성)을 열어두었다 -> 좋은상속 3가지 조건이 지켜지고 있는지 확인해야한다. -> 규칙 1개라도 못지키면 그냥 final을 class에 걸어야한다.
- 만약 좋은 상속의 3가지 조건인
private + final + abstract protected
+ 인자안받는 생성자의 조건이 안지켜지고 있다면, final로 막아야한다
얘는 normal class인데 final걸지 않아도 된다? 왜?
- 부모에 (인자받는) 생성자가 없다
- 모든 필드, 메서드가 private, final을 만족하고 있다.
- 아니면 메서드는 abstract protected여야한다.
final을 안달아줘도 되는, 다양한 확장가능한 클래스다.
우리가 class에 final 안걸고 열어두는 것이 버릇인데, 그럴려면 사실은 좋은확장 원칙3가지를 지킨 애들만 final 없이 열어 둘 수 있다.
우리가 짠 class들을 검토해서 좋은 확장 원칙을 안지키면 final을 걸어두자. 기계적으로 걸어도 된다. 에러의 원인은 그곳이다. 상속(확장)됬는데, 그 전 것의 context를 못지키거나 불변취급했는데 변해서 문제가 생긴다.
-
Plan은 좋은상속의 조건/원칙을 만족시키는 클래스라서 열어둔 것이다.
-
이제 상속없이, calc를 runtime시 주입만 해주면, 상속없이 알아서 처리된다.
전략객체를 받는 인터페이스는 내부에 전메/추클 1개가 Type만 지정해주면, 함수형인터페이스로서 완성된다.
pricePerTime (extends plan -> impl 전략인페)
좋은상속의 자식은 -> 구현체(전략객체)로 바꿀 때 변화가 거의 없다
좋은상속일 때 자식(구현체) 코드
전략패턴에서 전략객체(구현체) 코드
[자식] 소유모델=전략패턴, 합성으로 바꾸면서 변하는 부분
- extends 부모 -> implement 전략인페
- @Override protected 메서드 -> @Overried public 메서드
- 바뀌는 부분이 거의 없다
- 원래 impl될 때 필요한 안전성이 좋은 상속 원칙 3가지에서 확보된 상태였기 때문
NightDiscount
- 역시 마찬가지다.
포워딩
- 여기서 컨텍스트란
this or 변수 or 어떤 상태를 공유
하는 것-
상태공유의 대표적인 예 ex> 꼬리무는 재귀함수
- 여태껏 곱했던 것들을 넘겨주는 -> 업데이트된 상태를 외부생성자로 넘겨 받아 공유됨(
어그리게이션
)
- 여태껏 곱했던 것들을 넘겨주는 -> 업데이트된 상태를 외부생성자로 넘겨 받아 공유됨(
-
합성객체에 아무것도 공유 안하는 경우 :
포워딩
이라 부른다.- 공유도 안하는데 뭔가를 시킨다?
위임(델리게이션)
이라고 부른다.
- 공유도 안하는데 뭔가를 시킨다?
-
상태공유의 대표적인 예 ex> 꼬리무는 재귀함수
my) 위임: 전략객체에게 중요 상태값(컨텍스트)이 아니라, 유틸함수에 필요한 재료 넘겨주는식만이루어진다.
plan
-
전략객체는 일을 위임받아서 하는데, result(결과)를 합쳐가고 있다. 하지만, result를 공유하진 않고, this도 안준다.
call이라는 상태
만 넘겨준다.-
하지만, 이
call
이라는 것은, plan이연산이나 업무처리시 공유되는 context는 아니다.
값으로서
참조데이터만 넘어간 것뿐이지, 공유되는 컨텍스트 없이유틸함수처럼 전략객체를 호출
하고 있다.
-
하지만, 이
연결되는 합성객체
데코레이터 패턴이라고도 부른다.
- 하나의 객체 -> 인스턴스화 될 때, 자기만의 메모리를 가진다.
- 인스턴스의 객체 속 필드만큼 메모리를 가진다.
- 필드는 얼마나 가질 수 있을까?
- 클래스 정의하기 나름이다
- 팔드를 컬렉션으로 정의했다면?
배열<Object>
라면, 하나의 인스턴스는 메모리는 무한인가?
- 많은 사람들이, 컨설팅시 객체를 하나의 작은 컴퓨터라 부른다.
- 우리가 남발하는 인스턴스 1개가, 옛날컴퓨터보다 용량(메모리)가 클 수 있다.
- 우리는 캡슐화와 추상화를 통해 문제를 해결하기 때문에, 컴퓨터 1대가 문제를 해결해주는 식으로 생각한다.
- 그렇다면, 객체 2개 -> 컴퓨터2대(서버2대)를 가지고 협력하면서 문제를 해결하려면?
- 독립적으로 부르면 상관없지만, 협력해서 한다면?
-
완전히 독립된 메모리공간에 있는 2 객체(컴퓨터2대)의 어떻게 협력할지, 협력관계를 정의해줘야한다.
- 나는 너의 뭘 쓸거야,
- 너는 어떤 경로로 나에게 리턴해줘. 나는 그것을 가지고 또 뭘 쓸거야
-
연결된 합성객체 -> 독립되어있는 모듈끼리지만 병합된 결과를 주고받기 위한 인터페이스를 정한다는, 행위+개념이다.
- 2개를 연결했다면 -> 또 다른 것과 2개씩 연결할 수 있다.
- LinkedList와 연결된 합성 객체or데코패턴or체인오브리스폰서빌리티or composite패턴or …으로서
- 하나의 context를 공유해서, 공유된 context를 계속진행시키는 개념을 계속 쓴다.
- 중급 개발자를 가르는 기준이 된다.
재귀함수와 객체의 연결 차이
- 일반적인 재귀함수의 구조
- 네모1개 = 함수
- 인자를 받아들이는 곳
- 리턴 포인트
- 2번함수가 3번함수에게 리턴한 인자로 호출 -> 3번 함수는 인자를 받아들여서 수행
- 네모1개 = 함수
-
객체지향에서는 객체 != 함수이므로 위와 같은 모습을 보인다.
- 하나의 객체속
특정메소드
에서 -> 다른 객체가 발생하는게 아니라다른 객체의 메소드
로 보낸다. -
리턴할 때도 특정메소드의 위치로 가서 보낸다.
- 그냥 인자/리턴으로 보내는게 아니라, 객체 속 메소드들끼리 보내고 받고 한다.
- 하나의 객체속
-
연결되는 합성객체는 재귀함수의 확장판이라 볼 수 있다.
- 재귀함수: 다 같은 함수들이라서, 부르는 함수의 포인터를 알고 있다. a,b,c,d 다른 함수여도 상관없다.
- 연결된 합성객체들:
- 같은타입 객체의 같은 메소드들 서로 불러도 된다.
- 다른타입 객체의 특정(다른) 메소드들 서로 불러도 된다 -> 우리가 배웠던 객체간 메세지
-
중요한 것은 시작객체의 메서드가 -> 다른 객체의 메서드를 정확하게 알고 있다는 것이다.
-
객체가 메소드를 통해서
메세지를 보내려면
최소한 지식은메세지 받을 객체 Type과 그 메소드
에 대해서는 알고 있어야한다.- 객체는 형으로 말해서..?
-
객체가 메소드를 통해서
-
가장 왼쪽의 클래스는 특이한 클래스(객체)다.
진입하는 entry point 클래스(진입점 클래스)
- 애초에 모양자체가 특이함.
- 왼쪽에서 오는 것을 받지도 않고, 오른쪽으로 주기만 함.
- 바깥쪽으로 return도 안함.
- java의 main함수처럼 진입점은 어디든 존재한다.
- 애초에 모양자체가 특이함.
-
진입점에서 -> 메세지를 주고 받기 시작하는데,
재귀함수처럼 연결된 객체들이 다 같은 Type의 객체임을 인식후 메세지를 주고 받는 경우
를 배운다.- 다 같은 Type으로 인식되어 메세지를 주고 받으면 뭐가 좋을까? 연결된 4개 객체에 대한 지식이 1개의 지식만 있으면 된다.
- 원래 4개 서로 다른 객체라면
객체Type과 해당메서드
를 최소한의 지식으로 알아야했으니 X4배의 지식이 생겼다.
- 원래 4개 서로 다른 객체라면
-
다 같은 형이니까
형끼리 통신시 -> 1개의 지식만 필요한 데코레이터 패턴
이다
- 다 같은 Type으로 인식되어 메세지를 주고 받으면 뭐가 좋을까? 연결된 4개 객체에 대한 지식이 1개의 지식만 있으면 된다.
-
데코레이터 패턴에서는
같은Type을 상속 or impl하고 있는 모든 객체들
(my)구현체들)이 통신에 참여하면- 이 객체는 뭔지 모를지라도,
데코레이터 인터페이스를 따른 객체(구현체)다 == 특정메서드를 특정할 수 있다 == 연속으로 대화할 수 있다
- 이 객체는 뭔지 모를지라도,
코드로 보는 데코레이터 패턴
Calculator
- 기존 좋은상속 -> 합성으로 바꾼 전략 인터페이스
- 데코레이터 패턴을 적용한 전략 인터페이스
부수적 책임(모) -> (아니면 도로서) 포괄적 책임으로 승격시켰다.
-
상속계층을 잃어버린 이상에 -> 부모역할을 하던놈이 더 많은 역할을 가져봤자 책임만 분산된다.
- 따라서, 적은 책임의 좋은상속자식 -> 그대로 책임만 합성한 전략패턴 ->
객체에 책임을 부을 수 있을만큼 전략객체에 부어넘겨주는 데코레이터패턴
- 따라서, 적은 책임의 좋은상속자식 -> 그대로 책임만 합성한 전략패턴 ->
-
call
1개에서Set<Call>
콜집합으로 책임이 늘어난 것이다. -
갯수에 따라 무게가 증가한다. 1개 알고 있는 것이랑
그 컬렉션, 여러개를 알고있는 것이랑 무게가 다르다. 책임이 늘어난 것
이다. -
aggregation용 context
(my)누적, 업데이트되는 값)의 전달이 추가되었다.-
기존 Plan -> 보니 결과값들을 모아주는 변수도 넘어간다.
-
책에서는 afterCalculator와 그냥 Calculator로 2개를 통해서 aggregator를 수집하도록 설명해놓았지만, 여기선 귀찮아서 합친 것이다.
- 메서드 2개 -> 메서드1개 인자2개로 바뀔 수 있다.
- 인자가 적은 함수가 좋은 이유 -> 인자가 많아지면, 거기에 aggregator가 개입될 가능성이 높아진 것
- 여러인자를 쓰는 것도 싫은데, 함수 2개를 쓰는게 더 싫은 이유:
순서가 지식이 된다. 메서드의 순서는 컴파일러에 안걸려서 에러를 못찾는다.
-
순서형 메서드들 분할 -> aggregator를 합쳐서 메서드1개로 만들어서 트랜잭션을 자꾸 만들려고 노력한다.
- 학습용 코드라서, 순서형 메서드로서 2개가 나온 것이시지, 컴파일에러에 잡히게 만드려면, 1개의 메서드에 인자 2개를 넣어서 처리해야한다.
-
-
Plan
기존의 plan과 바뀐 plan
- calulator가
1개의 call만 처리하는 부분적인 역할
을 가지고 있었으나
- 이제는 니가 하다하라고
전체 call
를 다 밀어넣고,누적을 시작하기도 전에, 누적 aggregator의 초기값
을 넘겨줘서모든 책임을 위임
받는다.- my) 넘겨받은
전략객체 속 특정 메서드
로 누적연산의 초기값 + 상태값 전체를 메서드로 넘겨주면, 책임이 많이 줄어든다. - 이제부터는, 전략객체를 구상한 구상클래스가 모든 책임을 진다.
- my) 넘겨받은
-
plan의 줄어든 책임을
-
call 컬렉션(상태값)을 관리한다.
- 관리한 상태값을 넘겨줘서 연산은 전략객체가 시행하지만, 넘겨주기전까지 관리는 plan이
- 전략객체.특정메서드()에 call컬렉션을 전달해주는 것
-
call 컬렉션(상태값)을 관리한다.
-
계산하는 모든 책임은, 이름 그대로 Calculator가 다 가져갔다.
- my) 연산 값 1개만 전메로 만들지말고 -> 상태값/누적연산의 초기값 등 모든 연산필요 값들을 파라미터로 넘겨받아 모든 로직을 전메로 넘긴다.
책임을 다 넘긴 후에는, 문제 발생시 2가지를 나눠서 볼 수 있다.(올인된 책임으로 잘개 쪼개기 SRP)
- 계산이 잘못이 잘못되면 Calculator를 보면 된다
-
계산기의 투입이 잘못됬거나 or 잘못된 계산기가 투입됬다면, Plan쪽을 보면 된다.
-
설계란, 큰 책임을 졸라게 쪼개되, 쪼갠 책임마다 각각이 올인되도록 만들어 SRP(단일책임원칙)만 준다.
- my) 전략패턴, 합성으로도 모든 책임을 넘길 수 있다.
-
전략당한 부모쪽 = 업데이트되는 calls를 상태값 관리 + 전략객체 Calculator를 관리
- 원래는 calls관리가 내 책임
- Calculator는
위임을 위해, 참조포인터로서 알고 있는 것
임.
- plan이 가지고 있던 책임을 쪼개서 전략객체 Calculator가 계산에 관련된 모든 책임을 먹었다.