스터디/CleanCode

[CleanCode] Chapter03 함수

냠냠쿠 2023. 11. 3. 08:32
728x90

📖 책 내용 정리

🔶 3장 함수

1️⃣ 작게 만들어라

  • 어떤 프로그램이든 가장 기본적인 단위가 함수이다.
  • if문, else문, while문 등에 들어가는 블록은 한줄이어야한다.
    (함수의 들여쓰기 수준은 1단이나 2단을 넘어서면 안 된다.)

2️⃣ 한가지만해라

  • 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
  • 단순히 다른 표현이 아니라 의미있는 이름으로 다른 함수를 추출 할 수 있다면 그 함수는 여러가지 작업을 하는 셈이다.
  • 한 가지 작업만 하는 함수는 자연스럽게 섹션으로 나누기 어렵다.

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

  • 함수가 확실히 한가지 작업만 수행하려면 함수 내 모든 문장의 추상화 수준이 동일 해야한다.
    → 근본 개념인지 세부사항인지 구분할 수 있도록.
◾ 위에서 아래로 코드 읽기 : 내려가기 규칙
  • 위에서 아래로 읽으면서 함수 추상화 수준이 한번에 한 단계씩 낮아진다.
  • 각 함수가 다음 함수를 소개하듯이 작성하며 일정한 추상화 수준을 유지한다.
TO 설정 페이지와 해제 페이지를 포함하려면 A를 포함하고 B를 포함하고 C를 포함한다.
    A를 포함하려면 A-1를 포함한 후 A-2를 포함한다.
    A-1을 포함하려면 A-1-1을 포함한다
    ...

4️⃣ Switch문

  • 본질적으로 switch문은 N가지를 처리한다.
  • 다형성을 이용해 switch문을 추상팩토리에 숨기고 절대로 반복하지 않는 방법을 사용한다.
public abstract class school {
    public abstract string teacher();
    public abstract string student();
    public abstract string classRoom();
}

public interface ISchool {
    public school newSchool(schoolRecord r) throws InvalidSchoolType;
}

public class ISchoolImpl implements ISchool {
    public School newSchool(schoolRecord r) throws InvalidSchoolType{
        switch(r.Type){
            case ...
    }
}

5️⃣ 서술적인 이름을 사용하라

  • 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
  • 서술적인 이름을 사용하면 개발자 머릿속에도 설계가 뚜렷해지므로 코드를 개선하기 쉬워진다.
  • 이름을 붙일 때에는 일관성이 있어야 한다.
  • 모듈 내 함수이름은 같은 문구, 명사, 동사를 사용한다.

6️⃣ 함수인수

  • 인수 개수가 3개 이상인 경우는 피하는 것이 좋다.
    예) includSetupPageInto(new PageContent) 보다 includeSetupPage()가 더 좋다.
◾ 많이 쓰는 단항 형식
  • 함수에 인수를 한개를 넘기는 경우는 아래 경우가 아니라면 가급적 피한다.
    1. 인수에게 질문을 던지는 경우
    2. 인수로 뭔가를 반환하는 경우 이다.
    3. 이벤트로 해석하여 입력 인수로 시스템 상태를 바꾼다.

그리고 위의 경우 함수 이름을 분명히하여 혼선이 없도록 주의 해야 한다.

◾ 플래그 인수
  • 함수가 한꺼번에 여러가지를 처리하는 대표적인 예이다.
◾ 이항 함수
  • 단항 함수보다 이해하기 어렵다. 하지만 Point p = new Point(0, 0) 과 같이 좌표계 점과 같은 경우는 인수를 2개 쓰는 것이 더 적절할 때도 있다.
  • 이항함수는 단항함수로 바꾸어쓸 수 있도록 노력해야한다.
◾ 삼항 함수
  • assertEquals(1.0, amount, .0001)과 같이 최대한 신중하게 고려하여 함수를 생성해야한다.
◾ 인수 객체
  • 인수가 2~3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성을 짚어봐야한다.
◾ 인수 목록
  • 인수 개수가 가변적인 함수가 필요한데, String.format 메서드가 좋은 예미여, String.format은 사실상 이항 함수다.
    예) String.format("%s worked %.2f hours", name, hours)
◾ 동사와 키워드
  • 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다
    예) setName
  • 함수이름에 키워드(인수 이름)를 추가한다.(인수 순서를 기억할 필요가 없어진다.)
    예) assertExpectedEqulsActual(expected,actual)

7️⃣ 부수효과를 일으키지 마라

  • 클래스 변수를 수정하거나 함수로 넘어온 인수나 시스템 전역 변수를 수정하지 않도록 한다.
public boolean checkPwd(String userName, String password){
    ...
    if("Valid Password".equals(phrase)){
        session.initialize();
    }
    ...
}

예를들어 위의 같은 함수 이름을 통해 패스워드를 체크한다는 것은 알 수 있으나, session.initialize() (세선초기화)를 한다는 사실이 드러나지 않는다. 만약 결합이 필요하다면 함수 이름에서 드러나도록 수정 해 준다.

◾ 출력 인수
  • 일반적으로 인수를 함수 입력으로 해석한다. 그리고 출력 인수는 피해야한다.
  • 부득이하게 출력 인수를 사용하는 경우, 이 함수가 하는 일과 동시에 출력 인수임을 밝혀준다.
    예) report.appendFooter()

8️⃣ 명령과 조회를 분리하라

  • 명령과 조회를 섞으면 (ex. 이름이 A인 속성을 찾아 value로 설정 한 후 성공하면 true 실패하면 false 반환 등) 혼란을 초래한다.

9️⃣ 오류 코드보다 예외를 사용하라

  • 명령 함수에서 오류코드를 반환하면 자칫 if문에서 명령을 표현식으로 사용하기 쉬워진다.
  • 위의 경우 여러단계로 중첩되는 코드를 야기한다. 오류코드를 반환하면 호출자는 오류코드를 곧바로 처리해야한다는 문제에 부딪힌다.
  • 예외를 사용하면 오류 처리 코드가 원래의 코드에서 분리되어 코드가 깔끔해진다.
◾ Try/Catch 블록 뽑아내기 - try/catch블록은 코드 구조에 혼란을 일으키고 정상 동작과 오류 처리 동작을 뒤섞기 때문에 별도의 함수로 뽑아낸다
public void delete (Page page){
    try { 
    deletePage(page);
    } 
    catch (Exception e){
        logError(e);
    }
}

private void deketePage(Page page) throws Exception{
    deletePage(page);
    ...
}
◾ 오류 처리도 한 가지 작업이다.
  • 오류를 처리하는 함수는 오류만 처리 해야한다.
◾ Error.java 의존성 자석
  • 오류코드를 반환한다는 이야기는 클래스든, 열거형 변수든 어디선가 오류코드를 정의한다는 뜻
  • 아래와 같은 코드가 의존성 자것ㄱ인데, Error enum이 변하면 Error enum을 사용하는 클래스 전부를 다시 컴파일하고 배치해야하기 때문에 error 클래스 변경이 어려워진다.
    오류코드 대신 예외를 사용하면 재컴파일/재배치 없이도 새 예외 클래스를 추가 할 수 있다.
  • public enum Error { 404, 505, ... }

1️⃣0️⃣ 반복하지마라

  • 다른 코드와 섞이면서 모양새가 조금씩 달라진 탓에 중복이 금방 드러나지 않더라도 중복은 문제다.

1️⃣1️⃣ 구조적 프로그래밍

  • 데이크스트라 : 모든 함수와 함수 내 모든 블록에 입구와 출구가 하나만 존재해야함.
    즉, 함수는 return 문이 하나여야한다.
  • 루프안에서 break, continue, goto는 절대로 사용하면 안되지만,
    함수를 작게 만든다면 간혹 return, break, continue를 여러번 사용해도 괜찮다. 하지만 goto는 큰 함수에서만 의미가 있으므로 작은 함수에서는 피해야한다.

1️⃣2️⃣ 함수를 어떻게 짜죠?

  • 처음에는 길고 복잡하지만 빠짐없이 테스트하는 단위 테스트 케이스로 만들고, 코드를 다듬고 함수를 만들고 이름을 바꾸고 중복을 제거한다.
  • 메서드를 줄이고, 순서를 바꾸고, 전체 클래스를 쪼개기도한다.
  • 최종적으로는 설명한 규칙을 따르는 함수가 얻어진다.

1️⃣3️⃣ 결론

  • 모든 시스템은 프로그래머가 설계한 도메인 특화 헌어로 만들어진다.
    그 안에서 함수는 동사고, 클래스는 명사다.
  • 함수가 분명하고 정확한 언어로 깔끔하게 맞아떨어져야 시스템이라는 이야기를 풀어나가기 쉬워진다.

💌 책에서 기억하고 싶은 내용

부수효과를 일으키지마라

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

함수는 한 가지를 잘 해야한다. 그 한 가지를 잘 해야한다.

오류도 한 가지 작업이다.

💬 소감

이번 내용은 정말 어려웠던 점이 많았던 것 같다.
사실 코드를 짜다보면, 기능을 만드는 순서대로 줄줄 써지다보니 코드의 순서로 인해 가독성이 달라진다는 부분을 생각도 하지 못했다.
부수효과를 일으키지마라라는 부분이 무슨말을 하는거지 ? 했는데 코드를 보니 단박에 이해가 갔다.
C#을 이용하여 코드를 짰을때, 저랬던 적이 있기 때문에 찔리기도 했다.
나는 이 과정을 거치면 당연한 과정이지라고 생각했지만 다른 개발자가 읽을 때에는 미처 생각하지 못한 부분이라 오류를 발생할 수 있을 것이라고는 생각지도 못했다.
책을 읽으면서 switch 문을 작게 만드는 방법과 try/catch문을 뽑아내는 부분은 새롭게 알게 된 부분이라 다음에 실무에서 꼭 적용 해 보고 싶다는 생각을 했다.

💥 이해 안 가는 부분

  • 인수 객체(53p), 출력 인수(56p)는 이해가 잘 가지 않았다.
    출력 인수는 잘 쓸 일이 없지만, 인수 객체에 관해서는 따로 공부 해 볼 생각이다.
728x90