Object) prev, next 데코객체 적용 예시(Plan5)
prev를 이용한 동적 구간처리 + next를 이용한 정적 순차기능추가
📜 제목으로 보기
- 시작특이점을 아는 (동일형)prev데코객체 for 0부터 시작하는 구간처리
- 0부터 시작하는 연속된 구간을 받아, [미리 쪼개놓은 구간별 데코 객체들이 돌아가며 처리]하여 누적하는 계산기
- SectionPriceRule#구간별 요금을 각자 구간만큼 처리해줄 시작특이점의 데코객체
- 구간처리 데코, 시작특이점을 아는 데코 객체는, 같은 형의 prev로 생성자로 받아 저장한다. 처리할 구간은 start는 없이 처음부터 ~ 끝to(처리구간전체)를 가지되, 내부에서 prev를 이용해 짜른다.
- 1개 데코객체의 구간처리는, 누적될 결과값을 반환하므로, prev가 null인 시작특이점 객체 검사(invariant) + 전체구간이 처리구간보다 짧은지 검사(precondition)검사 후 부적절시 0에 해당하는 결과값 NULL객체를 반환한다.
- 전체구간의 상한이 내 상한 안쪽인지 -> duration이 상한 vs 내 상한보다 큰지 -> to가 상한 을 확인한다. (시작이 구간 비교 == 상한 비교)
- 구간을 시간(초)로 바꿔서, 초당 가격을 곱하여 구간별 요금처리를 마무리한다.
- test# duration은 LocalDateTime 2개를 between으로 구한다.
- SectionPriceCalc#협력할 데코객체를 1개만 소유 + 다음타자를 생성하고 기존은 prev에 저장해서 누적 -> 재귀형식으로 다음 객체에 저장하면서 마지막 타자만 가지고 있는 클래스
- 구간처리(시작특이점) 데코객체를 소유하여 협력하는 계산클래스는 시작특이점 객체를 1개를 미리 소유하고 있다. (어차피 변경될 필드이라서 final의 생성자 초기화는 불가능하다. 컬렉션처럼 빈컬렉션(시작특이점객체) + add로 받기기능 형태)
- 시작특이점 데코객체를 이용하는 클래스는, 데코객체를 필드로 1개만 소유하되, setter받기기능 메서드 호출을 통해 내부에서 다음타자를 생성하고 필드에 재할당한다. 이 때, 다음 타자 생성에 필요한 prev제외 필드정보들을 넣어줘야한다.(다음타자 생성에 필요한 정보prev가 내부에 있으니, 그외 정보만 받아서, 내부에서 생성해야한다.)
- prev를 가지는 시작특이점 데코객체는, 다음 타자를 생성하여 후 1개 필드에 마지막 타자만 재할당해서 소유하면, 반복문을 통해, 마지막 타자 -> 저장된 prev -> 그것의 저장된 prev -> 시작특이점 객체까지 소환할 수 있다.
- 데코객체 다음타자의 개별구나 추가시 구간은 start없는 처음부터이므로 인자로는 Duraton.ofSeconds()등의 0부터 시작하는 구간을 준다.
- 마지막 타자(rule필드)부터 시작하는 순차적 데코객체 loop실행기는, 현재 타자를 지역변수로 뺀 뒤, 처리 후 prev로 업데이트 해 나가되, do-while문을 사용해서 마지막타자부터 시작특이점 객체까지 개별처리를 누적한다.
- 구간처리에서 중간구간을 계산하고싶다면, [0~중간구간 끝] - [0~중간구간 시작] 형태로 계산해야한다.
- my) 중간에 구간추가하는 로직 짜보기
- SectionPriceRule#구간별 요금을 각자 구간만큼 처리해줄 시작특이점의 데코객체
- 0부터 시작하는 연속된 구간을 받아, [미리 쪼개놓은 구간별 데코 객체들이 돌아가며 처리]하여 누적하는 계산기
- 시작을 모르지만, 끝이 정해진 (템플릿추상클래스) 구상 next데코객체 for 정해진 기능 추가
- 동적 기능추가를 next로 연결한 뒤, 꼬리재귀호출로 누적 적용하는 방법
- 서로 다른 기능을 가졌지만, 같은 형으로서 next에 받기 위해서는, 추상체아래 구상체로서 기능을 수행해야한다. 근데, next라는 공통필드를 가지므로, 추상클래스여야한다.
- 구상체들은 추상체의 next필드와 그 것을 받는 생성자를 공통으로 필요로 한다. -> 하지만, 좋은 부모는 생성자를 쓰지 않으므로, [생성자 대신, not final 필드 + 체이닝setter로 변경후 public 템플릿메소드]로 올린다.
- 구상 next데코객체의 소유해서 사용하는 class는 외부에서 setter체이닝(by null)이 끝나 완성된, 시작 next데코객체만 받는다.
- next데코객체 소유 클래스에서, 계산책임위임시 - 추상체에 public템플릿 공통메서드이자 모든 구상체들이 재귀호추할 [꼬리재귀함수]로서, 누적결과값변수를 포함해야하며, 초기값을 인자로 넣어서 호출되어야한다.
- 데코객체소유 클래스에서, 데코객체 실행기메서드내부에서, [훅메서드로 개별계산] 후 [업데이트된 누적결과값 변수를 null터미네이팅 + 꼬리재귀]로 호출하기
- 각각의 훅구상체들의, 개별계산로직을 넣어준다. 개별구상체들 계산에서 필요한 정보가 있다면, 자식수준에서 생성자에서 주입받도록 정의하면 된다.
- 중요! 오류수정: 꼬리재귀하는 구상next데코 객체들의 계산내부에 이미 기존 누적값 -> 여기에 추가하는 누적의 성질이 존재하기 때문에, 구상next데코객체를 소유하는, 추상클래스의 재귀호출에서는 [누적계산완료된 결과값변수를 재할당 업데이트만] 해주면 된다.
- 구상next데코객체 자체 꼬리재귀 대신 객체 소유 클래스의 반복문으로 호출하도록 변경
- 값의 n -> n-1의 메서드재귀를 메서드루프로 바꾸는것 과 달리, 데코객체의 꼬리재귀(자신class의 method내에서 다음객체를 바로 호출 + 데코객체의 메서드에 처음부터 누적결과변수 끝까지 달고 다님) -> 데코객체의 반복문(외부에서 소유class가 데코객체를 다음타자로 업데이트 + 각 객체는 누적없이 자신의 연산만 처리함)으로 변경하는 것은 더 복잡하다.
- 구상next데코객체 소유 클래스(반복문이 생기는 곳)부터 수정
- 01 누적결과값을 가지고 다녀 매번 누적후 종착역(특이점) 도달시 한번에 누적결과값을 반환하는 로직을, 반복문위의 누적변수에 누적하기 위해 자기것만 계산하는 로직으로 바꿔야하며 객체는 자신의 계산만 반환후 객체 바깥으로 반환되어 누적되도록해야하므로, –> 데코객체 소유 클래스에서 반복문을 형성하게 된다.
- 02 꼬리재귀 정의 파라미터 -> 다음타자 꼬리재귀 호출의 인자를 보고 업데이트 로직을 가져와 반영한다. + for문이 아니므로, 다음타자로 넘어가는 업데이트 로직도 반영한다. + 더이상 데코객체가 누적을 안할 것이기 때문에, 반복문에서 누적해야한다 + 값객체라서 누적도 재할당으로 반영해야한다.
- 03 구상next데코객체의 반복문 while의 종착역은 특이점객체(연산x, 필드만null)가 아니라, 자신의 연산까지 끝낸 상태이므로, 다음타자를 불렀을 때 null이면 종료한다.
- 04 누적된 결과값을 반환한다(필요시 postcondition검사)
- 구상next데코객체가 더이상 누적연산이 아니라, 자신의 차례에 자신의 연산만 하도록 변경
- 01 꼬리재귀의 흔적인 return 속 종착역 + 다음타자 부르는 처리를 삭제하고, 자신의 처리(훅메서드)만 하도록 수정한다.
- 02 이제 [for문에서 누적 + 개별next데코객체는 자기값만 반환] vs [for문은 업데이트만 + 개별next데코객체가 기존값을 받아 유지하면서 누적]하는 방법 중 1가지를 선택한다.
- 03 구상next데코객체는 넣어준 순서대로 next로 나아가긴 하나, 생성하는 순간, 현재 존재하니 않는 nxt다음타자도 생성자에서 실시간 생성해야하며, 끝 null까지 다 한번에 넣어줘야한다. 즉, 동적 기능 추가가 어렵다. (고전적인 데코레이터도, base기능을 생성자에 넣는, prev방식으로 바깥에서 직전타자를 감싸서 넣어줬다.)
- prev vs next 비교
- next 대신 컬렉션으로 연결 + 전략패턴으로 변경해서 사용하기
- 동적 기능추가를 next로 연결한 뒤, 꼬리재귀호출로 누적 적용하는 방법
시작특이점을 아는 (동일형)prev데코객체 for 0부터 시작하는 구간처리
0부터 시작하는 연속된 구간을 받아, [미리 쪼개놓은 구간별 데코 객체들이 돌아가며 처리]하여 누적하는 계산기
-
10 ~ 12 / 12 ~ 14의 구간을 쪼개놓았는데, 11~13을 11~12 /12 ~13을 계산하는 방식이 아니다
- 0 ~ 14까지, 0~5, ~10, ~12, ~14 를 쪼개놓았더라도
- 11~13을 계산하는 것이 아니라, 0~2로 계산된다.
- 즉, 중간만 계산못하고, 처음부터 구간만큼만 계산되는데
- 중간구간을 계산하고 싶으면,
0~13
-0~11
로 하면 된다.
-
SectionPriceCalc
-
SectionPriceRule
: prev로 연결할 데코레이터 객체-
SectionPriceRule#calculateFee(duration)
: 데코레이터 실행기
-
-
SectionPriceCalc#calculateFee(duration)
: 처리할 구간을 외부에서 받아와 구간별 계산해줄 메서드
-
SectionPriceRule#구간별 요금을 각자 구간만큼 처리해줄 시작특이점의 데코객체
구간처리 데코, 시작특이점을 아는 데코 객체는, 같은 형의 prev로 생성자로 받아 저장한다. 처리할 구간은 start는 없이 처음부터 ~ 끝to(처리구간전체)를 가지되, 내부에서 prev를 이용해 짜른다.
-
구간별 요금 처리를, 구간별로 담당해줄 데코레이터 객체(-Rule)를 만든다. 각 구간처리 데코 객체들은
-
자신이 처리할 구간(시간: Duration)의 (처음부터~)
to
를 받으며 ,중간에 짤린 시작from은 안받는다.- 소유할 prev객체로부터 prev의 to를 이용해서 짜를 것이다.
- 자신의 구간에서 계산에 필요한 정보(돈)
Money
을 받는다. -
데코 객체 특이성으로서 자신의형의
prev
(혹은 next)를 받아서 태어난다.-
구간은 제일 처음( 구간0 )의 특이점 객체를 알기 때문에, 다음 타자는 prev를 받는다.
- 특이점객체는 연산을 하지 않는 꼬리재귀 종착역으로서, 그 타이밍에 현재까지 누적계산된 값을 반환한다.
- 구간처리 데코 or 시작특이점을 아는 데코객체는 prev를 인자로 받아 생성된다.
-
구간은 제일 처음( 구간0 )의 특이점 객체를 알기 때문에, 다음 타자는 prev를 받는다.
- 내가 처리할 구간은, 처음부터 시작되는 것 같지만, 변수명을
to
로 하고 내부의prev
의 상한to
를 이용해서 짤라서 처리할 예정이다.
-
자신이 처리할 구간(시간: Duration)의 (처음부터~)
1개 데코객체의 구간처리는, 누적될 결과값을 반환하므로, prev가 null인 시작특이점 객체 검사(invariant) + 전체구간이 처리구간보다 짧은지 검사(precondition)검사 후 부적절시 0에 해당하는 결과값 NULL객체를 반환한다.
-
구간처리 데코객체는, 전체구간을 받되, 각 데코객체별로 처리할 수 있는 구간을 짤라서 해당 부분만 계산해준다.
-
처음부터
to의 끝
만 정해진 처리구간을 가지고 있지만 내부에서는, 소유한prev의 상한 ~ 내 상한
큼만 계산해서 반환해준다.
-
처음부터
-
구간처리 데코객체는, 내부에서
필수 invariant검사 및 precondition(전체구간) 검증
을 해야한다.- 구간처리 데코객체의 1구간 처리는 전체 데코객체들을 돌고 있는 외부에 누적될 결과값을 반환할 것이다.
- invariant: prev는 시작특이점(연산X)가 아니여야한다. -> 맞다면, 연산하지말고, 누적될 결과값을 NULL객체를 반환한다.
- precondition: 들어오는 전체구간은, 내 하한인 prev의 상한보다 더 길어야한다. 같거나 더 짧으면, 내가 처리할이유가 없어서 누적될 결과값을 NULL객체를 반환한다.
전체구간의 상한이 내 상한 안쪽인지 -> duration이 상한 vs 내 상한보다 큰지 -> to가 상한 을 확인한다. (시작이 구간 비교 == 상한 비교)
-
시작이 0에서 시작하는 것으로 같으므로,
구간의 비교 == 상한 비교
가 된다.- duration > to : 내가 처리할 수 있는 끝인 to가 상한
- duration <= to: duration이 상한
-
상한이 결정되면, prev의 상한인 prev.to를 하한으로 하여
처리할 구간 결정
한다.
구간을 시간(초)로 바꿔서, 초당 가격을 곱하여 구간별 요금처리를 마무리한다.
test# duration은 LocalDateTime 2개를 between으로 구한다.
SectionPriceCalc#협력할 데코객체를 1개만 소유 + 다음타자를 생성하고 기존은 prev에 저장해서 누적 -> 재귀형식으로 다음 객체에 저장하면서 마지막 타자만 가지고 있는 클래스
구간처리(시작특이점) 데코객체를 소유하여 협력하는 계산클래스는 시작특이점 객체를 1개를 미리 소유하고 있다. (어차피 변경될 필드이라서 final의 생성자 초기화는 불가능하다. 컬렉션처럼 빈컬렉션(시작특이점객체) + add로 받기기능 형태)
-
생성자로 안받고, 내부에서 미리 필드 초기화해서 가지고 있다.
- 생성자 type이 아니라 필드 type으로 초기화한다. 어차피 다음 타자 데코객체를 재할당해서 알고 있을 예정이므로
-
시작특이점 객체로 초기화할 땐, 그것의 NULL객체들로 채워준다.
prev는 null
이어야한다.
시작특이점 데코객체를 이용하는 클래스는, 데코객체를 필드로 1개만 소유하되, setter받기기능 메서드 호출을 통해 내부에서 다음타자를 생성하고 필드에 재할당한다. 이 때, 다음 타자 생성에 필요한 prev제외 필드정보들을 넣어줘야한다.(다음타자 생성에 필요한 정보prev가 내부에 있으니, 그외 정보만 받아서, 내부에서 생성해야한다.)
-
다음 타자는, prev로서 rule필드값이 필요하므로,
그외 정보만 받아와서 내부에서 생성
한다. -
다음타자 정보들은, 내부정보를 이용하여 preconditino검증을 해야한다.
- to(다음타자의 구간(상한)) : 예비 prev구간인
rule.getTo()
보다 커야한다. - price : 계산 결과값을 0으로 만드는 NULL객체가 아니어야한다.
- to(다음타자의 구간(상한)) : 예비 prev구간인
prev를 가지는 시작특이점 데코객체는, 다음 타자를 생성하여 후 1개 필드에 마지막 타자만 재할당해서 소유하면, 반복문을 통해, 마지막 타자 -> 저장된 prev -> 그것의 저장된 prev -> 시작특이점 객체까지 소환할 수 있다.
-
다음타자를 생성하고, 그 전 타자(현재 rule 필드)는, 다음타자 prev에 저장되게 한다.
-
마지막 타자만 rule필드에 소유하고 있으면 된다. 그 전에 것들은 다음 타자의 prev에 연쇄적으로 저장되어있다.
데코객체 다음타자의 개별구나 추가시 구간은 start없는 처음부터이므로 인자로는 Duraton.ofSeconds()등의 0부터 시작하는 구간을 준다.
-
사용 : 아래는 잘못되었다, 입력은 start가 없이 end로서
처음부터 상한까지의 개별구간
을 입력해줘야한다.
마지막 타자(rule필드)부터 시작하는 순차적 데코객체 loop실행기는, 현재 타자를 지역변수로 뺀 뒤, 처리 후 prev로 업데이트 해 나가되, do-while문을 사용해서 마지막타자부터 시작특이점 객체까지 개별처리를 누적한다.
-
외부에서 전체 구간을 주고, 누적될 결과값을 초기값을 준다고 가정한다.
데코객체는 반복문 속에서 prev로 업데이트될 예정이므로, 누적 결과값 처럼, 반복문 위에서 지역변수로 빼서 재할당하며 업데이트 되어야한다.
현재 target 데코객체는, prev를 꺼내서 재할당 업데이트 한다. 그 prev(target)의 prev가 null이면, 시작특이점 객체라서, 연산없이 종료된다. (재귀, 데코객체 실행기 등의 종착역은 계산 안하는 곳이다.)
주의) 쪼개놓은 구간의 데코객체들은, 자기구간이 아니면 default값을 반환하니, postcondition에서 zero를 검사하면 안된다.
구간처리에서 중간구간을 계산하고싶다면, [0~중간구간 끝] - [0~중간구간 시작] 형태로 계산해야한다.
my) 중간에 구간추가하는 로직 짜보기
시작을 모르지만, 끝이 정해진 (템플릿추상클래스) 구상 next데코객체 for 정해진 기능 추가
동적 기능추가를 next로 연결한 뒤, 꼬리재귀호출로 누적 적용하는 방법
서로 다른 기능을 가졌지만, 같은 형으로서 next에 받기 위해서는, 추상체아래 구상체로서 기능을 수행해야한다. 근데, next라는 공통필드를 가지므로, 추상클래스여야한다.
일단 next를 첫번째 인자로 받고 생성자체이닝하는 구상 next데코객체들을 만들자.
-
일단 next필드를 1번째 인자로 가질 첫번째 구상체 데코객체를 만든다.
-
각 구상체들은 여러번 누적 적용시킬 것이므로,
직전 next데코객체의 계산 결과값
을 인자로 가져야한다.- 첫번째 인자로 줘야, 생성자 체이닝이 매끄럽게 나온다.
- 괄호에서부터 줄바꿈한다.
- 그외
자신만의 계산에 필요한 정보
가 필요하나,메서드에서 실제 계산로직 수행시, 생성자에서 받도록 추가 예정
이다.- 결과값은 메서드로 호출되어 메서드로 반환하니, 누적결과값변수는 여기서 안받는다.
-
마지막 next데코객체라면,
연산을 하는 끝 특이점 객체
로서 next로 null을 가진다.시작특이점 객체
는 구간을 처리하고, 그 구간이 0으로서, 연산이 없다.끝특이점 객체
는 추가기능의 마지막으로서, 마지막인 자신의 연산까지 하고 결과값을 반환해야한다.
-
각 구상체들은 여러번 누적 적용시킬 것이므로,
-
생성자 첫번째 인자의 파라미터를
next필드를 공통으로 가질 추상클래스
로 주고 추상체를 만들고 상속한뒤,next
필드에 받아준다.
구상체들은 추상체의 next필드와 그 것을 받는 생성자를 공통으로 필요로 한다. -> 하지만, 좋은 부모는 생성자를 쓰지 않으므로, [생성자 대신, not final 필드 + 체이닝setter로 변경후 public 템플릿메소드]로 올린다.
공통필드(next)를 생성자에서 받던 자식들은, not final 필드 + 체이닝setter 조합으로 만들어서 public템플릿메서드로 올린다.
- 생성자의 결과는 자기자신이다.
- setter후에도 자기자신이 나오려면, return this의 체이닝setter로 변경해줘야한다.
-
추상클래스의 구상체들은,
공통 필드에 대해서만
, 해당필드를 애초에 생성자에서 받지말고필드 + setter
조합으로 올리자!!! -
체이닝 setter를 만드는 과정에서, 자동완성하면 ->
return this
대신, 파라미터가 나아가버리니, 수정해줘야한다. -
setter를 pull members up으로 올리면, not final 필드도 같이 올릴 수 있다.
-
추상체로 메서드 등을 올리고 난 뒤에는,
좋은 부모의 조건
을 확인해서 수정해준다. -
나머지 구상 next데코객체에서도 공통로직은 삭제한다
구상 next데코객체의 소유해서 사용하는 class는 외부에서 setter체이닝(by null)이 끝나 완성된, 시작 next데코객체만 받는다.
-
prev데코객체처럼, 해당 데코객체 소유 클래스 생성 후엔 동적으로 기능 추가는 못한다.
- 외부에서 체이닝을 통해
동적기능추가가 완료된 시작 next데코객체
만 받는다.
- 외부에서 체이닝을 통해
next데코객체 소유 클래스에서, 계산책임위임시 - 추상체에 public템플릿 공통메서드이자 모든 구상체들이 재귀호추할 [꼬리재귀함수]로서, 누적결과값변수를 포함해야하며, 초기값을 인자로 넣어서 호출되어야한다.
- 올리는 메서드가, 단순 public 템플릿메서드가 아니라,
꼬리재귀로 호출될 public 템플릿메소드
이다. - 계산에 필요한 정보 + 누적결과값 변수를 파라미터로 가질 것이며 ->
최초호출시 누적결과값 초기값
을 넣어 호출한다.
next데코객체의 실행기 메서드의 2가지 선택지 -> 꼬리재귀 or 반복문
-
둘다 돌아가면서 계산값을 누적결과값 변수를 업데이트하는 것은 동일하다
- 꼬리재귀 -> 누적결과값을 파라미터로 가지고 다니며, 누적 전 초기값을 인자로 주고 호출 시작한다.
- return문에서 다음 next데코객체를 호출하며, 삼항연산자로 재귀문 첫줄처럼 nul로 터미네이팅한다.
객체의 꼬리재귀는 객체내부에서 next필드의 메소드를 호출함으로써 다음타자를 부른다.
- 반복문 -> 누적결과값을 반복문내에서 update하며, 반복문 위에 update되는 결과값의 초기값을 지역변수로 선언한다.
- while문에서 null로 시작특이점 or 끝특이점 객체를 찾는다.
객체의 재귀 데코객체를 반복문으로 만드려면, 데코객체 소유 클래스에서 돌려야한다.
- 꼬리재귀 -> 누적결과값을 파라미터로 가지고 다니며, 누적 전 초기값을 인자로 주고 호출 시작한다.
데코객체소유 클래스에서, 데코객체 실행기메서드내부에서, [훅메서드로 개별계산] 후 [업데이트된 누적결과값 변수를 null터미네이팅 + 꼬리재귀]로 호출하기
next데코객체들의 추상클래스
는, 현재 구상체 상태이며, 재귀호출로 다음 것으로 넘어온 상태임
을 인지해야한다. 그래야, “나는 마지막 타자까지 넘어왔다고 생각하여 -> next필드에 null이 차있으면 터미네이팅”도 쉽게 생각이 든다.
꼬리재귀를 호출할 - 개별 데코객체들의 계산은 내수용형태의 훅메서드로, 누적 결과값 변수를 인자로 받았다가 -> 반환받아 업데이트 한다.
꼬리재귀 필수조건은 return문내 update된 누적결과값변수를 포함한 인자들로 다음재귀호출이며, 현재객체의 연산까지 마치고 누적결과값을 반환하고 싶다면, 연산후 return문내에서 삼항연산자로 null로 터미네이팅(종착역) 처리를 한다.
-
꼬리재귀 필수족거return내에서 [삼항연산자]로 null터미네이팅(지금까지 누적결과값 변수를 반환) + next필드에 저장된 다음 next데코객체로 [업데이트된 누적결과값을 받아 꼬리재귀]를 호출한다
각각의 훅구상체들의, 개별계산로직을 넣어준다. 개별구상체들 계산에서 필요한 정보가 있다면, 자식수준에서 생성자에서 주입받도록 정의하면 된다.
중요! 오류수정: 꼬리재귀하는 구상next데코 객체들의 계산내부에 이미 기존 누적값 -> 여기에 추가하는 누적의 성질이 존재하기 때문에, 구상next데코객체를 소유하는, 추상클래스의 재귀호출에서는 [누적계산완료된 결과값변수를 재할당 업데이트만] 해주면 된다.
구상next데코객체 자체 꼬리재귀 대신 객체 소유 클래스의 반복문으로 호출하도록 변경
값의 n -> n-1의 메서드재귀를 메서드루프로 바꾸는것 과 달리, 데코객체의 꼬리재귀(자신class의 method내에서 다음객체를 바로 호출 + 데코객체의 메서드에 처음부터 누적결과변수 끝까지 달고 다님) -> 데코객체의 반복문(외부에서 소유class가 데코객체를 다음타자로 업데이트 + 각 객체는 누적없이 자신의 연산만 처리함)으로 변경하는 것은 더 복잡하다.
구상next데코객체 소유 클래스(반복문이 생기는 곳)부터 수정
객체 바깥으로 반환되어 누적되도록
해야하므로, –> 데코객체 소유 클래스에서 반복문을 형성
하게 된다.
01 누적결과값을 가지고 다녀 매번 누적후 종착역(특이점) 도달시 한번에 누적결과값을 반환하는 로직을, 반복문위의 누적변수에 누적하기 위해 자기것만 계산하는 로직으로 바꿔야하며 객체는 자신의 계산만 반환후 -
기존
- 데코객체 소유 클래스
- 구상 next데코객체의 추상체
-
그러려면, 데코객체 소유class(Calculator)는 데코객체에게
(반복문이라면 초기값으로 줬던) 꼬리재귀 최초 호출 인자
의 계산위임 ->인자에 있던 누적 초기값을 변수로 선언하고, 반복문내에서 next데코객체들도 직접 업데이트해주기 위해 업데이트변수로 초기값을 선언
한다.- 데코객체들의 종특(다음타자를 필드로 저장)으로서 자신은 연산후, 자신의 정보를 통해 다음타자를 확인한다면, do-while문을 쓴다.
02 꼬리재귀 정의 파라미터 -> 다음타자 꼬리재귀 호출의 인자를 보고 업데이트 로직을 가져와 반영한다. + for문이 아니므로, 다음타자로 넘어가는 업데이트 로직도 반영한다. + 더이상 데코객체가 누적을 안할 것이기 때문에, 반복문에서 누적해야한다 + 값객체라서 누적도 재할당으로 반영해야한다.
- 오타: 아래 보라색 필드의 calculator가 아니라 업데이트될 직전 targetCalculator로 수정되어야한다.
03 구상next데코객체의 반복문 while의 종착역은 특이점객체(연산x, 필드만null)가 아니라, 자신의 연산까지 끝낸 상태이므로, 다음타자를 불렀을 때 null이면 종료한다.
-
prev는 종착역이 계산을 안하지만, 필드만 null인 객체라서, 업데이트된 다음타자의 필드를 ull이면 끝내지만
-
prev는
종착역
에는 시작특이점 객체가 존재하며,필드로 null
을 가지기 때문에,다음타자의 필드 null을 검사
한다.- 왜냐면, 시작을 null객체로 시작할 수 없기 때문에
구간0의 필드가null
인 객체를 따로 잡았다.
- 왜냐면, 시작을 null객체로 시작할 수 없기 때문에
-
-
next는 특이점객체가 없으며, 업데이트된 다음타자가 null이면 종료다.
- next는
종착역
이 끝 특이점 객체 없이, 마지막까지 연산하고, 다음타자 자체가 null객체이므로다음타자가 null인지
를 검사한다
- next는
04 누적된 결과값을 반환한다(필요시 postcondition검사)
구상next데코객체가 더이상 누적연산이 아니라, 자신의 차례에 자신의 연산만 하도록 변경
-
이미 밖에서 누적시키고 있으니, 구상next데코객체는 자신의 연산만 해서 반환해주면 된다.
- 이 때, 직전까지의 누적값이 필요하면 인자로 받지만, 꼬리 재귀호출은 하지 않는다.
01 꼬리재귀의 흔적인 return 속 종착역 + 다음타자 부르는 처리를 삭제하고, 자신의 처리(훅메서드)만 하도록 수정한다.
02 이제 [for문에서 누적 + 개별next데코객체는 자기값만 반환] vs [for문은 업데이트만 + 개별next데코객체가 기존값을 받아 유지하면서 누적]하는 방법 중 1가지를 선택한다.
-
현재는
현재까지의 계산결과
에누적하며 계산
하는 로직이기 때문에for문이 개별next데코객체가 누적완료한 값으로, 누적 결과값 변수를 업데이트만
하는 것으로 유지하자.-
next데코는, 직전까지의
계산후 업데이트된 결과변수
를 인자로 받아 사용해야하므로 현재까지 누적된 결과를 가져오니, for문은 업데이트만 하자. - prev데코는, 구간처리에 쓰였는데, 계산메서드가 누적결과값을 변수로 받지않고, 자기 일만 하므로, 바깥의 for문에서는 누적을 해줬어야했다.
-
next데코는, 직전까지의
03 구상next데코객체는 넣어준 순서대로 next로 나아가긴 하나, 생성하는 순간, 현재 존재하니 않는 nxt다음타자도 생성자에서 실시간 생성해야하며, 끝 null까지 다 한번에 넣어줘야한다. 즉, 동적 기능 추가가 어렵다. (고전적인 데코레이터도, base기능을 생성자에 넣는, prev방식으로 바깥에서 직전타자를 감싸서 넣어줬다.)
-
고전적인 데코레이터 패턴도 prev데코객체 형식으로서, 다음타자자 생성시, 직전객체를 넣어주고, 마지막 타자로 업데이트하여, -> 마지막 타자에
다음타자를 동적으로 추가가 가능
하다- 실제로는 다양한 기능을 가진
구상 prev데코객체
로 표현하며의 -
prev필드를 추상클래스가 가지고서, 각 구상체들은
부모가 정의해놓은 prev의 기능 호출
+현재 구상체의 현재기능추가
로 메서드를 오버라이딩해서 호출한다- 우리는 구간처리에 있어서는 prev데코객체가 정보만 같은 형으로 구성되었었음.
- 실제로는 다양한 기능을 가진