728x90
반응형
🔥해당 글은 프로젝트 팀원과 함께 프로젝트 퀄리티 및 더 나은 개발자가 되기 위하여 북 스터디를 진행하였습니다.
- 프로그램 초창기에는 시스템을 루틴과 하위 루틴으로 나눴다.
- 현재 프로그램은 가장 기본적인 단위가 함수이고, 이 장은 함수를 잘 만드는 방법을 소개한다.
작게 만들어라!
- 함수를 만드는 첫째 규칙은 작게, 둘째 규칙은 더 작게다.
- 코드의 길이가 짧을수록 이해하기 쉽다.
블록과 들여 쓰기
- if문 / else 문/ while 문 등에 들어가는 블록은 한 줄이어야 된다. 대게 함수를 호출하여 처리한다.
- 바깥은 감싸는 함수가 작아질 뿐 아니라, 블록 안에서 호출하는 함수 이름을 적절하게 짓는다면 코드를 이해하기 쉬워진다.
- 즉, 중첩 구조가 생길 만큼 함수가 커져서는 안 된다. 그래야 함수를 읽고 이해하기 쉽다.
한 가지만 해라!
함수는 한 가지를 해야 한다. 그 한 가지를 잘해야 한다. 그 한 가지만을 해야 한다.
- 한 가지
- 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
- 함수를 만드는 이유는 다음 추상화 수준에서 여러 단계로 나눠 수행하기 위해서다.
- 함수가 '한 가지'만 하는지 판단하는 방법 [함수 내 섹션]
- 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 한다.
함수 당 추상화 수준은 하나로!
- 함수가 확실하게 '한 가지' 작업만 수행하기 위해서는 함수 내 모든 문장의 추상화 수준이 동일해야 한다.
- 함수 내 추상화 수준을 섞으면 특정 표현이 근본 개념인지 세부사항인지 구분하기 어려워 코드를 읽는 사람이 헷갈린다.
- 이로 인해, 근본 개념과 세부사항이 구분이 없이 지속적으로 사용하게 되면 사람들이 함수에 세부사항을 점점 더 추가하게 된다.
위에서 아래로 코드 읽기 : 내려가기 규칙
- 규칙 ['한 가지'만 하는 함수를 위한 규칙]
- 위에서 아래로, 한 함수 다음에 추상화 수준이 한 단계 낮은 수준의 함수가 오는 게 좋다.
- 즉, 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다. 이를 내려가기 규칙이라 한다.
- 이 규칙을 지켜 코드를 구현하면 추상화 수준을 일관되게 유지하기 쉬워진다.
Swtich 문
- 본질적으로 switch 문은 N가지를 처리한다.
- 다형성을 이용하여 각 switch 문을 저 차원 클래스에 숨기고 절대로 반복하지 않는 방법이 있다. [?!]
- Switch 문은 특성상 작게 만들기 쉽지 않아서 함수를 작성할 때 작성하지 않는 게 좋다. 혹은 사용하더라도 저 차원 클래스에 숨긴다.
서술적인 이름을 사용하라
- 깨끗한 코드 [좋은 코드]
- 코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 된다.
- 한 가지만 하는 작은 함수에 좋은 이름을 붙인다면 이런 원칙을 달성함에 있어 이미 절반은 성공했다.
- 함수가 작고 단순할수록 서술적인 이름을 고르기도 쉬워진다.
- 서술적인 이름
- 이름이 길어도 서술적인 이름이 짧고 어려운 이름보다 좋다.
- 길고 서술적인 이름이 길고 서술적인 주석보다 좋다.
- 시간
- 이름을 정하느라 시간을 들여도 괜찮다.
- 서술적인 이름을 사용하면 개발자 머릿속에도 설계가 뚜렷해지고 코드를 개선하기 쉬워진다.
- 좋은 이름을 고른 후 코드를 더 좋게 재구성하는 사례도 없지 않다.
함수 인수
- 인수는 개념을 이해하기 어렵게 만든다.
- 인수가 많아질수록 인수마다 유효한 값으로 모든 조합을 구성해 테스트하기가 부담스러워진다.
- 최선은 입력 인수가 없는 경우이며, 차선은 입력 인수가 1개뿐인 경우다.
많이 쓰는 단항 형식
- 함수에 인수 1개를 넘기는 두 가지 이유
- 인수에 질문을 던지는 경우
- 다른 하나는 인수를 뭔가로 변환해 결과를 반환하는 경우
- 함수 이름을 지을 때는 두 경우를 분명히 구분합니다. 또한, 언제나 일관적인 방식으로 두 형식으로 사용한다.
- 이벤트
- 드물게 사용하지만, 유용한 단항 함수 형식인 이벤트가 있다.
- 이벤트 함수는 입력 인수만 있고, 출력 인수는 없다.
- 프로그램은 함수 호출을 이벤트로 해석해 입력 인수로 시스템 상태를 바꾼다.
- 하지만 이벤트라는 사실이 코드에 명확히 드러나야 하고, 이름과 문맥에 주의해서 선택한다.
- 드물게 사용하지만, 유용한 단항 함수 형식인 이벤트가 있다.
플래그 인수 [지양]
- 플래그 값을 넘기는 관례는 함수가 한꺼번에 여러 가지를 처리한다고 대놓고 공표한다는 얘기기 때문에 사용하지 않는 것이 좋다.
이항 함수/삼항 함수 [지양]
- 인수가 1개인 함수와 인수가 2개인 함수 둘 다 의미는 명백할 수 있지만, 1개인 함수가 더 쉽게 읽히고 더 빨리 이해된다..
- 프로그램을 구현하다 보면 불가피하게 이항 함수를 작성하게 되는 경우 있다. (이항 함수가 무조건 나쁘진 않다.)
- 하지만 그만큼 위험이 따르므로 가능하다면 단항 함수로 바꿀 수 있도록 하면 좋습니다!
- 인수가 많아질수록 함수를 이해하기 어렵다고 말한 것처럼 삼항 함수를 만들 때는 신중하게 고려하라고 권고한다.
인수 객체와 목록
- 인수가 여러 개 필요하다면 일부를 독자적인 클래스 변수로 선언할 수 있다.
- 2개 이상의 인수가 독자적인 클래스로 선언하면, 클래스로 선언한 것이 인수도 줄이고 개념을 표현하는데 더욱 적절하다고 할 수 있다.
Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);
- 가변 인수를 사용하는 함수들을 사용할 때가 있는데, 가변 인수들을 하나로 묶어 이항, 삼항 등으로 취급할 수 있다.
- 하지만 이 경우에도 인수가 늘어나는 것을 주의해야 한다.
동사와 키워드
- 함수의 의도나 인수의 순서와 의도를 제대로 표현하려면 좋은 함수 이름이 필수다.
- 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다.
- 함수 이름에 키워드를 추가하는 형식도 좋다. 즉, 함수 이름에 인수 이름을 넣는다.
- 그러면 인수 순서를 기억할 필요가 없어진다!
부수 효과를 일으키지 마라!
- 거짓말
- 부수효과가 일어나면 함수가 여러 가지 일을 하게 된다.
- 게다가 부수효과는 알게 모르게 일어나므로 코드에 큰 혼란을 초래한다.
출력 인수
- 객체지향 프로그램이 이 나오기 전에는 출력 인수가 불가피한 경우도 있었다.
- 하지만 객체 지향 언어에서는 출력 인수를 사용할 필요가 거의 없다. 출력 인수로 사용하라고 설계한 변수가 바로 this이기 때문이다.
- 일반적으로 출력 인수는 피해야 한다.
- 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택한다.
명령과 조회를 분리하라!
- 함수는 뭔가를 수행하거나, 답하거나 둘 중 하나만 해야 한다. [둘 다 하면 안 된다.]
- 객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나다. [둘 다 하면 혼란을 초래한다.]
- 해결책은 명령과 조회를 분리하는 방법이 있다.
오류 코드보다 예외를 사용하라!
- 명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 미묘하게 위반한다.
- 오류 코드를 반환하면 호출자는 오류 코드를 곧바로 처리해야 한다는 문제에 부딪힌다.
- 반면, 오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해진다.
Try/Catch 블록 뽑아내기
- try/catch 블록은 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다.
- 그러므로 try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.
- 정상 동작과 오류 처리 동작을 분리하면 코드를 이해하고 수정하기 쉬워진다.
오류 처리도 한 가지 작업이다.
- 한 가지
- 함 수는 한 가지 작업만 해야 한다. 오류 처리도 한 가지 작업에 속한다.
- 그러므로 오류를 처리하는 함수는 오류만 처리해야 마땅하다.
반복하지 마라!
- 중복된 코드
- 중복된 코드는 코드 길이가 늘어날 뿐 아니라 알고리즘이 변하면 수정할게 늘어난다.
- 중복은 소프트웨어에서 모든 악의 근원이다.
- 원칙
- 많은 원칙과 기법이 중복을 없애거나 제어할 목적으로 나왔다.
- 중복을 제거할 목적으로 관계형 데이터베이스에서 정규 형식을 만들었다.
- 객체지향 프로그래밍은 코드를 부모 클래스로 몰아 중복을 없앤다.
- 구조적 프로그래밍, AOP, COP 모두 중복 제거 전략이다.
- 루틴이 발명한 이래로 소프트웨어 개발에서 지금까지 일어난 혁신은 소스 코드에서 중복을 제거하려는 지속적인 노력으로 보인다.
구조적 프로그래밍
- 에츠허르 데이크스트라
- 데이크스트라는 모든 함수와 함수 내 모든 블록에 입구와 출구가 하남나 존재해야 한다고 말했다.
- 즉, 함수는 return 문이 하나여야 한다는 말이다.
- 루프 안에서 break나 continue를 사용해선 안 되며, goto는 절대로 사용해선 안된다.
- 구조적 프로그래밍의 목표와 규율은 좋은 내용이긴 하지만, 함수가 아주 클 때만 상당한 이익을 제공한다.
- 그러므로, 함수를 작게 만든다면 return, break, continue를 사용해도 괜찮다.
- 오히려 때로는 단일 입/출구 규칙보다 의도를 표현하기 쉬워진다.
- 반면, goto 문은 큰 함수에서만 의미가 있어서, 작은 함수에서는 피하는 것이 좋다.
함수를 어떻게 짜죠?
- 글짓기 [초안]
- 처음에는 들여 쓰기 단계도 많고, 중복된 루프도 많고, 인수 목록도 아주 길다.
- 이름은 즉흥적이고 코드는 중복된다.
- 서투른 코드를 빠짐없이 테스트하는 단위 테스트 케이스를 작성한다.
- 글짓기 [리팩토링]
- 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거한다. 메서드를 줄이고 순서도 바꾼다.
- 때로는 전체 클래스를 쪼개기도 한다. 이 와중에도 코드는 항상 단위 테스트를 통과한다.
- 최종
- 이 장에서 설명한 규칙을 따르는 함수가 얻어진다.
- 처음부터 딱 짜내는 게 가능한 사람은 없는 편이다.
결론
모든 시스템은 특정 응용 분야 시스템을 기술할 목적으로 프로그래머가 설계한 도메인 특화 언어로 만들어진다.
함수는 그 언어에서 동사며, 클래스는 명사다.
예나 지금이나 프로그래밍의 기술은 언제나 언어 설계의 기술이다.
시스템에서 발생하는 모든 동작을 설명하는 함수 계층이 바로 그 언어에 속한다.
이 장은 설명한 규칙을 따른다면 길이가 짧고, 일므이 좋고, 체계가 잡힌 함수가 나온다.
하지만, 진짜 목표는 시스템이라는 이야기를 풀어가는 데 있다는 사실을 명심해야 한다.
개발자들이 작성하는 함수가 분명하고 정확한 언어로 깔끔하게 맞아떨어져야 이야기를 풀어가기 쉬워진다는 사실을 기억해야 한다.
728x90
반응형
'📚 서적 및 학습자료 > 클린 코드' 카테고리의 다른 글
[클린 코드] Clean Code 4장. 주석 (0) | 2023.07.28 |
---|---|
[클린 코드] Clean Code 2장. 의미 있는 이름 (0) | 2023.07.07 |
[클린 코드] Clean Code 1장. 깨끗한 코드 -Gyunny (0) | 2023.04.04 |