😀
fistkim TECH BLOG
  • Intro
  • 강의
    • Reactive Programming in Modern Java using Project Reactor
      • Reactor execution model 1
      • Reactor execution model 2
      • Reactor execution model 3 - parallelism
      • Reactor execution model 4 - overview
      • Transform
      • Combine
      • Side Effect Methods
      • Exception/Error handling
      • retry, retryWhen, repeat
      • BackPressure
      • Cold & Hot Streams
    • NEXTSTEP 클린코드 with java 9기
      • 정리노트
    • NEXTSTEP DDD 세레나데 2기
      • CH01 도메인 주도 설계 이해
      • CH02 크게 소리 내어 모델링 하기
      • CH03 도메인 주도 설계 기본 요소
      • CH04 도메인 주도 설계 아키텍처
      • CH05 도메인 이벤트
    • NEXTSTEP 인프라 공방 1기
      • 망 분리하기
      • 통신 확인하기
      • 도커 컨테이너 이해하기
      • [미션 1] 서비스 구성하기 실습
      • [미션 2] 서비스 배포하기 실습
      • 서버 진단하기
      • 어플리케이션 진단하기
      • [미션 3] 서비스 운영하기
      • 웹 성능 진단하기
      • 부하 테스트
      • k6
      • [미션 4] 성능 테스트
      • 리버스 프록시 개선하기
      • 캐싱 활용하기
      • [미션 5] 화면 응답 개선하기
      • Redis Annotation 및 설정
      • 인덱스 이해하기 & DB 튜닝
      • [미션 6-1] 조회 성능 개선하기
      • [미션 6-2] DB 이중화 적용
    • NEXTSTEP 만들면서 배우는 Spring 3기
      • CH01 올바른 방향 바라보기
      • CH02 HTTP 이해 - 웹 서버 구현
        • HTTP 파싱
        • HTTP 웹 서버 구현
      • CH03 MVC - @MVC 프레임워크 구현
        • Servlet 다시 짚기
        • Cookie, Session 다시 짚기
        • MVC 프레임워크 구현
      • CH04 나만의 라이브러리 구현
      • CH05 DI - DI 프레임워크 구현
      • CH06 Aspect OP
    • 스프링 시큐리티
      • 스프링 시큐리티 아키텍처
      • WebAsyncManagerIntegrationFilter
      • SecurityContextPersistenceFilter
      • HeaderWriterFilter
      • CsrfFilter
      • (+) 스프링 시큐리티 + JWT
      • (+) 마치며
    • 더 자바, 코드를 조작하는 다양한 방법
      • CH01 JVM 이해하기
      • (+) 클래스 로더 이해하기
      • CH02 바이트 코드 분석 및 조작
      • (+) jacoco
      • CH03 리플렉션
      • CH04 다이나믹 프록시
      • CH05 애노테이션 프로세서
    • 더 자바, 애플리케이션을 테스트하는 다양한 방법
      • CH01 JUnit 5
      • CH02 Mockito
      • (+) Spy vs Mock
      • CH03 도커와 테스트
      • CH04 성능 테스트
      • (+) VisualVM
      • (+) 테스트 자동화
      • CH05 운영 이슈 테스트
      • CH06 아키텍처 테스트
    • 모든 개발자를 위한 HTTP 웹 기본 지식
      • CH01 인터넷 네트워크
      • CH02 HTTP 기본
      • CH03 HTTP 메서드 속성
      • CH04 HTTP 메서드 활용
      • CH05 HTTP 상태코드
      • CH06 HTTP 헤더1 - 일반 헤더
      • CH07 HTTP 헤더2 - 캐시와 조건부 요청
      • (+) HTTPS 원리
    • 스프링 프레임워크 핵심 기술
      • CH01 IOC 컨테이너
      • CH02 AOP
      • (+) 스프링 의존성 관리
      • (+) 생성자 주입 장점
    • 코딩으로 학습하는 GoF의 디자인 패턴
      • 객체 생성
        • 싱글톤 패턴
        • 팩토리 메소드 패턴
        • 추상 팩토리 패턴
        • 빌더 패턴
        • 프로토타입 패턴
      • 구조
        • 어댑터 패턴
        • 브릿지 패턴
        • 컴포짓 패턴
      • 행동
        • (작성중)
    • 실전 Querydsl
      • CH01 프로젝트 환경구성
      • CH02 예제 도메인 모델
      • CH03 기본문법
      • CH04 중급 문법
      • CH05 실무활용 (스프링 데이터 JPA와 Querydsl)
      • CH06 스프링데이터JPA 가 제공하는 Querydsl 기능
      • (+) 별칭(alias)
      • (+) Slice 쿼리
    • 스프링 데이터 JPA
      • CH01 핵심개념이해 1
      • CH02 핵심개념이해 2
      • CH03 핵심개념이해 3
      • CH04 Spring Data Common
      • CH05 Spring Data JPA
    • 실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화
      • CH01 지연 로딩과 조회 성능 최적화
      • CH02 컬렉션 조회 최적화
      • CH03 전체 정리
    • 초보를 위한 쿠버네티스 안내서
      • CH01 쿠버네티스 시작하기
      • CH02 쿠버네티스 알아보기
      • CH03 쿠버네티스 실습 준비
      • CH04 쿠버네티스 기본 실습
    • Flutter Provider Essential
      • CH01 Introduction
      • CH02 Provider Overview
      • CH03 TODO App
      • CH04 Weather App
      • CH05 Firebase Authentication App
    • Flutter Bloc Essential
      • CH01 Introduction
      • CH02 Bloc Overview
      • CH03 TODO App
      • CH04 Weather App
      • CH05 Firebase Authentication App
    • Flutter Advanced Course - Clean Architecture With MVVM
      • CH01 Introduction
      • CH02 Clean Architecture 4 Layer
      • CH03 MVVM
      • CH04 Data Layer
      • (+) Data Layer - response to model
      • (+) Data Layer - Network
      • CH05 Domain Layer
      • CH06 Presentation Layer
      • CH07 Application Layer
      • (+) Application Layer - l10n
      • (+) Application Layer - DI
      • (+) Application Layer - environment
    • 자바 알고리즘 입문
      • CH01 문자열
      • CH02 Array(1, 2 차원 배열)
      • CH03 Two pointers, Sliding window[효율성: O(n^2)-->O(n)]
      • CH04 HashMap, TreeSet (해쉬, 정렬지원 Set)
      • CH05 Stack, Queue(자료구조)
      • CH06 Sorting and Searching(정렬, 이분검색과 결정알고리즘)
      • CH07 Recursive, Tree, Graph(DFS, BFS 기초)
      • CH08 DFS, BFS 활용
      • CH09 Greedy Algorithm
      • CH10 dynamic programming(동적계획법)
  • 도서
    • 만들면서 배우는 클린 아키텍처
      • 학습목표
      • CH01 계층형 아키텍처의 문제는 무엇일까?
      • CH02 의존성 역전하기
      • CH03 코드 구성하기
      • CH04 유스케이스 구현하기
      • CH05 웹 어댑터 구현하기
      • CH06 영속성 어댑터 구현하기
      • CH07 아키텍처 요소 테스트하기
      • CH08 경계 간 매핑하기
      • CH09 어플리케이션 조립하기
      • CH10 아키텍처 경계 강제하기
      • CH11 의식적으로 지름길 사용하기
      • CH12 아키텍처 스타일 결정하기
    • 클린 아키텍처
      • 들어가며
      • 1부 소개
        • 1장 설계와 아키텍처란?
        • 2장 두 가지 가치에 대한 이야기
      • 2부 벽돌부터 시작하기: 프로그래밍 패러다임
        • 3장 패러다임 개요
        • 4장 구조적 프로그래밍
        • 5장 객체 지향 프로그래밍
        • 6장 함수형 프로그래밍
      • 3부 설계 원칙
        • 7장 SRP: 단일 책임 원칙
        • 8장 OCP: 개방-폐쇄 원칙
        • 9장 LSP: 리스코프 치환 원칙
        • 10장 ISP: 인터페이스 분리 원칙
        • 11장 DIP: 의존성 역전 원칙
      • 4부 컴포넌트 원칙
        • 12장 컴포넌트
        • 13장 컴포넌트 응집도
        • 14장 컴포넌트 결합
      • 5부
        • 15장 아키텍처란?
    • 스프링 입문을 위한 자바 객체 지향의 원리와 이해
      • CH01 사람을 사랑한 기술
      • CH02 자바와 절차적/구조적 프로그래밍
      • CH03 자바와 객체 지향
      • (+) 자바 코드 실행에 따른 메모리 적재과정
      • CH04 자바가 확장한 객체 지향
      • CH05 객체 지향 설계 5 원칙 - SOLID
      • CH06 스프링이 사랑한 디자인 패턴
      • CH07 스프링 삼각형과 설정 정보
      • (부록) 람다(lambda)
    • 객체지향의 사실과 오해
      • CH01 협력하는 객체들의 공동체
      • CH02 이상한 나라의 객체
      • CH03 타입과 추상화
      • CH04 역할, 책임, 협력
      • CH05 책임과 메시지
      • CH06 객체 지도
      • CH07 함께 모으기
      • (+) 인터페이스 개념 바로잡기
    • 도메인 주도 개발 시작하기
      • CH01 도메인 모델 시작하기
      • CH02 아키텍처 개요
      • CH03 애그리거트
      • CH04 리포지터리와 모델 구현
      • CH05 스프링 데이터 JPA를 이용한 조회 기능
      • CH06 응용 서비스와 표현 영역
      • CH07 도메인 서비스
      • CH08 애그리거트 트랜잭션 관리
      • CH09 도메인 모델과 바운디드 컨텍스트
      • CH10 이벤트
      • CH11 CQRS
    • 자바 ORM 표준 JPA 프로그래밍
      • CH01 JPA 소개
      • CH02 JPA 시작
      • CH03 영속성 관리
      • CH04 엔티티 매핑
      • CH05 연관관계 매핑 기초
      • CH06 다양한 연관관계 매핑
      • CH07 고급 매핑
      • CH08 프록시와 연관관계 관리
      • CH09 값 타입
      • CH10 객체지향 쿼리 언어
      • CH11 웹 애플리케이션 제작
      • CH12 스프링 데이터 JPA
      • CH13 웹 애플리케이션과 영속성 관리
      • CH14 컬렉션과 부가 기능
      • CH15 고급 주제와 성능 최적화
      • CH16 트랜잭션과 락, 2차 캐시
    • 소프트웨어 세상을 여는 컴퓨터과학
      • CH01 컴퓨터 과학 소개
      • CH02 데이터 표현과 디지털 논리
    • 이펙티브 자바
      • 1 장 들어가기
      • 2장 객체 생성과 파괴
        • [01] 생성자 대신 정적 팩터리 메서드를 고려하라
        • [02] 생성자에 매개변수가 많다면 빌더를 고려하라
        • [03] private 생성자나 열거 타입으로 싱글턴임을 보증하라
        • [04] 인스턴스화를 막으려거든 private 생성자를 사용하라
        • [05] 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
        • [06] 불필요한 객체 생성을 피하라
        • [07] 다 쓴 객체 참조를 해제하라
        • [08] finalizer 와 cleaner 사용을 피하라
        • [09] try-finally 보다는 try-with-resources 를 사용하라
      • 3장 모든 객체의 공통 메서드
        • [10] equals는 일반 규약을 지켜 재정의하라
        • [11] equals 를 재정의하려거든 hashCode도 재정의하라
        • [12] toString 을 항상 재정의하라
        • [13] clone 재정의는 주의해서 진행하라
        • [14] Comparable 을 구현할지 고려하라
      • 4장 클래스와 인터페이스
        • [15] 클래스와 멤버의 접근 권한을 최소화하라
  • 토픽
    • 서버 모니터링
      • CPU 사용량
      • 메모리 사용량
      • 스레드 풀
    • Spring Boot Monitoring
      • Spring actuator
      • Spring eureka
      • Prometheus
      • grafana
      • Spring actuator + Prometheus + grafana
    • JAVA 데일리 토픽
      • 메모리 누수(memory leak)
      • 객체 참조의 유형
      • 커스텀 스레드 풀
      • Mark And Compact
      • serialVersionUID 이해하기
      • 함수형 인터페이스
      • 메소드 참조
      • equals()와 hashCode()가 무엇이고 역할이 무엇인지
      • StringBuffer vs StringBuilder
      • String vs StringBuilder, StringBuffer
      • String interning
    • JAVA GC
    • 프로그래머스 문제 풀기
      • 해시
      • 스택/큐
      • 힙(Heap)
      • 정렬
      • 완전탐색
      • DFS/BFS
    • 데이터베이스 구성 및 작동 흐름
    • 데이터베이스 JOIN 원리
    • 객체지향생활체조 원칙
    • 상태(state), 상속(inheritance), 합성(composition) 의 상관관계
    • java enum은 메모리에 언제, 어떻게 할당되는가
    • Checked Exception vs UnChecked Exception
    • Reactive Streams 원리탐구 - 간단한 예제 직접 작성해보기
    • Flutter Basic
    • Flutter StatefulWidget 생명주기
    • Flutter 가 위젯을 그리는 원리
    • Flutter 클린 아키텍처
      • application layer
        • 패키지 구조 및 레이어 설명
        • environment
        • dependency injection
        • go_router
        • foreground & background
        • 다국어처리 (l10n, i18n)
        • Global 처리(시스템 점검, fore->back 등)
        • connection_manager
        • permission_manager
        • push_notification_manager
        • firebase 연동
      • data layer
        • 패키지 구조 및 레이어 설명
        • network
        • repository
      • domain layer
        • 패키지 구조 및 레이어 설명
      • presentation layer
        • 패키지 구조 및 레이어 설명
        • resources
    • 기술 관련 포스팅 읽기
  • 기타
    • 작업일지
      • 2023. 10
      • 2023. 09
      • 2023. 08
      • 2023. 07
      • 2023. 06
      • 2023. 05
      • 2023. 04
      • 2023. 03
      • 2023. 02
      • 2023. 01
      • 2022. 12
    • Business Model
      • 아이디어 불패의 법칙
      • 린 모바일 앱 개발
      • 린 스타트업
      • 제로투원
      • MIT 스타트업 바이블
      • 린치핀
    • 백로그 종합
Powered by GitBook
On this page
  • 소트워크 앤솔로지 에서 제시하는 객체지향생활체조 원칙 목록
  • 1. 한 메서드에 오직 한 단계의 들여쓰기만 한다.
  • 2. else 예약어를 쓰지 않는다.
  • 3. 모든 원시 값과 문자열을 포장한다.
  • 4. 한 줄에 점을 하나만 찍는다.
  • 5. 줄여 쓰지 않는다(축약 금지)
  • 6. 모든 엔티티를 작게 유지한다.
  • 7. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.
  • 8. 일급 컬렉션을 쓴다.
  • 9. getter/setter/프로퍼티를 쓰지 않는다.
  1. 토픽

객체지향생활체조 원칙

Previous데이터베이스 JOIN 원리Next상태(state), 상속(inheritance), 합성(composition) 의 상관관계

Last updated 1 year ago

NEXTSTEP에서 진행하는 클린코드 수업을 들은 것이 NEXTSTEP을 알게 된 계기였다. 지금도 많이 부족하지만 그 수업을 들으면서 많이 성장할 수 있었고, 이걸 듣지 않았으면 계속해서 퀄리티가 떨어지는 코드를 작성했겠다는 생각을 수업을 듣는 그 당시에도 많이 했었다.

그만큼 수업, 피드백 자체가 양질이었는데 강사진이나 리뷰어분들이 잘해서도 있겠지만 그 근간에 에서 제시하고 있는 객체지향생활체조원칙 이 자리하고 있기 때문이라는 생각이 든다.

이 단어 자체는 리뷰어분들이 여기서 나오는 원칙에 근거해서 리뷰를 해주시는 과정에서 알게 되었는데, 체조? 지향생활? 과 같은 단어 자체가 너무 이상했고 메소드 하나 만드는 것도 원칙을 지키려니 너무 시간이 오래걸리는 것 같았고 왜 그런지 원칙을 이해하지 못한체 마냥 이걸 따라야하니 너무 답답했다.

하지만 그렇게 불평(?)을 품은채로 미션을 진행해보니 어느새 완성된 코드 자체가 그 원칙을 지키면서 작성을 하다보니 단위 테스트를 하기에도 무척 효율적이게 되었고, 가독성도 좋아 보였다.

결국 포비님 말씀대로 이건 각각의 원칙에 대한 당위성을 머리로 이해하는 것 보다 마치 운동을 하듯이 아래 원칙들을 생활화 하다보면 클린코드의 의미를 이해하게 되고 아래 원칙들의 당위성을 알게 되는 것 같다.

아래는 각 원칙별로 정리한 내용이다.

에서 제시하는 객체지향생활체조 원칙 목록

  1. 한 메서드에 오직 한 단계의 들여쓰기만 한다.

  2. else 예약어를 쓰지 않는다.

  3. 모든 원시 값과 문자열을 포장한다.

  4. 한 줄에 점을 하나만 찍는다.

  5. 줄여 쓰지 않는다(축약 금지).

  6. 모든 엔티티를 작게 유지한다.

  7. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.

  8. 일급 컬렉션을 쓴다.

  9. getter/setter/프로퍼티를 쓰지 않는다.

위 원칙들은 클린코드를 위한 정량적인 지침들이며 각 원칙은 곧 원칙이 지켜지는 전제로 코드가 작성될 경우 최대한 클린코드에 가까워진다는 전제를 갖고 있다. 하지만 그저 원칙만으로 달달 외워서 이를 따르면서 코드를 작성하기 보다는 근본적으로 왜 이 원칙대로 코드를 작성하면 클린코드에 가까워지는지 세부적으로 이해하는 것이 학습 가성비 측면에서 더 좋다고 판단되어서 어렴풋했던 이유들을 세세하게 구체화해보는 시간을 가져보았다.

기본적으로 모든 원칙들이 지향하는 바는 클린코드이며 클린코드가 지향하는 바는 근복적인 '좋은 품질'이라는 덕목을 추구한다. 여기서 말하는 '좋은 품질'이란 응집력, 느슨한 결합, 무중복, 캡슐화, 테스트가능성, 가독성을 의미한다.

1. 한 메서드에 오직 한 단계의 들여쓰기만 한다.

이 지침의 대전제는 '하나의 메서드는 하나의 기능을 수행해야한다'는 것이다. 왜냐햐면 하나의 메서드가 둘 이상의 기능을 할 경우 재사용성도 떨어지고 모듈화가 그만큼 떨어진다는 의미이므로 테스트도 힘들어지는 등 여러가지 안좋은 경우가 발생한다.

다르게 말하면 하나의 메서드가 둘 이상의 기능을 수행한다는 것은 곧 메서드의 응집력이 떨어진다고 표현할 수 있다. 그러므로 하나의 메서드는 하나의 기능을 수행해야하는데, 한 메서드에 둘 이상의 들여쓰기가 있다는 것은 중첩된 제어구조가 존재할 가능성이 매우 크다는 뜻이며 이는 곧 하나의 메서드에 둘 이상의 기능이 추상화되어 코드로 작성되었을 가능성이 크다는 것을 의미한다.

for문 안에 for문이 있거나 for문 내에 if 문이 있는 등 특정 연산 내에 또 다른 연산이 들어가게되면 이는 두 단계 이상의 들여쓰기이다.

2. else 예약어를 쓰지 않는다.

객체지향언어의 장점중 하나인 다형성을 사용하기 위한 Strategy 패턴에서 else를 쓸 경우 코드의 의도를 읽기 쉽지 않고 다형성 활용성을 저해시킨다는 의미에서 else 사용을 원천적으로 금지하는 것이다.

다르게 말해서 다형성을 활용하는 코드에서 if-else를 사용할 경우 실제로 재사용성과 가독성을 떨어트리기 때문에 OOP의 정수를 제대로 활용하지 못한다는 논리인 것이다.

사실 나도 else를 피하려다보니 보호절과 조기종결을 너무 자주 쓰게 되는 감이 있었고 항상 '이게 과연 더 최선인가'하는 의문이 있긴 했지만 습관화가 되니 오히려 보호절과 조기종결에 의한 처리가 편해지긴 했다.

정리하자면 OOP의 장점인 다형성을 활용하기 위해서는 원천적으로 else를 사용하면 안되고, 이를 위해서 미리 다른 경우에서도 else를 쓰지 않도록 습관화하기 위한 원칙이 바로 'else 예약어를 쓰지 않는다.' 인 것이다.

3. 모든 원시 값과 문자열을 포장한다.

모든 원시 값과 문자열을 Constant 로 빼서 쓰라는 의미이다. 이 원칙의 효용은 실무에서 매우 크게 느꼈다. 이 원칙의 대 전제는 '원시형 변수 자체로는 프로그래머에게 그 값이 어떤 값이며 왜 쓰이고 있는지에 대한 정보를 전달할 방법이 없다' 이다. 부가적으로 만약 이 특정한 값이 다른 곳에서도 재사용 된다면, 이것이 만약 변경 되었을 때 상수로 빼서 쓰지 않을 경우 변경 지점이 너무 많아진다. 즉, 유지보수가 더 어려워지는 것이다.

예를 들어 사용자가 하루에 먹은 모든 끼니를 가져와서 각 끼니의 칼로리가 500을 넘을 경우 뭔가 처리를 해야 한다고 해보자.(요즘 다이어트중이라 예제가 다이어트에 관해서만 떠오른다) 이 때 원시값을 그대로 사용할 경우 아래와 같은 코드가 나올 수 있다.

if(totalKcal > 500){
    // 원하는 처리(너무 많이 먹었으니 포인트를 차감한다던가 하는 패널티)
}

이걸 만약 만든사람이 아닌 다른 사람이 본다면(혹은 만든 사람이 오랜 시간이 지나서 기억이 휘발된 채로 다시 본다면) 여기서 사용된 500이 권장칼로리인지 하루 제한 칼로리인지 정책적으로(기획상) 의미가 무엇인지 알길이 하나도 없다. 아래와 같이 코드를 바꿔보자.

if(totalKcal > Constant.ONE_DAY_KCAL_LIMIT){
    // 원하는 처리(너무 많이 먹었으니 포인트를 차감한다던가 하는 패널티)
}

만약 위와 같이 작성이 되어 있다면? 바로 이 하루 제한 칼로리가 500인지는 알 수 없게 되지만, 코드상에서 이미 의도가 전달되고 있다. 그리고 이 값이 여러군데에서 쓰인다면 똑같은 곳에서 끌어다 쓸 수 있고 그만큼 유지보수도 쉬워진다.

반대로 원소값 자체가 의미 전달이 가능하다면 굳이 포장해서 쓰지 않아도 된다. 가령, 원소의 첫번째를 가리키는 0을 쓰는 상황에서 누구든 프로그래머라면 배열 내 인덱스를 가리키는 지점에 0이 있는 것을 보면 이것의 의미가 첫번째 원소를 가리키는 것을 아는데 여기서 이걸 포장한다면 되려 혼란을 가중시키는 것이다.

4. 한 줄에 점을 하나만 찍는다.

한 줄에 둘 이상의 점이 있다는 것이 곧 둘 이상의 오브젝트를 동시에 다루고 있다는 의미이며 이것 자체가 코드의 복잡도를 올리고, 잘못이 발생 했을 때 책임 소지를 파악하기 힘들어지기 때문에 한 줄에 점을 하나만 찍으라는 원칙이다.

스트림과 같은 주어진, 내가 바꿀 수 없는 것에서는 체이닝을 쓰되 라인만 구분해주고 그 외에 내가 만드는 class에서는 이 원칙을 지켜주는 것이 타협안이다.

5. 줄여 쓰지 않는다(축약 금지)

축약을 지양하게 하는 이유는 축약된 것을 보고 누구나 같은 해석을 하지 않기 때문에 혼란이 커지기 때문이다. 이 원칙 역시 뚜렷한 논리가 있긴 하지만 실무에서 동료에게 피드백을 하기에는 다소 '단순한 개인 스타일 차이'에 가타부타 이야기를 얹는 느낌이 들어서 실무에서도 좀 애매한 것 같긴 하다.

말하기 편한 환경이거나 컨벤션을 처음부터 같이 잡을 수 있는 환경이라면 꼭 축약을 하지 않는 방향으로 협의를 하는게 좋은 것 같다.

다만 이 부분에 대해서 '프로그래머의 뇌' 책에 명확하게 실험기반으로 제시된 자료가 있는데, 핵심은 축약 하지 않은 코드에서 버그가 덜 나오고 생산성이 더 좋았다.

6. 모든 엔티티를 작게 유지한다.

책에서는 50줄 이상 되는 클래스와 10개 파일 이상 되는 패키지를 만들지 말라고 말하고 있다. 당연히 코드의 절대량이 작으면 한 번에 파악하기가 쉽고 간결한 것은 맞는데 문제는 하나의 클래스에 꼭 같이 있어야 할 기능들이 붙어 있기 위해서는 5줄을 넘기기 쉬운데 이런 경우 패키지를 적극 활용하라고 책에서는 권하고 있다.

클래스가 점점 작아지면서 하나의 패키지 내에 기능들을 최대한 분산하고, 그러면서 동시에 패키지 자체의 응집력을 올리라는 것이다. 일단 내가 인식하고 있던 패키지는 '동일 부류의 묶음' 기능으로만 생각했는데, 패키지의 응집력을 키운다는 생각을 못해봤어서 새로운 view라서 좋았다. 그리고 패키지 역시 응집력있고 작게 유지함으로써 패키지 자체로서의 정체성을 갖게 해야한다고 한다.

7. 3개 이상의 인스턴스 변수를 가진 클래스를 쓰지 않는다.

클래스 내에서 3개 이상의 지역 변수를 갖지 않도록 하는 원칙이다. 이유는 지역 변수가 많을 수록 응집력이 떨어지기 때문이다. 이러한 원칙하에서 클래스를 작성하게 되면 결국 클래스의 종류는 두 가지만 나오게 된다.

  1. 지역변수 1개만을 가지며 이 하나의 상태를 유지하는 것

  2. 두 개의 독립된 변수를 조율하는 것

1번은 말 그대로 어떠한 목적으로 만든 클래스의 상태값 하나를 의미하는 것이고 2번은 상태값의 변화보다는 정보성 클래스를 의미하는 것으로 보인다. 책에 나와있는 예시가 괜찮아서 옮겨보자면 아래와 같다.

{
    String first;
    String second;
    String third;
}

이걸 7번 원칙에 맞게 쪼개본다면 아래와 같아진다.

class Name{
    Surname family;
    GivenNames given;
}
class Surname{
    String family;
}
class GivenNames {
    List<String> names;
}

훨씬 더 의미가 분명해지고 책임과 역할이 분배되어서 모듈화가 더 잘 되었다.

8. 일급 컬렉션을 쓴다.

콜렉션을 포함한 클래스는 반드시 다른 멤버변수가 없어야 한다는 규칙이다. 위 7번 규칙에서 GivenNames가 바로 일급콜렉션 이라고 볼 수 있다. 일급콜렉션이란 Collection을 Wrapping 하면서 해당 Collection 외에 다른 지역변수가 없는 클래스를 일급콜렉션 이라고 한다.

일급콜렉션을 쓰면 코드가 간결해지고 역할과 책임이 더 모듈화 될 수 있는 장점이 있다. 지역변수로 있는 콜렉션에 대해서 어떠한 처리를 해줘야할 때 이 지역변수를 가진 클래스가 그걸 처리하기 위해서 기능을 하나 갖기 보다는 애초에 그 지역변수가 차라리 클래스화(Wrapping)되어서 온전히 그 클래스의 책임으로 분산해주면 더 객체지향적인 코드가 되기 때문이다.

9. getter/setter/프로퍼티를 쓰지 않는다.

스택오버플로우에 실제로 이 있었다. 이 글에서 다형성에 if-else가 얼마나 해가 되는지를 알 수 있다.

하지만 이건 좀 이상하다. 왜냐면 체이닝 자체를 부정하는 것인데 체이닝이 주는 이점들이 많기 때문이다. 그래서 외국의 포스팅을 찾아봤더니 아니나 다를까 조금 더 을 발견했다.

그리고 또 일급콜렉션의 유일한 그 지역변수 하나를 final로 선언해주어서 재할당을 막는다면 데이터의 불변성까지 보장해줄 수 있다. 더 자세한건 을 참고하자.

getter, setter는 성격상 캡슐화를 방해하는 동작의 성격을 지니고 있기 때문에 쓰지 않도록 하는 원칙이다. 여기에 관해서는 예전에 내가 피드백을 받을때 리뷰어님께 직접 질문을하고 을 받았던 내용이 있다.

소트워크 앤솔로지
소트워크 앤솔로지
이와 관련된 질문
이 원칙에 대해서 상세히 정리해둔 포스팅
NEXTSTEP의 한 리뷰어 분의 포스팅
답변