클린 코드(Clean Code) - 3장 함수

Muzi·2023년 1월 3일
0

Clean Code

목록 보기
3/14

함수

의도를 분명히 표현하며 직관적으로 파악할 수 있도록

1. 작게 만들어라

작으면 작을수록 좋다

  • if, else, while 등에 들어가는 블록은 1줄이어야 한다.
  • 함수의 들여쓰기 수준은 2단을 넘어서면 안된다. (중첩구조를 줄이자, depth를 2까지)

2. 한 가지만 해라

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

그 한 가지란?

  • 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행하는가?
  • 함수 내 로직을 의미있는 이름으로 다른 함수를 추출할 수 있는가?
  • 함수 내 섹션이 나눠지는가? 나눠진다면 여러 작업을 한 함수에서 한다는 증거

3. 함수 당 추상화 수준은 하나로 (추상화 수준을 동일하게)

/*
- html을 가져오는 함수 추상화 수준이 매우 높다
- 우리는 getHtml()이 어떻게 진행되는지 모르고 범용적으로 사용될 수 있다
*/
getHtml() // html을 가져오는 함수 추상화 수준이 매우 높다

/*
- pagePath를 인자로 넣어줘야 실행될 수 있는 메소드
- 특정 값에서 사용할 수 있는 메소드로 추상화 수준이 중간
*/
String pagePathName = PathParser.render(pagePath);

/*
- 메소드의 범위가 매우 작고 사용도 제한적
*/
buiffer.append("\n");

4. 서술적인 이름을 사용하라

서술적이고 이해가 쉬운 긴 이름이 짧고 난해한 이름과 서술적인 주석보다 낫다

// 짧고 난해한 이름
testOrder()

// 서술적이고 이해가 쉬운 긴 이름
isOrderable()
hasAvailableProduct()

5. 적절한 함수 인자

  • 이상적인 인수 개수는 0개(무항), 그 다음은 1개, 그 다음은 2개
  • 삼항 이상은 가능한 지양하자
  • 많은 인수는 개념을 이해하기 어렵게 만든다

단항 함수

// 질문을 던지는 경우
Boolean fileExists("MyFile");
// 인자를 무언가로 변환해 결과로 반환하는 경우
InputStream fileOpen("MyFile");

// 피해야하는 경우
render(true) // 플래그 인자 - 참 거짓에 따라 두가지 일을 하기 때문 헷갈림

이항 함수

// 좌표 경우
Point p = new Point(x,y) // 자연스럽게 읽힌다

// 피해야하는 경우
writeField(outputStream, name) // 인수들이 어떻게 쓰이는지 헷갈린다
// 이렇게 바꾸자
outputStream.writeField(name) // 이름을 쓴다는 의미가 바로 이해된다

6. 부수 효과를 일으키지 마라

함수에서 한 가지 일만 한다 약속하고선 부수효과를 통해 다른 일을 해서는 안된다

public class UserValidator {
    private Cryptographer cryptographer;
    
    public boolean checkPasswd(String id, String passwd) {
    	User user = UserGateway.findById(id);
        if (user != null) {
            String codedPhrase = user.getPhraseEncodedByPasswd();
            String phrase = cryptographer.decrypt(codePhrase, passwd);
            
            if ("Valid Passwd".equals(phrase)) {
            	Session.init(); // checkPassword안에 세션을 초기화하는 부수효과가 있다
                return true;
            }
        }
        
        return false;
    }
}

7. 명령과 조회를 분리하라

함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야한다

/*
- attribute에 value를 set해서 성공하면 true 실패시 false를 리턴하는 함수
- set함수를 사용하면 아래와 같은 코드가 발생하는데 set을 한다는건지 되었다는건지 의미를 구분하기 힘들다
*/
public boolean set(String attribute, String value);
if (set("username", "effortguy")) {...}

// 명령과 조회를 분리하니 보기 좋아졌다
if (existsAttribute("username")) {
	setAttribute("username", "effortguy");
}

8. 오류 코드보다 예외를 사용하라

/*
- 명령과 조회를 분리하라는 규칙을 어김
- 여러단꼐로 중첩되는 코드를 야기한다
*/
if (deletePage(page) == E_OK) {
    if (registry.deleteRegistry(page.name) == E_OK) {
    	logger.log("Registry deleted");
    } else {
    	logger.log("delete Registry failed");
    }
} else {
    logger.log("delete failed")
    return E_ERROR;
}

/*
- try catch 사용
*/
try {
	deletePage(page);
	registry.deleteRegistry(page.name);
} catch (Exception e) {
	logger.log(e.getMessage());
}

/*
- try catch 블록내 함수 로직 분리
- 오류도 한 가지 작업
*/
public void delete(Page page) {
    try {
    	deletePageAndRegistry(page);
    } catch(Exception e) {
    	logError(e);
    }
}
 
private void deletePageAndRegistry(Page page) throw Exception {
	deletePage(page);
	registry.deleteRegistry(page.name);
}
 
private void logError(Exception e) {
	logger.log(e.getMessage());
}

9. 함수를 짜는 법

로직을 짠다 -> 테스트 코드를 짠다 -> 리팩토링을 한다

마치며

좋은 코드는 한 번에 안나온다.. 그러니 끝없는 리팩토링을 통해 발전시키자

profile
좋아하는걸 열심히

0개의 댓글