입문자를 위한 객체지향 안내서 - 조영호님
반응형
사내 세미나 강의 요약
객체지향을 이해할 수 있도록 절차지향의 코드과 객체지향의 코드를 비교!
어디에 포커스를 맞추면 객체지향적인 코드가 나오는 지! 코드 자체가 중요한 것은 아님!
절차지향보다 객체지향이 왜 응집도 높고, 캡슐화, 결합도 낮은 코드인지 알아보는 시간을 갖음
객체 지향에 대한 이해
요구사항 : 온라인 영화 예매 도메인
- 도메인 : 개발할 영역을 추상화 하는 것
- 영화(Movie) - 메타데이터에 가까움
- 상영(Screening) - 실제 판매하는 상품
- 할인 정책(Discount Policy)
- Amount Discount Policy 정액할인 (정해진 금액 할인)
- Percent Discount Policy 정률할인 (비율에 따라 할인)
- 할인 조건(Discount Condition)
- Sequence Condition
- 조조 상영인 경우
- 10회 상영인 경우
- Period Condition
- 특정 요일의 특정 시간대
- Sequence Condition
- 할인 정책 + 할인 조건
- 영화 하나에 할인 정책은 없거나 한 개 (1 : 0..1)
- 할인 정책은 하나 이상의 할인 조건을 갖고 있음 (1 : 1..*)
- 예매
- 대상 영화, 상영 정보, 인원, 정가, 결제 금액
도매인 개념 또는 후보 객체
도메인 용어를 실무와 연관된 명칭으로 해야하는 이유에 대해서는 천천히 설명해나가겠음
- Screening
- Reservation
- Movie
- Discount Policy
- Discount Condition
도메인 로직을 설계하는 방법 (1) 절차적
- Process와 Data를 분리하여 생각 (객체지향은 이 둘을 한데 묶을 수 있는 방법을 고민함)
- 언제 누가 어디서 사용할 지 모르니까 일단 프로세스를 고려하지 않고 데이터 관점에서 설계를 시작함
- 절차지향 관점에서는 데이터는 아무것도 모르는 것이기 때문에, 프로세스 쪽에서 모든 것을 처리하게 됨. 데이터는 수동적인 존재임
- 데이터를 쓰는 쪽에서 판단한다는 것은, 해당 데이터를 다루는 곳들이 다양하면 거기에 중복된 로직이 흩어져서 존재하게 됨
- 응집도가 낮고 서비스쪽에 모든 로직이 결합되어 있는 모습이 쉽게 발견됨
절차지향 설계 순서
- 무엇을 저장할 것인가 - 데이터
- 데이터 모델 설계 (ERD)
- ERD 설계를 먼저 하는 것이 나쁜 게 아니다. 좋다. 하지만 DB 구조가 application 레벨까지 올라오는 것이 안좋은 것임.
- 테이블 형상에 맞는 클래스를 설계하고 getter, setter 정의
- 일단 여기서 캡슐화 깨짐
- DAO나 Repository
- 데이터 모델 설계 (ERD)
- 어떻게 처리할 것인가 - 프로세스
- 어떤 형식으로 어떻게 구현하면 좋을까 고민을 함
- 서비스 만들고 DAO/Repository Injection 함
- 필요한 흐름을 작성하겠지
도메인 로직을 설계하는 방법 (2) 객체지향적
- 객체는 상태와 행위를 갖고 있는 단위 (Process와 Data를 같이 묶여있음)
- 객체는 자기가 갖고 있는 데이터를 자기가 처리
- 객체지향 설계에서 중요한 것
- 이 객체가
어떤 행위를 하는가(프로세스) 런타임 관점에서의 객체의 동작- 컴파일 타임 관점은 클래스
- 런타임 관점은 객체
- 이 객체가
객체지향 설계 순서
- 객체 협력
- 클래스 구조
책임 주도 설계 → TDD가 책임 주도 설계를 코드로 할 수 있게 해줌!
우리 시스템에 필요한 행위를 먼저 찾고, 그걸 책임질 객체를 명명하고, 그 행위에 필요한 상태를 갖추는 것
- 어플리케이션의 요구사항 파악
- 절차지향떄에는 이 데이터가 언제 사용될지에 대한 컨텍스트 고려가 전혀 없었음. 객체 지향에서는 딱 이 객체의 행위에 필요한 데이터만 넣음.
- 요구사항을 시스템의 책임으로 변환
- 그래서 ‘영화를 예매한다’ 요구사항이 있을 때, 이 요구사항 충족을 위해 필요한 객체, 필요한 행위 그리고 나서 상태(데이터)를 고려함 (객체가 있다고 먼저 가정하는거야! 클래스 설계를 하는게 아니라!)
- 시스템의 책임을 객체의 책임으로 변환
- 책임을 담당할 수 있는 적절한 객체 선택
- 객체의 책임 일부를 수행하기 위해 외부의 도움이 필요하다면 다른 객체에게 도움을 요청
- 이 요청을 또 다른 객체의 책임으로 변환
- 책임을 담당할 수 있는 적절한 객체 선택
- 어플리케이션의 요구사항 파악
CRC Card
- 책임과 협력을 표현하기 위한 객체지향 설계 도구
- Candidate(Role or Object)
- Responsibility
- Collaborator
영화 예매 생성 책임(=행위) 할당
- 도메인 개념을 기반으로 예매 생성에 필요한 정보의 전문가에게 할당
- 여기서의
정보란 행위 관점. 이 행위를 잘 수행할 수 있는 이에 대한 정보. 정보는 도메인 모델이라는 공감대 형성 - 데이터는 객체가 가진 속성과 상태
- 여기서의
- 도메인 개념 상에서는 Screening이 적합함
- 예를 들어, 영화가 예매에 대한 책임을 갖게 되면 원래 영화와 Reservation 사이에 연관이 없었는데 생기게 됨
- 근데 Screening(상영)은 영화와 Reservation과 관계가 있지
- 절차지향 : 상영을 예매
- 객체지향 : 상영이 예매
- CRC Card
- 예매 생성 책임 할당 - 예매 생성에 필요한 정보의 전문가에게 할당(Creator) : Secreening 상영
- 상영 정보를 알고 있다
- 예매 정보를 생성한다
- 가격 계산 책임 할당 - 영화 가격 정보를 알고 있는 전문가에게 할당 : Movie 영화
- 영화 정보를 알고 있다
- 가격을 계산한다
- 할인율 계산 책임 할당 - 할인율을 적용할 책임을 가진 객체 추가 : Discount Policy
- 할인 정책을 알고 있다
- 할인된 가격을 계산한다
- 할인 여부를 판단할 책임 할당 - 할인 정책을 판단하는 책임을 가진 객체 추가 : Discount Condition
- 예매 생성 책임 할당 - 예매 생성에 필요한 정보의 전문가에게 할당(Creator) : Secreening 상영
런타임 관점으로 설계한 것을 컴파일 관점으로 구현
- 추상클래스는 구현을 공유하는 것이고, 인터페이스는 구현을 공유하지 않음(근데 디폴트가 나와서 좀 애매해지긴 함)
- DiscountPolicy에서 메시지는 getDiscountAmount임. 런타임에 어떤 구현이 올지 모르지만 이런 정보를 얻는다고 약속/메시지를 선언한 것임
도메인 개념과 구현
설계 이야기
좋은 설계
- 응집도가 높은 객체 - 모듈(클래스) 안의 코드가 동일한 이유로 변경
- 결합도가 낮은 객체 - 모듈(클래스)의 구현 변경 시 파급효과 제한
- 캡슐화 - 변하는 무엇이든 감추는 것
전통적인 관점의 응집도, 결합도, 캡슐화
- 응집도
- 모듈(클래스) 내부 요소들이 서로 관련 있는 정도
- 내부의 기능적인 집중도
- 속성과 메서드가 동일한 기능에 집중
- 결합도
- 모듈(클래스)가 다른 모듈에 의존하는 정도
- 다른 모듈에 대해 알고 있는 지식의 양
- 캡슐화
- 상태(데이터)와 행동(메서드)를 함께 모음
- 데이터를 감추고 공용 메서드를 외부에 공개
설계란
- 설계란 코드를 배치하는 방식
- 두가지 조건을 만족해야함
- 기능 구현
- 유지보수 쉬운 코드
- 쉽게 변경 가능한것과 유연한 코드는 다름
- 읽기 쉽고 변경하기 쉬운 코드는 심플한 코드임
- 유연한 코드 짜겠다고 불필요하게 복잡한 코드가 생길 수 있음
- 설계의 핵심 - 모든 설계 원칙과 용어는 변경과 관련
- SOLID - 녹화본 듣고 다시 적기~
- S : 단일 책임의 원칙. 변경의 이유는 하나여야한다
- O
- L : 클라이언트 관점에서
- I : 불필요한 사이드 이팩트 ㄴㄴ
- D :
- 디자인 패턴
- 뭐가 변경되냐에 따라 디자인패턴을 선택하여 적용
- 디자인 패턴의 목적은 변경을 감추는 것
- SOLID - 녹화본 듣고 다시 적기~
변경 관점의 응집도, 결합도, 캡슐화
절차지향이 항상 나쁜게 아님. 변경이 일어났을 때 응집도/결합도/캡슐화를 고려한 객체지향의 장점을 안내함
응집도
- 모듈(클래스) 내부 요소들이 함께 변경되는 정도
- 👎 낮은 응집도 - 모듈이 다양한 이유로 변경
- ex: 절차적인 예매 로직에서 할인 조건 변경과 할인 정책 변경이 필요해질때 동일한 서비스에서 서로 다른 이유로 변경하게 되는 것임
- 👍 높은 응집도 - 모듈 전체가 동일한 이유로 변경
- ex: 다른 사람들/파일에 피해를 끼치지 않고 내가 해야할 것만 하기 위해 수정 가능해야함
결합도
- 모듈(클래스)을 변경할 때 다른 모듈이 함께 변경되는 정도. 의존성이란 어떤 코드가 바뀔 때 그 코드에 의해 영향을 받을 수 있다는 의미임. 하지만 의존성이 있다고 해서 진짜 그 코드가 바뀌었을 때 영향이 받는 다는 것은 아님. 그걸 결합도가 느슨한 의존이라 함.
- 구현과 추상화의 분리
- 구현 - 자주 변경되는 불안정한 부분
- 추상화 - 자주 변경되지 않는 안정적인 부분. 자주 안바뀌는 애들을 떼어내서 추상클래스나 인터페이스를 사용한 것이지 추상클래스, 인터페이스를 쓴다고 추상화된 것은 아님
- 👎 강한 결합도 - 구현이 변경될 때 함께 변경
- 👍 느슨한 결합도 - 구현이 변경될 때 함께 변경되지 않음
- 안정적인 추상화에 의존하도록 설계
- 인터페이스 - 협력 관점에서 추상화
- 인터페이스와 구현의 분리
- 구현이 아닌 인터페이스에 의존
- 구현과 추상화
- 처음에는 뭐가 변할 지 모름! 그런데 어떻게 추상화를 하겠어! 설계를 할 때 추상화가 명확한 경우는 거의 없어! 설계 초반에는 추상화는 뭔가 중복으로 보임. 중복된 것들을 한 군데로 모으면 추상화임.
- 추상화는 설계 초기에 잡히는게 아니라 리팩토링 하면서 잡힘
- 아래 부분 화장실 다녀오느라 못들음ㅠㅠ 강한 결합도와 느슨한 결합도 부분! 젠장! 현시각 3시 50분~56분 사이
- 클래스 내 private 은 구체적인 구현임! 내맘대로 하겟다는거!
- 상대적으로 해당 변수에 대한 public getter는 인터페이스로 볼 수 있음! (아닌가????? 화장실 다녀오니까 이 절차지향 클래스의 getter setter가 인터페이스 아니라고 하시네)
캡슐화
추상화는 변경 가능성이 높은 부분을 내부로 숨기는 추상화 기법
설계에서 변하는 것이 무엇인지 고민하고 변하는 개념을 캡슐화
변경될 수 있는 어떤 것이라도 감추는 것
절차적인 코드는 캡슐화가 잘 되어있지 않음. public getter setter로 되어있긴 하지만..
👎 ㅇㅇ
👍 ㅇㅇ
타입 캡슐화 (객체지향에서 다형성으로 자주 사용되지!
의존성 역전 원칙- 상위 모듈과 하위 모듈 모두 추상화에 의존!)- 자주 변하는 객체의 타입을 추상회 뒤로 캡슐화
- 공통의 인터페이스를 제공하는 객체라면 협력 가능
두 가지 협력
- 하는 일이 비슷하면 메시지로 추상화함 - 메시지 기반의 추상화를 이용해서 협력 통합
객체지향을 이해하는 게 아니라 응집도, 결합도, 캡슐화를 이해하는 게 중요함!
요구사항 변경
- 도메인 개념 - 중복 할인 정책
- 이전에는 영화 하나에 할인 정책이 0~1개만 가능했는데, 이제 N개가 가능해짐
- 절차적인 예매 코드에서의 변경
- 변경 지점을 찾기 위해 코드를 쭉 읽어나감!
- 지점 찾아서 원래 1개만 조회해오던 할인 정책에서 N개를 불러온 뒤 for문 돌리게쯰
- 나쁜 점 두 가지
- 기존 코드 변경이 두려움
- 응집도 낮다
- 결합도 높다
- 사람은 개념을 기반으로 탐색해서 범위를 좁혀감. 근데 절차지향 프로세스에는 ‘단일 할인’ 이라는 개념이 알고리즘에만 흐름으로, 여러군데에서 적혀있기 때문에 파악이 쉽지 않음
- 기존 코드 변경이 두려움
- 객체지향 예매 코드에서의 변경
- 사이트 이펙트에서 안정적임
- DiscountPolicy 추상클래스에 OverappedDiscountPolicy 개념이 들어간다고 해서 이 영향이 movie나 DiscountPolicy의 약속에 변경이 생기지 않음
- 영화가 협력해야하는 할인 정책의 개수가 변경된 것임! 그렇다면 개수가 바뀌더라도 사이드 이펙트가 젖게 해야함. 이럴 떄 사용하는 디자인패턴이 컴포지트(Composite) 패턴임
- 사이트 이펙트에서 안정적임
- `개방 폐쇄 원칙`이라 함! 기능 확장에는 열려있고 코드 수정에는 닫혀있음!정리
- 좋은 설계
- 응집도가 높은 객체 - 모듈(클래스) 안의 코드가 동일한 이유로 변경
- 결합도가 낮은 객체 - 모듈(클래스)의 구현 변경 시 파급효과 제한
- 캡슐화 - 변하는 무엇이든 감추는 것
- 객체지향 설계가 적합할 때
- 복잡성을 알고리즘에서 분리하고 객체 간의 관계로 만들 수 있다
- 모든 경우에 객체지향이 적합한 것은 아님
- 유효성 검사, 계산, 파생 등이 포함된 복잡하고 끊임없이 변하는 비즈니스 규칙을 구현해야 한다면 객체 모델을 사용해 비즈니스 규칙을 처리하는 것이 현명하다. - Martin Fowler
- Controller, 실제 DB 데이터를 다루는 레이어는 객체 지향으로 짜지 않음
- DB를 읽어와 단순 제공만 하는 경우는 굳이 객체에 담으려고 노력하는 게 아니라 DTO에 담아 제공하는 것이 낫다
- 내가 사용하는게 데이터를 다루는 영역인지, 객체가 필요한 것인지 판단을 해야함. 계속 바뀌고 우리 시스템이 중요하고 뭐 그러면 객체 지향적으로 짜면 좋다
Q&A
- Q. 질문은 못들음
- 실무는 복잡하기 때문에 결합도도 높고 응집도도 높음
- 테스트코드를 짤 떄 모킹을 우당당 많이 써야하면 결합도가 높다고 보면 됨. 결합도 높은 코드의 단위테스트를 짜면 테스트코드도 자주 바뀌게 된다
- 단위테스트 없이 리팩토링도 가능하긴 함
- 코드가 복잡해서 단위 테스트 코드 짜기가 어려울 수도 있지만, 의존성이 넘 많이 물려있어서 그럴수도 이씀. 이럴 땐 분리부터 시작해야함. 작게작게.
- 조영호님은 절차지향으로 빠르게 짜고 그 이후 리팩토링을 하면서 객체지향으로 바꾸신다고 함
- Q. MSA와 객체지향
- 둘이 연관 없음
- MSA를 도입하면 시스템 복잡도가 올라가긴 함
- 이 내부를 함수형이든 객체지향이든 뭐로 구현하든 상관은 없숨!
- MSA는 얻을 수 있는 이점돠 단점이 이써! 모놀로틱 대비 배포나 뭐나 그런걸 얻고자 하는건데 여튼 전체적으로 복잡도는 올라감. 어떤 것들은 서비스별로 오히려 중복도가 올라갈 수 있고 협업 강도가 올라감
- Q. DDD는 OOP의 다른 방법론일 뿐이다. 라는 이야기를 들음. 그정도로 밀접도가 있나요?
- OOP는 코드를 배치하는 방식 중 하나임
- DDD는 객체지향이든 함수형이든 절차저향이든 상관 없음. 더 큰개념임. 도메인에 중점을 두는 것임
- JPA 엔티티와 DDD의 엔티티는 완전 다른 개념임. 용어만 같은거임. DDD에서는 valu of 데이터?일 뿐인데 어쩔 수 없이 JPA에서 엔티티로 다루는 경우가 있자나
- DDD는 패턴 랭기지에 가깝고, JPA는 실제 구현에 가까운거
- MSA 진영에서 자기네 무언갈 설명하기 위해 DDD쪽 바운디드 컨텍스트. 헥사고날을 이용하여 설명한거임
- 객체지향에서는 주문쪽도 상품을 쓰고, 배달도 상품을 쓰고. 그러면 상품이 겁나 뚱뚱해지겠지. 객체지향에서는 어런 거에 대한 언급이 없어.
- DDD에서는 이런것들을 찢어서 중복을 갖고 쓰라고 안내함. 도메인 모델을 각각의 바운디드 컨텍스트마다 따로 만들라고 함. 거기서 도메인 모델을 만들고~ 뭐~ 하고~ 하라함
- 객체지향을 하게 되면 하나의 객체를 여러 군데서 써야한다고 오해하는데 그게 아님!
- 아! 내가 객체지향을 모르는 줄 알았는데 DDD를 몰라서 패키징을 하기 어려워했던거구나!
- Q. OOP 훈련할만한 혹은 어떤 것에 포커스를 두고 훈련하면 좋을지
- 사이트나 책에 있는 예제가 큰 게 없어서 애매쓰…
- 그나마 접할 수 있는 건 프레임워크인데 이건 추상화 덩어리임. 안바뀌는 애들만 모여있음. 객체지향의 완성품을 볼떈 프레임워크를 보는게 좋긴 한데.. 그 과정에 대한 예제는 거의 없어. 프레임워크 보기 어려운게 라이브러리같은게 잔뜩 들어가이썽서 좀 어렵긴해!
- Q. OOP와 실무와의 갭이 존재함. 팀 전체가 비슷한 이해도를 가지고 서로 코드를 짜고 협업하기 위해 하신 액션들이 있는지?
- 실무에 적용하기 어려운 게 ORM이 없어서 힘들었고, 이렇게 짜는게 좋다고 이해도를 맞춰야하니까.
- 그래서 팀원 하나하나 찾아가서 주로 페어 프로그래밍을 하고 일은 야근을 한다던가 그랬음 (팀이 20명 정도라서 가능했음. 시니어/주니어 분포도도 영향이 있음)
- Q. ORM 사용하는 경우 엔티티 클래스 선언하고 사용함. 엔티티 클래스를 객체로 바라보고 비즈니스 로직을 포함하는 코드를 보는 경우가 있음. 엔티티의 용도를 어디까지 정의해주는 게 좋을까?
- JPA의 엔티티는 테이블과 매핑하는 단위임. JPA는 DB를 객체로 매핑하게 편하게 하는 정도임!
- 엔티티를 어느정도 수준으로 하냐에 따라 다름
- 객체지향 설계를 제대로 하려면 JPA는 DB 테이블 매핑용도로만 써! 그니까.. JPA 엔티티를 진짜 테이블과 매핑하는 용도로 쓰고, 객체와의 매핑은 별도로 하는 경우가 있음 (요고 조셉님이 했던거당! 순수하게 하는거! 이상적으로 하는거! JPA Entity에 로직을 넣지 않음)
- 근데 대부분 귀찮으니까 JPA 엔티티를 도메인 객체의 일부로 사용! 실용적인 관점. 그래서 그 JPA 엔티티 클래스에 뭐 ~ 로직도 좀 넣고 하는긔~
반응형
'리뷰' 카테고리의 다른 글
| 2022 인프콘 후기 (0) | 2022.10.10 |
|---|---|
| [책 리뷰] 구글 엔지니어는 이렇게 일한다 - 코드리뷰 파트 (0) | 2022.09.11 |
| [컨퍼런스 리뷰] 여성 개발자분들의 ${ } 개발자로 살고 싶은데요 Day2 (1) | 2022.05.27 |
| [컨퍼런스 리뷰] 여성 개발자분들의 ${ } 개발자로 살고 싶은데요 Day1 (1) | 2022.05.26 |
| [책리뷰] 함께자라기 (3) - 애자일 (0) | 2022.02.18 |
댓글
이 글 공유하기
다른 글
-
2022 인프콘 후기
2022 인프콘 후기
2022.10.10 -
[책 리뷰] 구글 엔지니어는 이렇게 일한다 - 코드리뷰 파트
[책 리뷰] 구글 엔지니어는 이렇게 일한다 - 코드리뷰 파트
2022.09.11 -
[컨퍼런스 리뷰] 여성 개발자분들의 ${ } 개발자로 살고 싶은데요 Day2
[컨퍼런스 리뷰] 여성 개발자분들의 ${ } 개발자로 살고 싶은데요 Day2
2022.05.27 -
[컨퍼런스 리뷰] 여성 개발자분들의 ${ } 개발자로 살고 싶은데요 Day1
[컨퍼런스 리뷰] 여성 개발자분들의 ${ } 개발자로 살고 싶은데요 Day1
2022.05.26