일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | 31 |
- 데이터베이스
- 리액트 네이티브
- 팀플회고
- 깃허브 로그인
- 리액트 네이티브 시작하기
- 깃 터미널 연동
- 백준
- 데베
- 백준 4358 자바
- 모두를위한딥러닝
- 스터디
- 지네릭스
- 모두의 네트워크
- 정리
- 백준 5525번
- 모두의네트워크
- 리액트 네이티브 프로젝트 생성
- 깃허브 토큰 인증
- 깃 연동
- 문자열
- 딥러닝
- 자바
- SQL
- React Native
- HTTP
- 백준 4949번
- 네트워크
- 백준 4358번
- 모두를 위한 딥러닝
- 머신러닝
- Today
- Total
솜이의 데브로그
Ch1 ) 객체, 설계 본문
책 : 오브젝트
서론
- 티켓 판매 시스템이라는 간단한 도메인을 예로 들어 전체적인 주제를 함축 및 전달
기본적으로 패러다임에 대해서 설명하고 있고 이 중 프로그래밍 패러다임의 경우 기존의 것을 새로쓰는 것이 아니라 별개의 패러다임으로 존재한다.
과거의 패러다임의 단점을 보완하는 발전적인 과정이라고 보인다.
→ 프로그래밍 패러다임은 새로운 것으로 바꾸는 혁명적이라기 보다 발전적이라고 볼 수 있다.
객체지향 패러다임과, 절차지향 패러다임의 경우를 나눈다기 같이 배우면 좋은 영역이라 보면 좋을 거 같다.
이론이 먼저일까?, 실무가 먼저일까?
로버트 L 글래스는 학습에 있어서 대부분 이론을 먼저 배우고 정립한 후 실무로 넘어가는 것이 더 발전할 것이라는 견해와는 다르게 실무가 선행 되고 이론을 정립하는 것이 더 발전적이라고 말한다.
→ 여기서 하고자 하는 말은 실무를 통해 자세히는 모르지만 대략의 흐름을 파악하고 그에 대해 자세히 이론을 배우는 것이 이해하기가 쉽고 성장할 수 있기 때문이 아닐까 싶다.
이 책에서는 소프트웨어 유지와 설계에 있어서 글래스의 주장을 기반으로 이론보다는 실무에 맞춰 책을 소개하고 있고 장황한 설명보다 코드를 통해 대화하는 형식으로 우리에게 말해준다.
01. 티켓 판매 애플리케이션 구현하기
- 작은 이벤트를 진행
- 추첨을 통해 선정된 관람객에게 공연을 무료로 관람할 수 있는 초대장을 발송하는 것
- 일반 관람객, 이벤트 당첨 관람객은 다른 방식으로 입장해야한다.
- 일반 관람객 : 티켓을 구매 해야만 입장
- 이벤트 당첨 관람객
- 이벤트 당첨 여부를 확인!
- **당첨(O)** → 바로 입장
- **당첨(X)** → (일반 관람객 처럼) 티켓을 구매 해야만 입장
- 이벤트 당첨 여부를 확인!
Step 1
public class Theater {
private TicketSeller ticketSeller;
public Theater(TicketSeller ticketSeller) {
this.ticketSeller = ticketSeller;
}
public void enter(Audience audience) {
if (audience.getBag().hasInvitation()) {
Ticket ticket = ticketSeller.getTicketOffice().getTicket();
audience.getBag().setTicket(ticket);
} else {
Ticket ticket = ticketSeller.getTicketOffice().getTicket();
audience.getBag().minusAmount(ticket.getFee());
ticketSeller.getTicketOffice().plusAmount(ticket.getFee());
audience.getBag().setTicket(ticket);
}
}
}
- 테스트를 해봤을땐 전혀 문제되는 에러 없이 잘 돌아간다.
- enter 메서드에서 봤을때 로직이 처리되는 방식이 자동화되어 처리되고 있단 느낌이 없다.
- 특히, 일반 관람객의 경우 티켓을 받기 전 티켓 부스에서 티켓하나를 가져오고 금액을 지불하고 티켓 판매원은 판매 금액(amount)를 증가시키고 그제서야 티켓을 받는 형태를 띄게된다.
02 무엇이 문제인가?
로버트 마틴은 소프트웨어 모듈이 가져야 하는 세가지 기능에 관해 설명했습니다.(클린 소프트웨어 : 애자일 원칙과 패턴, 실천방법)
모듈이란? 크기와 상관없이 클래스나 패키지, 라이브러리와 같이 프로그램을 구성하는 임의의 요소를 의미한다.
마틴의 3가지 목적
- 실행 중에 제대로 동작하는 것
- 변경을 위해 존재하는 것
- 대부분의 모듈은 생명주기 동안 변경되기 때문에 간단한 작업 만으로도 변경이 가능해야한다.
- 변경하기 어렵다 하면 이는 개선해야한다!
- 코드를 읽는 사람과 의사소통 하는 것
- 결국 개발은 혼자하는 것이 아니라 같이작업하기 때문에 다른 사람이 봐도 파악하기 쉬워야한다.
→ 위 목적을 step1에 대입해보면 1번의 항목만 만족하고 나머지는 만족하지 않는다.
02-1 예상을 빗나가는 코드
예상을 빗나간다는 의미는 우리가 실생활에서 처리되는 방식과 다르다는 것을 의미한다. 먼저 문제는 관람객과 판매원이 소극장의 통제를 받는다.
1. 왜 통제를 받을까?
- 코드를 잘보게되면 소극장(theater)이 enter메서드를 통해 관람객한테서 초대장이 있는지 없는지를 직접 확인하고 관람객 돈을 차감하고 매표소(ticket office)의 판매금액을 높이고 있다.
- 이 상황을 좀더 쉽게 풀어보면 소극장안에 판매원과 관람객이 있다고 했을때 서로 가만히 있는데 티켓을 누군가 판매하고 그 돈이 쌓이고 차감하고의 형태가 된다. (마치 기계가 대신 해주는 느낌?)
2. 이해 가능한 코드인가?
- 원래 우리가 극장에서 티켓을 구매한다고 했을때 내 가방에서 직접 초대장을 꺼내거나 돈을 꺼내어 지불하고 티켓 판매자는 초대장을 확인하고 직접 티켓을 가져와서 관람객에게 전해주게된다.
- → 비교해 봤을때 일상생활에서 겪는 프로세스와 다르게 보인다. 그래서 이책에서도 우리의 상식과 다르게 동작하여 의사소통을 제대로 하지 못한다 나와있다.
- enter 메서드만 봐도 판매원과 관람객의 행위가 각각 나뉘어져 있기 때문에 일련의 기능을 전부 알고있어야만 하고 하나의 기능을 빼먹는 경우가 생길 수 있는 구조이다.
- 기억에만 의존하게 되는 경우가 생겨 실수를 할 수 있고 이 코드를 처음본 사람의 경우 혼동을 줄 수 있는 구조가 된다.
02-2 변경의 취약한 코드
step1의 코드에서는 무조건 현금이라는 하나의 결제 수단만 한정지어 구성되어 있다. 이러한 경우 관람객이 다른 결제수단을 늘린다 했을때 관람객 class와 연관된 소극장(theater)도 같이 수정되어야한다.
→ 프로젝트를 하다보면 이런 경우가 한번씩 발생하게 되는데 이럴때 어떤 관계냐에 따라 같이 수정해줘야하는 경우와 아닌 경우를 나눠서 잘 설계해야한다고 생각한다.(무조건 변경에 있어 독립적인 것은 아니라고 생각한다.)
Theater가 의존하는 객체
→ Audience, Bag, TicketSeller, TicketOffice, Ticket (많은 클래스를 의존하고 있다)
- 이와 같은 문제는 **의존성(dependency)**와 연관이 있다.
- 의존성은 변경에 대한 영향을 암시한다.
- 객체의 변경이 있을때 그 객체에게 의존하는 다른 객체도 함께 변경될 수 있다는 사실을 내포하고 있다.
- 객체 사이의 의존성을 완전히 없애는 것이 정답은 아니다.
- 객체간 관계에서 필요한 의존성은 유지하지만 불필요한 의존성은 제거하는 것이 좋다.
- 결합도와의 연관성
- 객체 사이의 의존성이 너무 강한 경우를 결합도가 높다고 말한다.
- 결합도는 의존성과 관련돼 있고 결합도가 높을 수록 변경에도 자유롭지 못하게된다.
03 설계 개선하기
코드가 이해하기가 어렵고 변경하기가 쉽지않기 때문에 개선이 필요하다. 여기서는 변경과 의사소통이라는 문제가 서로 엮여 있다는 점에 주목한다.
- 이해하기 어려운 이유
- 소극장(Theater)이 관람객의 가방과 판매원의 매표소에 직접 접근하기 때문.
- 관람객과 판매원의 각자의 일을 해야만 우리가 생각하는 일반적인 예상과 일치하게 된다.
→ 여기서의 개선에서는 변경의 있어 자유롭고 소극장(Theater)의 의존성을 낮추기 위해 관람객과 판매원을 각각 독립적인 형태로 리팩토링을 진행한다.
03-1 자율성을 높이다
소극장(Theater)이 판매소에 접근하는 것을 막는다(판매소는 판매자만 접근 가능하도록 수정)
(기존) 소극장에서 여러 클래스를 접근, 의존하는 형태 → (변경) 오직 판매자에만 접근하도록 변경
- TicketOffice에 대한 접근을 판매자만 접근할 수 있게 변경함으로서 티켓의 판매, 금액 적립은 판매자에게만 일임하게 된다.
- 개념적이나 물리적으로 객체 내부의 세부적인 사항을 감추는 캡슐화 적용
- 캡슐화를 통해 객체 내부로의 접근을 제한하게 되면 객체간의 의존성(결합도)을 낮출 수 있게된다.
- 변경에 있어서도 한 부분만 수정해도 된다.
→ TheaterSeller의 캡슐화가 진행되었고 이 다음은 Audience의 캡슐화를 진행해보자!
(기존) 판매원의 캡슐화로 관람객 bag은 판매자가 접근하는 형태 → (변경) bag은 오직 관람객만 접근 가능
- 변경된 코드에서는 관람객은 자신의 가방 안에 초대장 유무를 스스로 확인할 수 있게된다.
- 판매자와 같은 제 3자는 접근을 허용하지 않는다.
→ Audience 클래스에서 getBag()은 필요가 없어지고 Bag을 내부로 캡슐화할 수 있게 된다.
- 코드 수정 결과, 판매자와 관람객 사이의 의존성(결합도)이 낮아진걸 확인할 수 있다.
- 이번 코드 수정을 통해 관람객과 판매자는 자신의 역할에 대해선 자신이 책임을 갖게되고 독립적으로 해결한다. (자율적인 존재!)
03-2 무엇이 개선됐는가?
마틴의 3가지 목적에 대해서 어느정도 만족하게 되었다.
- 코드는 잘 동작하는 것을 만족하였다.
- 변경에 있어서 판매자, 관람객에 의존성을 낮춰 자유도를 높였다.
- 코드를 처음 읽는 사람도 어떤 클래스가 무엇을 하는지에 대한 의도를 파악하기 쉬워졌다.
03-3 어떻게 한 것인가?
처음 코드의 경우 객체의 경우 잘 나누었지만 객체간의 연관성을 생각하지 못한 설계였다. 소극장 자체가 직접 판매자와 관람객의 행동을 제한한 형태를 가지고 있었다. → 수정한 후 소극장은 판매자에게 티켓 판매만을 전달해주고 판매자는 티켓을 판매하고 금액을 정산 하기만 하고 관람객은 자신의 초대장을 제시하거나 티켓을 구매하는 행위를 각자 역할에 맞춰 나누고 관리할 수 있게 되었다.
→ 각각의 객체는 자율성이 높아지도록 개선되었고 이로써 유연한 설계를 얻을 수 있었다.
03-4 캡슐화와 응집도
응집도(cohesion)
- 밀접하게 연관된 작업만을 수행하고 연관성 없는 작업은 다른 객체에게 위임하는 객체를 가리켜 응집도가 높다고 말한다.
- 이런 응집도를 높이기 위해서는 객체 스스로 자신의 데이터를 책임져야한다.
→ 외부의 간섭을 최대한 배제하고 메시지를 통해서만 협력하는 자율적인 객체들의 공동체를 만드는 것이 훌륭한 객체지향 설계를 얻을 수 있는 지름길이다
03-5 절차지향과 객체지향
절차지향 프로그래밍
프로세스와 데이터를 별도의 모듈에 위치시키는 방식을 절차적 프로그래밍이라고 부른다.
- 소극장(Theater)의 예시
- enter 메서드는 **프로세스(Process)**에 해당한다.
- enter 메서드 내부의 Audience, Bag, TicketSeller, TicketOffice는 **데이터(Data)**에 해당한다.
→ 소극장 프로젝트의 초기 코드에서는 Theater라는 한 클래스 내부에서 일련의 모든 처리를 담당하였고 데이터에 해당하는 클래스들을 모두 의존하고 있다.
책에서는 이런 일반적으로 절차적 프로그래밍은 우리의 **직관에 위배**된다. 우리 일상에서는 관람객과 판매원이 각자 역할이 나뉘어져 자신의 일을 한다. 그렇지만 절차적 방식에 있어서는 능동적인 존재가 아니라 수동적인 존재로 여겨진다.
→ 이런 방식은 우리가 생각하는 예상되는 일들을 쉽게 벗어나게 된다.
절차적 방식은 변경에 취약하다.
- 초기 코드에서 Theater가 여러 클래스들을 의존하고 있기 때문에 이 여러 클래스중 하나라도 변경이 발생하면 Theater도 변경해야하는 경우가 생긴다.
→ 변경하기 쉬운 설계란 한번의 하나의 클래스만 변경할 수 있어야 한다
객체지향 프로그래밍
데이터와 프로세스가 동일한 모듈 내부에 위치하도록 하는 방식을 객체지향 프로그래밍이라 부른다.
- 소극장(Theater)의 예시
- Theater는 오직 TicketSeller에만 의존한다.
→ 의존성은 적절히 통제되고 있고 하나의 변경으로 인한 여러 클래스의 변경은 막을 수 있다.
훌륭한 객체지향 설계의 핵심은 캡슐화를 이용해 의존성을 적절히 관리함으로써 객체 사이의 결합도를 낮추는 것이다.
03-6 책임의 이동
앞서 말한 절차지향, 객체지향 프로그래밍의 차이점은 책임의 이동이라고 말할 수 있다.
- 책임이란? 어떤 일을 수행할때 그 일을 맡아서 직접 맡아서 처리하는 역할이라고 생각한다.
- 절차지향 방식의 경우 티켓을 판매하는 판매자, 판매소, 관람객, 가방에 해당하는 행동들이 Theater가 전부 처리하고 있고
- 객체지향 방식의 경우 행동들의 주체를 각각의 객체로 분산함으로서 Theater가 분담하는 책임을 나누었다.
→ 이런 객체지향 설계의 핵심은 객체에 적절한 책임을 할당하여 서로의 의존성을 낮추고 각 객체는 자신과 관련된 항목의 행동만을 수행하여 높은 응집력을 가질 수 있게된다.
(캡슐화로 높은 응집력을 만들 수 있고 객체마다 결합도를 낮출 수 있다. )
03-7 더 개선할 수 있다
더 개선할 수 있는 사항으로는 Audience와 Bag사이의 역할 분리 TicketOffice 와 TicketSeller의 역할 분리 정도가 남아있다.
(기존)Audience에서 Bag의 행동(초대장 유무,계산)까지 처리 → (변경)Bag의 행동을 따로 분리해준다.
- 이렇게 하면 Bag에서의 초대장 유무, 티켓 구매, 정산을 분리하여 관리 할 수 있게 된다.→ 이렇게 생각하면 복잡해지지만 레베카 워프스브록은 객체지향의 세계에서는 이러한 수동적인 사물의 객체로 사람처럼 행동하여 자유로운 존재로 생각하여 설계해야한다고 한다.
- → 여기서 처음에 의문이 들었던건 객체지향이란 우리가 이해할 수 있는 코드여야 한다고 했다 그렇지만 가방은 사람이 아니고 사물이기 때문에 티켓을 구매하는 행동이라는것이 이해가 되지 않는다.
🤔그렇다면 모든 객체에 대해 책임을 지도록 설계하면 무조건 맞는걸까??
- 기존에는 판매원을 통해 관람객의 티켓을 구매하는 것과 판매소의 티켓을 가져오고 정산하는 과정이었다.
- 여기서 객체마다의 역할로 바꾸게 되면 티켓을 가져오고 정산하는걸 오로지 팬매소에 위임하게되는 형태가 되는데 이때 판매소는 관람객에게 티켓을 판매하는 형식이 된다.→ 객체지향적으로 응집도를 높이려 했지만 객체간의 결합력은 높아진 형태가 되었다. 이런 경우에는 다른 동료들의 생각을 들어보고 같이 유지보수, 설계에 있어 이해하기 쉬운 방향을 선택하는게 좋다고 생각한다.
- (책에서는 이부분의 개선은 안하는 쪽으로 기존 왼쪽의 방식을 가져간다.)
- → 지금까지 객체지향에 대해 들어봤을땐 사물을 사람처럼 의인화해서 생각하면 개선된 코드도 틀리지 않는다 하지만 여기서 좀더 생각해봐야할건 개선했을때 불필요한 의존성이 생기기 때문에 차라리 전에 코드가 더 좋을 수 있게 된다.
04 객체지향 설계
설계란 코드를 배치하는 것이다.
좋은 설계란 정확히 무엇일까?
→ 요구사항을 전부 만족하고 변경에 있어서 수용할 수 있는 설계이다.
- 변경을 수용할 수 있는 설계가 왜 중요할까?
- 요구사항은 항상 변경되기 때문에 그럴때마다 쉽게 변경하여 반영할 수 있어야한다.
- 코드를 변경할때마다 버그가 생기기 마련인데 이런 부분을 최소화 하고 사이드 이펙트가 적게 나오게 하기 위함이다.
'책을 읽자 > 오브젝트 : 코드로 이해하는 객체지향 설계' 카테고리의 다른 글
Ch2 ) 객체지향 프로그래밍 (0) | 2022.11.28 |
---|