OBJECT 10 객체설계4_개발자의세계1_LSP위반(코드스핏츠)
object 책을 강의한 코드스핏츠 유튜브 요약
📜 제목으로 보기
- ch10. 개발자의 세계
- 개발자의 세계
- Director
- LSP위반 시행착오
- 1. LSP위반 지점- 일반적으로 정보없는 추상층Paper에 의해 시작: FrontEnd다운캐스팅>
- 2. BackEnd와 같이볼 때 DRY위반으로 추상층에 공통로직올린: Programmer
- 3. 추상층에겐 말고 로 떠넘긴: Paper나에게>물어보고>
- 4. 시 모든 대상에게 하기 위해 을 줬더니, 정의시 전과하여 발생: Client시켜서>추상층>시켜서>
- 5. 더 심각한 문제: ServerClient
- 6. 외부 객체 메세지를 받는 이상, if 분기는 외부context관점의 case들까지 다 처리해야하는데: Client
- 7. 시행착오 교훈
- 참고 유튜브 : https://www.youtube.com/watch?v=navJTjZlUGk
- 정리본: https://github.com/LenKIM/object-book
- 코드: https://github.com/eternity-oop/object
- 책(목차) : https://wikibook.co.kr/object/
ch10. 개발자의 세계
개발자의 세계
- 개발자는 IT회사내에서도 극히 희귀한 존재다.
- 개발자를 도구로 바라본다. (생각을 주면 프로그램으로 바꿔줄거야)
Director
디렉터가 하는 일을 추상레이어로
- 악의 축 Director: 한국에선 잘 안쓰는 용어. 일본Director 미국, 한국PM
-
디렉터가 하는 일:
-
Paper: 어디선가 지령서(기획서, Paper)를 받는다. Paper안의 사양서는 2가지 범주(Class)가 있고, 범주에 해당하는 프로젝트들을 수행할 것이다.
- ServerClient : 1건, 서버+클라이언트 다 만든다.
- Client: 2건, 클라이언트만 만든다.
-
Programmer: Director는 기획서 안의 사양서(1건, 2건)을 다 살펴본 뒤, 개발자들을 섭외해온다.
- Programmer는 Paper(의 사양을 알아야 개발)를 기준으로 개발한다.(아는 관계 성립)
- 디렉터는 Paper와 Programmer를 잘 연결해서 프로젝트를 수행하는 것이다.
- Programmer는
- FrontEnd
- BackEnd로 나뉜다.
- Programmer는 Paper(의 사양을 알아야 개발)를 기준으로 개발한다.(아는 관계 성립)
-
-
Director
가 바라보는 추상레이어는Paper
와Programmer
가 될 것이고-
Paper
와Programmer
가 각각 바라보는 추상레이어는-
ServerClient
/Client
와FrontEnd
/BackEnd
가 될 것이다.
-
-
Paper
-
Paper
는형
으로서 아무것도 메소드가 없는 인터페이스로서의 형이다.- 책에서는 인터페이스에 정의된 시그니쳐(추상메소드)를
오퍼레이터
라 부름- 그 오퍼레이션이 구상클래스에서 구현되는 것을
메소드
라 부름 - 그렇다면, 인터페이스는 메소드를 가지는게 아니라 오퍼레이터를 가지는 것
- 그 오퍼레이션이 구상클래스에서 구현되는 것을
- 책에서는 인터페이스에 정의된 시그니쳐(추상메소드)를
Programmer
-
Programmer
는 외부에프로그램을 만드는 인터페이스
를 제공한다-
my) 인터페이스 속 오퍼레이터, 메소드는 외부에 인터페이스를 제공하는 역할을 한다.
-
팀장(PM, Director)이
"()(Paper paper)기획서를 던져주면서) 프로그램 만들어!"
라고 명령할 수 있게끔-
프로그래머들은…
팀장에게 기능을 제공
해주는 책임을 가지므로 메소드makeProgram()
을 제공한다.- 이 때, 팀장이 기획서를 던져주면서 만들라고 하니, 인자를 던질 수 있게 기능 제공
- my) 인터페이스든, 클래스든, 외부에서
나를 쓰는 놈
에게기능을 제공하기 위해 메소드를 정의
한다.
-
프로그래머들은…
-
Client
- Paper라는 추상클래스를 기반으로 구상클래스
Client
를 구현해보자.- my) Paper나 Programmer는 각각의 공통점을 뽑아놓 상위개념 -> 추상층이다.
- my)
실존하는 것들은 범주별로 impl해서 구현
하고, 그공통점을 가진 윗 개념은 추상클래스or인터페이스
로 만들자.
- my)
- my) Paper나 Programmer는 각각의 공통점을 뽑아놓 상위개념 -> 추상층이다.
- Paper는 메소드가 없는
마커 인터페이스
다.- Client용 프로젝트를 만들고 싶어서 Class로 구현한다.
-
client의
사양서
에 들어있어야하는 내용들을필드와 메소드
로 채운다.-
library
와language
를 지정해주고 - 프로젝트 담당할
programmer
를 할당받을 변수도 미리 만들어놓자.- 개발자는 프로젝트 사양서 나오고, 나중에 할당되니, 초기화 없이 변수만
- 추후 투입될 programmer를 박아줄
특정필드setter인 setProgrammer(프로그래머객체)
도 만들어준다.
-
- 나중에 수십개의 필드가 추가 되어야할 것이다.
- 나중에 생성자에서 만들어도 되는데, 지금은 일단 대입해서 필드 초기화
ServerClient
-
마찬가지로 server + client를 다 개발할 수 있도록 하는 사양서의 내용을 채운다.
- 사용할
server
- 랭귀지 앞/뒤 2개
- 프로그래머 앞/뒤 2명
- 프로그래머 setter 2개
- 사용할
FrontEnd
- language와 library를 인식해서 자기만의 프로그램을 만든다.
- Programmer를 구현해서 -> makeProgram을 제공하는데,
- makeProgram내부에서 -> 자신만의 makeFrontEndProgram ( Program )을 만드는 기능을 제공한다.
- 던지는 paper를 받아들여서, paper속에 있는 language와 library를 세팅해준 다음 -> 자신만의 프로그램을 만든다.
-
이 때, Paper는
마커인터페이스
이므로paper로부터는 어떠한 정보를 얻어낼 수 없다
-
마커인터페이스 paper를 구현한 Client에서 정보를 얻기 위해
다운캐스팅(=OCP어김)
할 수 밖에 없는 상황이다.- 다른 방법이 생각이 안날 것이다…
- 대부분이 여기서 설계를 포기한다. 오늘의 핵심은 이 문제의 해결이다.
- 리스코프 치환 원칙을 어김
- OCP를 어김(부모자리에 자식이 들어갈 수 있다? 부모를 자식으로 바꿔야하는 상황에선.. 어기는 것이라고 함…)
- SOLID원칙이나 설계원칙들을 배운다고 해서 이 문제를 해결할 순 없다. 저자가 강조하듯
코드로서 익혀야햐한다
-
이 때, Paper는
- Programmer를 구현해서 -> makeProgram을 제공하는데,
-
마커인터페이스
paper
를 객체로 받을 때- paper 구현체인
Client
일 수도 있고,ServerClient
이 들어갈 수도 있다.- 바깥에서 Paper 구상체들이 늘어날때마다
if instanceof -> (다운캐스팅) 분기
가 늘어나도록 코드수정해야하는 문제점 발생할 것이다. - else의 처리가 지금은 생략되어있지만, 필요하며.. 또 그 밑으로 추가될 수도 있다.
- 바깥에서 Paper 구상체들이 늘어날때마다
- paper 구현체인
- 그래도 paper -> 구현체속 정보가 세팅되어야 -> 자신만의 프로그램을 만들 수 있는 상황이다.
BackEnd
- 백엔드 개발자도, 사정이 똑같다.
- 마커인터페이스 -> 택1 구현체를 받아서 정보 받기 -> 확인용으로 다운캐스팅 해야하는 상황 + else 등 코드 추가 수정이 이루어질 예정임
-
그래도 paper -> 구현체속 정보가 세팅되어야 -> 자신만의 프로그램을 만들 수 있는 상황이다.
-
개발자의 세계 핵심문제: Paper받아서 구현체로부터 정보 빼내야하는 상황
- 리스코프 치환 원칙을 어김
- OCP를 어김 = 다운캐스팅 해야함
- 프로그램이 굳어서 paper의 자식class(구상체)를 겁나서 못만들게 됨.(코드 수정해야함)
- Programmer를 구현한 모든 세부개발자들의 코드마다 if instanceof 분기를 수정해줘야함
- 프로그램이 굳어서 paper의 자식class(구상체)를 겁나서 못만들게 됨.(코드 수정해야함)
- if를 제거해서 외부쪽 클라이언트쪽으로 옮기는게 좋다.
- 여기서 문제 발생 paper가 다운캐스팅을 시도하고 있다. 그러면 이 클래스는 확장에 막혀있게 된다.
이런 문제를 해결하기 위해서는 코드로서 문제를 해결하기 위한 방법을 모색해야 한다.
- if는 goto를 일으켜서 런타임, 컴파일에러를 일으키지 않아 그래서 if나 swich를 쓰면 정적타입의 안정성을 잃어버리게 된다.
Director
- 디렉터는
특정 프로젝트 이름(String)
으로특정 기획서 객체(Paper)들
을 소유하기 위해 hashmap에 소유하고 있다.- 빈 hashmap에
addProject
메소드로 더한다. (remove나.. edit 등은 일단X)
- 빈 hashmap에
- runProject()는 name으로 project의 paper를 꺼내는 데
- **Paper는 마커인터페이스로 -> 자식구현체
Class들
로 범주의여러 종류
가 정해져있으며 ** -
paper로 꺼냈다면 -> 구체적으로 어느 종류 사양서(client or serverclient)인지 모른다.
if instanceof 분기
로종류마다 하나씩 분기
를 만들어줘야하는 수고로움 -> 유지보수시 수정 많음.- 만약, 해당종류라고 분기가 되었으면
(해당종류로 다운캐스팅)해야
마커에는 없던 기능들을 쓸 수 있다.- ServerClient project = (ServerClient)paper;
- 만약, ServerClient 사양서를 가졌다면
- FE/BE 각각 프로그래머 변수를 만든 뒤 -> set으로 세팅해준다. (프리랜서 seller개념? 차후 세팅할려면 빈변수->setter)
- 각각의 개발자들한테, 자신만의 프로그램 만들라고 마커인터페이스를 다운캐스팅한 (ServerClient)project를 넘겨서 만들게 한다.
- 각각의 개발자들의 완성품이 Program으로 같다
- deploy해준다.
- **Paper는 마커인터페이스로 -> 자식구현체
- 만약, client프로그램이라면
-
(해당종류로 다운캐스팅)해야
마커에는 없던 기능들을 쓸 수 있다.- Client project = (Client)paper;
- 개발자 1명만 변수 만들고 -> setter로 세팅해주고 -> (자신만의X, 공통적인) 프로그램만들라고 시키고 -> deploy
-
Error
-
추상체인 paper를 받아도, 특정 구상체인 Client paper만 내부분기(if instanceof)로 받아 세팅하던
FrontEnd
개발자에게ServerClient
paper인 project를 넘겨봤자, language와 library를 세팅 못하기 때문에엄한 결과(context에러)
가 나온다.- Programmer중 FrontEnd 종류가 된 이상,
Client로 다운캐스팅된 paper만 받아 처리(lang, lib 세팅)
하도록 정의되어있다.-
FrontEnd
-
-
paper 종류에 따라 project가 가진 필드가 다르며,
FE/BE개발자는 Client가 아닌 ServerClient paper만 처리하도록 설계된 상태
다.-
Client
-
ServerClient
-
-
if를 썼기 때문에 -> 컴파일 에러가 안나고 내부에 문제가 숨어있게 된다.
- 모든 에러가 context에러로 빠져버렸다.
-
if -> OCP원칙 파괴(다운캐스팅)로 끝나지않고 대부분의 에러가 context에러로 숨어버린다.
- goto를 써서 runtime에 죽지도 않고, compile에 걸리지도 않는 context에러
- 여러 case로 사람이 따로 판별해볼수 밖에 없다.
- if -> 다운캐스팅들이, 바깥에서 context에러를 일으킴.
-
SOLID 원칙 중에서도 설계상에서는
LSP와 OCP
를 더 신경써야한다.- LSP와 OCP를 지키면, 나머지 3개도 다 지켜진다
- LSP는 지키기가 너무 힘들다.
- 심지어 Paper는 마커인터페이스로, 구상체들은 다 자기만의 메소드를 가질텐데, abc -> abcd 확장성LSP가 어렵다고 했지만, 여기서는 구상체들 각각이 0 -> d에 해당하는 경우이므로
- d의 문제를 해결하도록 습관적으로 연습하자
- Programmer중 FrontEnd 종류가 된 이상,
LSP위반 시행착오
1. LSP위반 지점- 일반적으로 정보없는 추상층Paper에 의해 시작: FrontEnd다운캐스팅>
- LSP를 위반하는 곳은
빨간네모
코드이다. 이것에 의해 다운캐스팅 할 수 밖에 없었다.-
LSP: 우리는 자식형을 <— 부모형이 대신해도 충분해
-
부모형이 충분하지 못해서
LSP위반
-> if ->다운캐스팅
->OCP 위반
- 부모가 자식에게만 있는 d가 없거나
- 부모가 아예 메소드가 비어있는 마커인터페이스 등 자식이 확장형일 때
- 왜 OCP위반(다운캐스팅)했냐? -> 항상 선행되는 LSP위반(부모가 자식을 대체못함)
-
부모형이 충분하지 못해서
- paper(부모)가 충분했으면 = LSP 만족시켰으면 -> 다운캐스팅 할 일이 없는데
- paper가 paper로서 충분치 않았다.(부모가 자식 대신하는 역할 = LSP 만족X) -> if instanceof -> 다운캐스팅 -> OCP 위반
-
LSP: 우리는 자식형을 <— 부모형이 대신해도 충분해
-
확장형LSP ->
d()의 문제
해결은, 6장에서 배운헐리우드 원칙 = 묻지말고 시켜라
이ㄷ다.-
"얘가 다운캐스팅 하는 이유는, 얘가 추상형(paper형)에 대해 구상 지식(client형의 지식)을 가지려 했기 때문이야"
- 복잡한 상황은 내가 아닌, 외부로 추상화를 떠밀어야하는데, 그 방법이
헐리우드 원칙
- 복잡한 상황은 내가 아닌, 외부로 추상화를 떠밀어야하는데, 그 방법이
-
내가 Paper한테 너무 많이 물어봤다 -> 물어보지 말랬는데, 첨부터 다 물어보고 있다.
-
"paper야 너 혹시 Client형이니?"
"Client라면 이런 Data들 가지고 있지 않니?"
- 물어보는 순간부터 OCP위반(다운캐스팅 시작)
- my)
paper
에게 물어보다
=if절 (주체로 들어가)
paper
야 instanceof Client 이니? +가지고있으면 변수로 꺼내줄래?
-
-
-
물어보지 않으면 OCP 위반(다운캐스팅) 안할 수 있다. + 시킬때 는 나(
this
)만 들어가는 것 같지만, 그 외에 같은 레벨의 구상체(BE)도 들어가야하는 공통코드라면 ->추상체인 (Programmer)로 정의
될 것이다.-
"paper야 <~이니? 맞으면 변수에 꺼내줄래?> 대신 니가 [나에게] 데이터를 set해줘"
를 set으로 시킨다. ->paper.setData( this )
-
my)
if + 변수로 정보꺼내보며
물어보지말고,setXXX
(this
)로나에게 세팅해줘(XXXX(this))
를set
으로 시킨다.- 나에게 ( this )
- 시킨다. setXXXX (this)
- cf) 시키려면
해줄 사람
이 메소드로 기능을 제공해줘야한다 -> 메소드로 만들기- 개발자야, paper 던져줄테니, 프로그램 만들어줘
- Program makeProgram(Paper paper)
- 개발자야, paper 던져줄테니, 프로그램 만들어줘
-
setData(
this
) 의 인자는나에게
자리에는FrontEnd
(구상적인)가 오든지,Programmer
(추상적인)가 올 것인데, 딱히 관심없다. -> 미뤘다.- paper가 뭔가를 하는데
this
= 나에게 = 언어 특징상상위(추상적인) Programmer
든,하위(구체적인) FE/BE
이든 누구든지 올 수 있으며, 참고적으로 return은 부모(상위, 추상적인) Programmer로 받아야한다. -
인자로
this
를 사용한 순간- 형(Type)이 무엇인지 물어보던 것도 미뤘다. -> 알아서
인자의 특성상
상위->하위 모든구상형들 모두 사용가능
이므로 - 원래는 “if
너 무슨형
이니?”부터 물어봤었고 + 속성값들도 다 물어봤었는데
- 형(Type)이 무엇인지 물어보던 것도 미뤘다. -> 알아서
- paper가 뭔가를 하는데
-
-
물어보지 않고 시켜서 -> paper에게 미룬 것들 정리
-
paper에게, 무슨형인지 물어보는 것을 -> this로 미룸
-
paper에게, 무슨 데이터가지고 있는지 물어보고 세팅하는 것 -> setXXX로 미룸
-
-
앞으로 더 많은 paper(Programmer인듯?)의 자식class들이 생겨나도,
this
에 들어갈 수 있기 때문에, FrontEnd 클래스는 더이상 안고쳐도 된다.
2. BackEnd와 같이볼 때 DRY위반으로 추상층에 공통로직올린: Programmer
-
paper로 가기전에, backend를 보자.
-
수정된 makeProgram() 내부에 있는
makeBackEndProgram()
은 BE만의 특성이다. -
하지만,
paper
를 이용해서 [물어보지 않고 시키는].setData( this )
는 FE/BE 공통코드다
-
-
인터페이스
였던 Program을 ->추상클래스
로 upgrade{ 템플릿메소드 + 훅 }
으로 변경해줘야한다.- 두 하위(자식)범주(클래스)에
공통코드 + 각자의 코드
->템플릿메소드 + 훅
으로 각자코드 -
DRY원칙
에 의해공통코드가 반복
되는 것을 막기위해<로직없는 인터페이스>
—><공통로직을 가진 추상클래스>
로 upgrade- 왜 우리가 처음엔 인터페이스로 시작했다가 -> 중간에 추상클래스로 바뀌는 이유는 DRY원칙 때문이다.
- 로직이 없던
인터페이스
- 공통코드는
템플릿메소드
+ 각 구상코드의 고유 코드가 들어가도록훅
을 가진추상클래스
- FE/BE 공통코드
getProgram()
- 개벌 고유코드를 정의해줄 들어갈 (코드없는 메소드)훅
makeProgram()
- FE/BE 공통코드
- 두 하위(자식)범주(클래스)에
-
공통코드를 중복제거 하기 위해서는 인터페이스 -> 추상클래스 공통템플릿 + 훅으로 바뀌는 경우가 많음을 생각하자.
-
템플릿 메소드 패턴은
객체지향에서 중복발견
시마다 쓰이니, 엄청 자주 쓰인다.- 인터페이스로 설계 -> 중복발견해서 템플릿메소드를 가진 추상클래스로 바꾸는 것은 밥먹듯이 하는 일
- 만약, 공통코드가 아니다. (어떤 프로그래머Class는 paper.setData안한다 ) -> 다시 인터페이스로 바꾸면 된다.
-
템플릿 메소드 패턴은
3. 추상층에겐 말고 로 떠넘긴: Paper나에게>물어보고>
-
추상레이어
=공통로직
에서.setData( 나에게 )
를 호출하는 이상은- 공통로직을 그대로 가져가는 구상층인 FE에서 .setData( 나에게 )로 , BE도 .setData( 나에게 )로 부를 것이다.
- 그러려면, setData()의 인자는 추상형(FE/BE의 추상형 -> Programmer)으로 정의해야한다.
- 하위(FE)를 받으면.. 다른 하위(BE)는 사용못함..
- programmer도 abc(공통로직)은 거의 없었다. -> 구상층의 공통점은 아주 조금이다.
-
paper도 마찬가지다. 추상층으로서 abc공통로직은 아주 조금이이며 이게 정상이다.
- 추상층인 인터페이스, 추상클래스가 하는일은 진짜 극 소수이다.
- 구상층(FrontEnd, BackEnd)의 대부분은 d()코드로 차이점 코드들이 대부분이다.
-
paper도 마찬가지다. 추상층으로서 abc공통로직은 아주 조금이이며 이게 정상이다.
- paper도 마커 인터페이스라서, 정보가 없다( 추상클래스라도 공통로직은 조금인데 이건 아예 없다)
- 다운캐스팅해서 -> 구상층으로 가서 정보를 물었었다.
- **Programmer쪽에서 OCP를 지키려고, 정보물음의 대상인 Paper에게
시켜서
**-
Paper내부에서 정보를 받으라고 밀어버렸
다.- Paper도 정보가 없는 놈이라 ->
Paper의 구상층들에게 구현하도록 다시 민다.
- Paper도 정보가 없는 놈이라 ->
-
4. 시 모든 대상에게 하기 위해 을 줬더니, 정의시 전과하여 발생: Client시켜서>추상층>시켜서>
-
programmer가
물어봐서 받기
( if instance + 변수받기)를 paper에게시켜서 꽂기
떠넘겼는데 paper도 정보가 없는 인터페이스다 -> 자연스럽게 구상층 Client에게 `물어봐서 떠넘긴다.- 물어보는 로직자체는 안사라진다 ->
Programmer - Paper - Client
로 넘어왔다. - .setData(Programmer
programmer
)가 makeProgram(Paperpaper
)가 일어켰던 context에러를 일으킬 것이다.-
이쯤에서 개발자들이 포기한다.
- 끙끙거리면서 Programmer를 개선시켰으나, 다음날 Paper에서 같은 문제가 또 발생해서 포기함
-
if instanceof
로다운캐스팅하면서 물어보기
& 구상층은 절대적으로 n개로 정해졌다고 합리화해버린다.(더이상 안늘어나므로 물어보기 써도된다고 합리화)
-
이쯤에서 개발자들이 포기한다.
- 물어보는 로직자체는 안사라진다 ->
-
LSP 해소방법이
시켜서 꽂기
라서 시켰더니-
꽂을 때도 <다운캐스팅해서 꽂는기능이 있는지 물어보기>
의 문제가 생겼다. -
추상층끼리만 대화하고, 구상층끼리는 완전히 격리되서 모르게 설계 = 잘 설계되었음에도 불구하고, **가 계속 떠넘겨진다.**다운>
추상층이 포함하는 내용(공통로직)이 일반적으로 적어서
-> 추상층에 정보가 적다 ->다운캐스팅해서 물어볼 수 밖에 없는게 일반적
이기 때문이다.
-
추상층인 discountPolicy와 discountCondition의 메소드 = 공통로직 abc()
- 구상층도 abc() 공통로직만 가지고 있음 -> 추상층이 구상층을 풀커버 가능(비현실적)
- 원래는 구상층에 d()가 대부분이어야한다.
- 여기 예제에서 구상층 ServerClient와 Client의 공통로직은 아예 없다
- setFE+setBE vs set프로그래머
-
우리가 알 수 있는 공통점이라고는 ==
공통 추상층
==형
(Programmer를 구현, 상속한 구상층들) 밖에 없다- 이게 정상이다.
- 자식들의 공통점은 없다고 보면 된다.
- 가장 좋은 인터페이스(공통점을 올려 추상화한 것)은 메소드(공통로직)가 없는 것이라고 이야기 할 정도..
- 구상층도 abc() 공통로직만 가지고 있음 -> 추상층이 구상층을 풀커버 가능(비현실적)
-
-
공통점이 없을 게 뻔한데, 여기다 메소드들을 다 정의했다? ->
성급화 추상화
->그냥 공통로직(점)을 올린 추상층 인터페이스or추상클래스는 메소드가 없어야 or <메소드 1개이하>여야 정상
2개이상의 메소드가 있는 인터페이스, 추상클래스 -> 성급한 추상화
- 근데,
성급한 추상화를 안하려고 <공통로직>=메소드가 적게 만들었지만 -> 그로 인해 LSP위반으로 -> 공통로직이 너무 정보가 없어서 다운캐스팅해야하는 상황이 발생
- 어쩌라는 거지?
-
물어보는 애를 LSP, OCP를 없애기 위해서 시켰더니 -> 핑퐁으로 주고받는 결과밖에 못얻었다
5. 더 심각한 문제: ServerClient
-
setData( this )로 시켜서 (Programmer programmer)를 꽂을 때,
-
구상층 ServerClient입장에서 들어오는 programmer가 무조건 2개이상(n개를 대응해야한다)이다.
관계역전 후
시킨 paper쪽에서, 다 받기 위해 progammer를 알게 시켰더니(그림 설계상 위배된 화살표 반대방향)
-
관계역전 전
에는- 구상층 BackEnd입장에서 들어오는 paper가 1개만 들어온다.
-
-
Client와 비교해서는
- Client는 programmer 한명만 대응하는 setData였지만
- ServerClient는 여러명의 programmer를 if로 나눠서 대응하여 책임이 훨씬 높아진 상태가 된다.
-
결론적으로, 관계역전 전(programmer가 해결)보다 난이도가 높은 일이 되어버렸다.
- 1:M 책임(Paper에게 전과하여 다운캐스팅) 보다 1:1 책임(Programmer가 해결)이 더 쉽다
- 1:M 매칭은 더 일반화된 코드가 필요해서 더 어려운문제가 된다.
- 다운캐스팅은 안하는게 좋지만, 누가 다운캐스팅을 가져가냐에 따라 난이도가 결정된다.
- 1:M 책임(Paper에게 전과하여 다운캐스팅) 보다 1:1 책임(Programmer가 해결)이 더 쉽다
6. 외부 객체 메세지를 받는 이상, if 분기는 외부context관점의 case들까지 다 처리해야하는데: Client
-
Client 프로젝트는 Paper의 setData()를 받아서
-
set시킬 메소드를 제공하는 FrontEnd 개발자인지 만 확인한 뒤 처리했다.
- 얘는 지금 BackEnd 개발자는 처리를 아예 안하고 있다.
-
if로 짜지말라고 하는 이유는 -> 우리는 모든 Case를 다 알 수 없다.
- 매 case를 if분기에 다 넣어야하는데
-
Client 클래스를 짜다보면, Client관점에서 FrontEnd만 챙기는 오류를 범하기 때문
작성중인 Class관점만 생각 -> 다른 case를 안다루어도 된다고 생각하는 오류
- java를 비롯한 모든 랭기지들은, 함수의 위치에 따라 메소드들이나 context가 다뀌는 문법을 사용한다
- 특정 위치 (Client class내부)에 함수를 넣음으로써 -> 그 instance.(내부this.)에서 호출할 것이라고 생각하고 짠다.
- 즉, 보다 일반적인 상황이 아니라, 위치상의 context에 맞게 짜려고 한다.
-
set시킬 메소드를 제공하는 FrontEnd 개발자인지 만 확인한 뒤 처리했다.
-
그러나, 인자에
메세지로 넘어온 객체
(Programmer programmer)로서외부와 통신
하고 있는 이상 ->외부에 영향 받는
이상-
client코드에서 호출하는
외부 context까지 다 파악
한 상태여야만내부 if 모든 case를 파악할 수 있다
- Client class입장에서만 짜지말고, 외부에서 호출할 때 외부 context까지 다 생각해야한다.
- 측, FrontEnd만 들어온다가 아니라, BackEnd로서 programmer가 들어올 수도 있으며 그 경우에도 처리를 해야한다.
- Client class입장에서만 짜지말고, 외부에서 호출할 때 외부 context까지 다 생각해야한다.
-
메소드의 생성 이유:
남(외부 클라코드)이 시키도록 서비스 제공
하는 것- **
외부 클라 코드가 이 메소드를 어떻게 쓰는지, 모든 경우의 수를
생각하면서 메소드 짜기 **- 형태가 특정 class안에 있다고 해서, 그 class관점에서만 짜면 안된다.
- **
-
client코드에서 호출하는
-
가둬지는 문법체계는 조심해라
- test case를 최대한 많이 짜낸다.
- 외부 클라이언트 측면에서 이렇게 많이 부르는구나를 알고나서
- 메소드를 짠다.
7. 시행착오 교훈
-
한쪽 Class(Programmer -> Paper로 문제 넘김)를 LSP, OCP지키도록 바꾼다 하더라도
- 추상페이퍼까지 이쁜데도 불구하고
- 받아준 쪽에서 문제가 옮겨갔을 뿐이지 해결되진 않았다.