- 8장. 강한 결합: 복잡하게 얽혀서 풀 수 없는 구조
- 9장. 설계의 건정성을 해치는 여러 악마
- 10장. 이름 설계: 구조를 파악할 수 있는 이름
- 11장. 주석: 유지 보수와 변경의 정확성을 높이는 주석 작성 방법
- 12장. 메서드(함수): 좋은 클래스에는 좋은 메서드가 있다.
- Reference
이 글은 내 코드가 그렇게 이상한가요? 책을 읽고 정리한 내용을 바탕으로 작성하였습니다.
8장. 강한 결합: 복잡하게 얽혀서 풀 수 없는 구조
소프트웨어 설계에서
결합도
는 모듈 사이의 의존도를 나타내는 지표이고,책무
는 어떤 관심사를 정상적으로 작동하게 제어하는 책임이다.
-
단일 책임 원칙
을 기반으로 설계해야 한다.-
단일 책임 원칙
은 ‘클래스가 담당하는 책임은 하나로 제한해야 한다’는 설계 원칙이다. -
책임을 대신 지는 클래스가 만들어지면, 다른 클래스가 제대로 성장할 수 없다. (= 성숙해지지 않는다)
-
따라서 관심사에 따라 분리해서 독립되어 있는 구조, 즉
느슨한 결합
으로 설계해야한다.
-
-
DRY 원칙(= Don’t Repeat Yourself) : ‘빈복을 피해라’
-
DRY는 각각의 개념 단위내에서 반복을 하지 말라는 의미이다.
-
같은 로직, 비슷한 로직이라도 개념이 다르면 중복을 허용해야 한다.
-
무리하게 중복을 제거하려 하면, 강한 결합 상태가 된다.
-
-
상속을 하면, 강한 결합 구조를 유발하게 된다. (이 책에서는 상속 자체를 권장하지 않는다)
-
상속 관계에서 서브 클래스는 슈퍼 클래스에 크게 의존하게 된다. -> 서브 클래스가 슈퍼 클래스의 구조를 하나하나 신경 써야 한다.
-
강한 결합을 피하려면, 상속보다
컴포지션
을 사용하는 것이 좋다. -
컴포지션
이란 사용하고 싶은 클래스를 pri-vate 인스턴스 변수로 갖고 사용하는 것을 의미한다. -
상속은 전략 패턴 등으로 조건 분기를 줄일 때 활용할 수 있다.
-
만약 상속을 사용한다면, 반드시
단일 책임 원칙
을 염두에 두고 구현해야 하며, 값 객체와 컴포지션 등 다른 설계를 사용할 수는 없는지 검토한다.
-
-
관련된 것끼리 클래스로 분리한다.
- 클래스를 잘 분리하려면, 각각의 인스턴스 변수와 메서드가 무엇과 관련 있는 지 잘 파악해야 한다.
-
특별한 이유 없이
public
을 사용하지 않는다. ->public
을 붙이면, 강한 결합 구조가 되어버린다. -> 유지보수가 어려워진다.- 클래스는 기본적으로
package private
(default)로 만들고, 패키지 외부에 공개할 필요가 있는 클래스에 한해서만public
으로 선언한다.
- 클래스는 기본적으로
-
한 클래스 내에서 private 메서드가 많으면, 책임이 너무 많은 건 아닌지 확인해본다. -> 책임이 다른 메서드는 다른 클래스로 분리한다.
- 각각의 개념을 각각의 클래스에 잘 분할해서 값 객체로 설계하는 것이 좋다.
-
스마트 UI
는 화면 표시를 담당하는 클래스 중에서 화면 표시와 직접적인 관련이 없는 책무가 구현되어 있는 클래스를 말한다. -
트랜잭션 스크립트 패턴
은 메서드 내부에 일련의 처리가 하나하나 길게 작성되어 있는 구조이다. -
갓 클래스
는 하나의 클래스 내부에 수천에서 수만 줄의 로직을 담고 있으며, 수많은 책임을 담당하는 로직이 난잡하게 섞여있는 클래스이다. -
강한 결합 클래스 대처 방법 -> 객체 지향 설계와 단일 책임 원칙에 따라 제대로 설계한다.
- 책임별로 클래스를 분할 -> 단일 책임 원칙에 따라 설계된 클래스는 아무리 많아도
200줄
, 일반적으로100줄
정도이다.
- 책임별로 클래스를 분할 -> 단일 책임 원칙에 따라 설계된 클래스는 아무리 많아도
9장. 설계의 건정성을 해치는 여러 악마
-
데드코드
는 절대로 실행되지 않는 조건 내부에 있는 코드를 말한다. -> 발견하는 즉시 제거한다. -
YAGNI
: ‘You Aren’t Gonna Need it.’ -> 지금 필요 없는 기능을 만들지 말라 -> 지금 당장 필요한 것들만 만들라는 방침이다. -
매직 넘버
는 로직 내부에 직접 작성되어 있어서, 의미를 알기 힘든 숫자를 말한다. -> 구현자 본인만 의도를 이해할 수 있다.- 매직 넘버를 사용하지 않으려면, 상수를 활용하면 된다. 예시) private static final int POINT = 100
-
영향 범위가 가능한 한 되도록 좁게 설계해야 한다. -> 호출할 수 있는 위치가 적고 국소적일수록 로직을 이해하고 구현하기 쉽다.
-
null을 리턴/전달하지 않는다. -> null 대신 static final 인스턴스 변수 EMPTY로 만든다.
-
예외를 확인했다면, 바로 기록하는 것이 좋다.
- catch 구문에서는 최소한 로그로 기록하고, 상위 레이어의 클래스로 오류를 통지하는 것이 좋다.
-
비즈니스 클래스는 비즈니스 개념을 기준으로 폴더를 구분하는 것이 좋다. -> 도메인 별로 구분되어 응집도가 높아진다.
-
이 책에서 설명하는 방법들은 사양 변경이 있을 때, 이를 조금이라도 쉽게 하기 위한 설계를 설명한다.
- 설계에 Best라는 건 없다. 항상 Better을 목표로 임한다.
10장. 이름 설계: 구조를 파악할 수 있는 이름
-
결합이 느슨하고 응집도가 높은 구조로 만들기 위해서는 관심사 분리로, 관심사에 따라서 각각 클래스로 분할해야 한다.
관심사 분리
는 ‘관심사(유스케이스, 목적, 역할)에 따라서 분리한다’라는 소프트웨어 공학의 개념이다.
-
이름 설계하기
-
최대한 구체적이고, 의미 범위가 좁고, 특화된 이름을 선택하기 -> 어떤 비즈니스를 하는지 모두 파악해야 한다.
-
존재가 아니라 목적을 기반으로 생각하기 예시)
상품
이라면 -> 입고 상품, 예약 상품, 주문 상품, 발송 상품 -
어떤 관심사가 있는지 분석하기
-
소리 내어 이야기해 보기 -> 비즈니스 측면을 잘 이해하고 있는 사람과 이야기 해본다. -> 잘못 인식하는 부분이 있으면 바로 피드백을 받을 수 있다.
-
이용 약관 읽어보기 -> 비즈니스 규칙과 클래스를 일치하게 만든다.
-
다른 이름으로 대체할 수 없는지 검토하기 -> ‘고객’이 아니라 ‘투숙객’, ‘결제자’처럼 대체할 수 없도록 이름을 변경한다.
-
결합이 느슨하고 응집도가 높은 구조인지 검토하기
-
-
이름
을 기반으로 메서드와 클래스를 설계해야 한다. -> 프로그램 구조를 크게 좌우한다. -
수식어를 붙이면서까지 차이를 나타내고 싶은 대상은 각각 클래스로 설계하는 것이 좋다. -> 의미가 다른 개념을 서로 다른 클래스로 설계해서 구조화하면, 개념 사이의 관계를 이해하기 쉽다.
-
OriginalMaxHitPoint : ‘캐릭터의 원래 최대 히트 포인트’를 나타내는 클래스
-
CorrectedMaxHitPoint : ‘장비 착용으로 높아진 최대 히트 포인트’를 나타내는 클래스
-
-
DTO(Data Transfer Object) : 예외적으로 데이터 클래스를 사용하는 경우
- 데이터 전송 용도로 사용되는 디자인 패턴으로, 참조 용도로만 사용되며, 값을 변경하는 용도로 사용하면 안된다.
-
메서드의 이름은 동사 하나로 구성되게 한다.
11장. 주석: 유지 보수와 변경의 정확성을 높이는 주석 작성 방법
-
실제 내용과 내용이 다른 주석은 제거한다.
-
주석 규칙
-
로직을 변경할 때는 반드시 주석도 함께 변경해야 한다. -> 주석을 제대로 변경하지 않으면, 실제 로직과 달라져 주석을 읽는 사람에게 혼란을 준다.
-
로직의 내용을 단순하게 설명하기만 하는 주석은 달지 않는다. -> 시간이 지남에 따라 내용이 낡은 주석이 될 가능성이 높다.
-
로직의 의도와 사양을 변경할 때 주의할 점을 주석으로 달아야 한다. -> 유지 보수와 사양 변경에 도움이 된다.
-
-
문서 주석
이란 특정 형식에 맞춰 주석을 작성하면, API 문서를 생성해 주거나 코드 에디터에서 주석의 내용을 팝업으로 표시해 주는 기능입니다.- 자바의
Javadoc
을 참고하자.
- 자바의
12장. 메서드(함수): 좋은 클래스에는 좋은 메서드가 있다.
-
getter/setter는 값을 마음대로 변경할 수 있으므로 잘못된 값이 섞일 수도 있고, 코드가 중복되는 등 응집도를 낮출 수 있다.
-
매개변수에 final 수식자를 붙여서, 불변으로 만든다.
- 매개변수를 변경하고 싶으면, 불변 지역 변수를 만들고, 여기에 변경 값을 할당하는 형태로 구현한다.
-
출력 매개변수는 사용하지 않는 것이 좋다. -> 가독성 저하
-
매개변수는 최대한 적게 설계한다. -> 메서드가 처리할 게 많아지면, 그만큼 로직이 복잡해진다.
-
오류는 특정 값으로 리턴하지 말고, 곧바로 예외를 발생시키는 것이 좋다. -> throw new IllegalArgumentException()