티스토리 뷰

[Clean Code] 3장 함수

codeyun2 2022. 11. 22. 23:56

어떤 프로그램에서든 가장 기본적인 단위: 함수

 

안 좋은 함수

: 길이가 긺

중복된 코드

괴상한 문자열(이름)

추상화 수준이 너무 다양

 

함수를 잘 만드려면(읽고 이해하기 쉽게)

1. 작게 만들 것

- 블록과 들여쓰기

if/ else/ while 등에 들어가는 블록은 한 줄(= 함수 호출)이어야 함

: 이를 감싸는 함수가 작아질 뿐만 아니라, 내부 함수 이름을 적절히 짓는다면 코드를 이해하기 쉬워짐

 

2. 한가지만 할 것

여러 기능을 수행하는 것이 아닌 한 가지만 잘 할 것

- 함수 내 섹션

섹션이 자연스럽게 나눠진다면 여러 작업을 하는 것.

한 가지 작업만 하는 함수는 섹션으로 나누기 어려움

 

3. 함수 당 추상화 수준은 하나로

함수가 한가지 작업만 하려면(2) 함수 내 모든 추상화 수준이 동일해야 함

한 함수 내 추상화 수준을 섞으면(높고, 중간, 낮고) 헷갈리게 됨

- 근본 개념과 세부사항을 구분하기 어려움

 

내려가기 규칙

- 코드는 이야기처럼 위에서 아래로 읽혀야 좋음

- 위에서 아래로 내려갈수록 추상화 수준이 한 단계씩 낮아짐 

TO 설정 페이지와 해제 페이지를 포함하려면,

설정 페이지를 포함하고, 테스트 페이지 내용을 포함하고, 해제 페이지를 포함한다.

    TO 설정 페이지를 포함하려면, 슈트이면 슈트 설정 페이지를 포함한 후 일반 설정 페이지를 포함한다.

    TO 슈트 설정 페이지를 포함하려면, 부모 계층에서 "SuiteSetUp" 페이지를 찾아 include문과 페이지 경로를 추가한다.

    TO 부모 계층을 검색하려면, ...

 

4. Switch 문

switch(+case, if/else)문은 작게 만들기 어려움

(한가지 일만 하지 않고 N가지를 처리)

 

abstract, class, interface를 이용하여 다형성 객체 생성

 

5. 서술적인 이름을 사용할 것

"코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드" -워드

- 길고 서술적인 이름이 길고 서술적인 주석보다, 짧고 어려운 이름보다 좋음

- 서술적인 이름을 사용하면 개발자 머릿속에서도 설계가 뚜렷해져 코드를 개선하기 쉬워짐

- 이름에 일관성이 있어야 함(모듈 내에서 같은 문구, 명사, 동사 사용)

Good: includeSetupAndTeardownPages, includeSetupPages, includeSuiteSetupPage, includeSetupPage, includeTeardownPages, includeSuiteTeardownPage, includeTeardownPage

 

6. 함수 인수

- 함수 내 이상적인 인수 개수는 0개

- 많이 쓰는 단항 형식(인수 하나)

: Boolean, 인수를 변환한 결과를 반환

- 플래그 인수: 함수로 부울 값을 넘김(한번에 여러 작업을 처리하는 것은 바람직하지 않음)

- 이항 함수, 삼항 함수

: 사용하는 인수가 많을 수록 무엇을, 어느 순서로 넣어야 하는지 이해하기 어려움

- 인수 객체

: 다항 함수보다 나음(이항 함수보다 2개 요소를 가진 객체)

변수를 묶어 넘기려면 이름을 붙여야 하므로 결국 개념을 표현하게 됨

- 인수 목록

: 인수 개수가 가변적인 함수(rest 매개변수)

- 동사와 키워드

: 함수의 의도, 인수의 순서/의도를 표현하려면 좋은 함수 이름이 필수

함수와 인수가 동사/명사 쌍을 이뤄야 함, 키워드 추가(함수 이름에 인수 이름을 넣는 형태)

Good: writeField(name) assertExpectedEqualsActual(expected, actual)

 

7. 부수 효과를 일으키지 말 것

- 의도치 않게 클래스 변수/ 전달인자(argument)/ 전역 변수를 수정

- 시간적인 결합(temporal coupling)

checkPassword 함수 내부에 세션을 초기화하는 함수가 속해있음

(함수 이름만 보고 호출했다가 세션을 초기화시킬 수 있음)

> checkPasswordAndInitializeSession 이름으로 변경

- 순서 종속성(order dependency)

- 출력 인수

appendFooter(s) 바닥글에 s를 첨부하는 건지(입력) s에 바닥글을 첨부하는 건지(출력) 알 수 없다

report.appendFooter() 같은 방법으로 작성

 

8. 명령과 조회를 분리할 것

- 수행하거나, 답하거나 한 가지만 해야함

if (set("username", "unclebob")) -> 유저이름이 엉클밥으로 되어있는지 확인하는 건지, 변경하는 건지 의미가 모호함

 

// 개선. 조회와 명령을 분리
if(attributeExists("username")) { // 존재하는지 확인
  setAttribute("username", "unclebob") // 변경
  ...
}

 

9. 오류 코드보다 예외를 사용할 것

- 오류 코드를 반환하면 오류 코드를 바로 처리해야 함

- try/catch문으로 예외를 사용

- 오류 처리도 한가지 작업만 할 것(또한 이도 내부에 한줄 함수를 작성하는 것이 좋음)

 

Error.java 의존성 자석

- 오류 코드를 반환 = 오류 코드를 정의했다는 뜻

- Error enum이 변한다면 클래스 전부를 재컴파일, 재배치해야함 -> 번거로움

예외를 사용하면 Exception 클래스에서 예외 클래스를 추가할 수 있음

 

10. 반복하지 말 것(중복)

- 코드 길이가 길어지고, 알고리즘이 변하면 중복 개수만큼 수정해줘야 함

- 중복을 피하니 모듈 가독성이 높아짐

- 중복을 제어할 목적으로 나온 원칙과 기법들

: 객체 지향 프로그래밍(OOP), 구조적 프로그래밍, Aspect Oriented Programming(AOP), Component Oriented Programming(COP)

 

11. 구조적 프로그래밍

- (큰 함수)함수와 함수 내 모든 블록에 입구와 출구가 하나만 존재해야함

: return문이 하나, 루프에서 break, continue 사용하지 말고, goto도 안됨

- 작은 함수에서는 return, break, continue를 자주 사용하는 것이 의도를 표현하기 쉬울 수 있음(goto는 피할 것)

 

 

 

함수를 어떻게 작성해야할까

글짓기와 비슷

처음엔 길고 복잡, 들여쓰기, 중복 루프가 많고, 인수 목록이 길며 이름은 즉흥적이며 코드도 중복

-> 단위 테스트 케이스를 만들고

코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거, 메서드를 줄이고 순서를 바꿈

이를 진행하는 동안 항상 단위 테스트를 통과

 

처음부터 깔끔하게 작성하기는 어려움

 

프로그래밍 기술은 언어 설계의 기술임

시스템을 구현할 프로그램이 아니라 풀어갈 이야기로 보기

위 규칙들을 참고하여 길이가 짧고, 이름이 좋고, 체계가 잡힌 함수를 만들기

함수가 분명하고 정확한 언어로 깔끔하게 작성되어야 시스템을 풀어가기 쉽다.

 

 

 

 

SRP(Single Responsibility Principle)

: 단일 책임 원칙. 모든 클래스는 하나의 책임만 가지며, 클래스는 그 책임을 완전히 캡슐화해야 함

OCP(Open Closed Principle)

: 개방-폐쇄 원칙.

'소프트웨어 개체(클래스, 모듈, 함수 등등)는 확장에 대해 열려 있어야 하고,

수정에 대해서는 닫혀 있어야 한다'는 프로그래밍 원칙

 

 

 

 

22.11.22~ 22.11.24

'' 카테고리의 다른 글

[Clean Code] 6장 객체와 자료 구조  (1) 2022.11.27
[Clean Code] 5장 형식 맞추기  (0) 2022.11.26
[Clean Code] 4장 주석  (0) 2022.11.24
[Clean Code] 2장 의미 있는 이름  (0) 2022.11.21
[Clean Code] 1장 깨끗한 코드  (0) 2022.11.19
댓글
공지사항