1. 작게 만들어라.
- if/else 문 또는 while 문에 들어가는 블록은 한 줄 이면 충분하다.
- - 대부분 block 에서 함수 호출 문을 사용한다. - 중첩구조가 생길 만틈 함수 구조가 복잡해서는 안된다.
2. 한 가지만 해라!
익히 들었던 말이다. 함수는 오로지 “한 가지의 기능”만 한다.
그 한 가지만을 잘하는 도록 만드는 것의 우리의 목표이다.
🤔 여러 조건문이 필요한 경우에는 어떻게 하나요?
- 단순 다른 표현이 아니더라도, 의미있는 이름으로 지은 다른 함수를 호출/추출할 수 있다면, 그 함수는 여러 작업을 하는 셈이다.
3. 위에서 아래로 읽을 수 있도록
- Top-Down 방식
- 한 가지 의미를 갖는 함수를 만들어야 함수를 위에서 아래로 읽어 내려갈 수 있다
4. Switch문
switch문은 n가지 일을 처리하기 때문에 ‘한 가지’ 일만 작업하도록 만드는게 어렵다.
//직원 유형에 따라 다른 값을 계산해 반환하는 함수
public Money calculatePay (Employee e)
throws InvalidEmployeeType {
switch (e.type) {
case COMMISSIONED:
return calculateCommissionedPay(e);
case HOURLY:
return calculateHourlyPay(e);
case SALARIED:
return calculateSalariedPay(e);
default:
throw new InvalidEmployeeType(e.type);
}
}
문제점
1. 함수가 길다. -예를 들어 새 직원 유형이 생기면, 추가해야 한다.
2. 한가지 작업만 수행하지 않는다.
3. SRP를 위반한다.(단일 책임 원칙) -> 코드를 변경해야 할 이유가 많다.
(참고 : https://yoongrammer.tistory.com/96)
4. OCP를 위반한다. (개방 폐쇄 원칙) -> 새 직원 유형을 추가할 때마다 코드를 변경해야 한다.
(참고 : https://nesoy.github.io/articles/2018-01/OCP)
5. 서술적인 이름을 사용하라!
“코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다.” - 워드
나는 항상 이름이 긴 걸 꺼려했던 것 같다. 사람들이 이 이름이 너무 길어서 불평하면 어쩌지? 라고 걱정을 했었던 것다.
그런데, 괜히 이름 줄여서 무슨뜻인지 해석하게 만드는 것보다는 서술적으로 이름을 만들어서 이해하기 쉽도록 만드는 것이 더 중요한 것 같다.
- 대신 이름을 붙일 땐, 일관성 있게 사용할 것
- - 모듈 내 함수이름은 같은 문구, 명사, 동사를 활용한다.
6. 함수 인수
오.. 이 장은 적잖은 충격이다. 인수까지 적은게 좋다고?
인수가 들어가게 되면, 우리는 함수 뿐만 아니라 인수가 무슨 의미인지도 들여다 봐야 한다.
또한, 이 책에서 언급한 것처럼 “추상화”의 정도가 다를 수 있다.
최선은 입력 인수가 없는 경우, 차선은 입력 인수가 1개 뿐인 경우이다.
6-1. 많이 쓰는 단항 형식
🤔 인수가 1개 이상이 되는 경우는 어떤 경우일까?
- 인수에 질문을 던지는 경우
- boolean fileExists('MyFile')
- 인수로 변환해 결과를 반환하는 경우
- inputStream fileOpen('MyFile') // String 형의 파일이름을 inputStream 형으로 변환
- 이벤트 함수
- 입력 인수만 있음. 출력 인수 x
- 프로그램은 함수 호출을 이벤트로 해석한다. → 따라서 인수로 시스템 상태를 바꿈
passwordAttemptFailedNtimes(int attempts)
6-2. 플래그 인수
함수로 부울 값을 넘기는 것 ❌
ex) render(true)
대놓고 한 함수에서 여러 기능을 처리하겠다고 공표하는 것과 마찬가지이기 때문,
6-3. 이항함수
인수가 2개이면, 당연히 1개일 때보다 이해하기 어렵다.
//AS-IS
writeField(outputStream, input)
//TO-BE
public class outputStream {
public writeField (String name){}
....
}
outputStream.writeFiled(name)
6-4. 삼항 함수
6-5. 인수 객체
6-6. 인수 목록
- 인수 개수가 가변적인 함수
6-7. 동사와 키워드
7. 부수효과를 일으키지 마라!
public boolean checkPassword(String userName, String Password) {
User user = UserGateway.findByName(userName);
if(user != User.NULL){
...
if("Vaild Password".equals(phrase)){
Session.initialize();
return true;
}
}
}
checkPassword 만 보면, 패스워드를 확인하는 함수로 보인다.
그러나 이 함수에서 부수효과는 바로 Session.intialize() 이 부분이다.
세션을 초기화하는 소스코드인데, 이름만 봐서는 알 수 없다. 자칫하면 함수를 남발했다가, 세션을 초기화 할 수도 있는 것..
8. 명령과 조회를 분리하라!
- 함수의 기능 : 무언가를 수행한다. or 무언가에 답한다.
명령과 조회기능이 한 함수에 혼재되어 있으면, 각자 해석하는 의미가 달라질 수 있다.
예)
//AS-IS
if(set("username", "Tom"))
//TO-BE
if(attributeExists("username")){ //조회
setAttribute("username", "Tom") //명령
}
9. 오류코드보다는 예외를 사용하자
어떤 함수가 오류코드를 반환하면, 이 함수의 호출자는 오류 코드를 곧바로 처리한다.
이럴 때는 오류코드 대신 예외 코드를 사용하면 원래 코드에서 분리되므로, 코드 자체가 깔끔해진다.
- 이 때 try/catch 블록문은 별도 함수로 빼 두는 것이 조금 더 깔끔하겠다.
예)
AS-IS : 정상 동작과 오류 처리동작을 분리하지 않은 경우
try{
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}catch(Exception e){
logger.log(e.getMessage());
}
TO-BE : 정상 동작과 오류 처리 동작을 분리한 경우
public void delete(Page page){
try {
deletePageAndAllReferences(page);
}
catch(Exception e){
logError(e);
}
}
private void deletePageAndAllReferences(Page page) throws Exception {
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
}
private vodi logError(Exception e){
logger.log(e.getMessage());
}
10. 반복하지 마라!
결론
코드도 곧 언어이다. 함수는 그 안에서 동사이며, 클래스는 명사이다.
프로그래밍 기술은 어떻게 더 잘 언어를 풀어써나갈지 고민하는 그야말로 글쓰기 스킬이다.
내가 작성하는 함수가 분명하고 명확한 의미를 전달하며 한 편의 스토리가 짜여지듯 흐름대로 흘러갈 수 있느냐에 대해 끊임 없이 고민하는 과정이다.
처음부터 잘 할 수 없지만, 지금 작성해 둔 코드들을 하나씩 살펴보며, 중복된 반복문을 없애고, 들여쓰기를 다듬고, 이름을 이해하기 쉽게 바꾸고, 메서드를 줄이며 순서를 바꿔나가는 등 하나씩 다듬어 보는게 프로그래머의 과제가 아닐까 싶다.
'CS > 클린코드(cleancode)' 카테고리의 다른 글
10장. 클래스 - 자바스크립트 클린코드 (0) | 2022.11.20 |
---|---|
7장. 오류 처리 - 자바스크립트 클린코드 (0) | 2022.11.06 |
6장. 객체와 자료구조 (0) | 2022.11.06 |
5장. 형식 맞추기 (0) | 2022.10.30 |
2장. 의미 있는 이름 (0) | 2022.10.30 |