본문 바로가기
까망 동네/클린 코드

[클린코드] 함수 Function

by 까망 하르방 2022. 7. 18.
반응형

클린 코드(Clean Code)

 

클린 코드(Clean Code)란?

💻 클린 코드 (Clean Code)? • 프로그래밍을 모르는 사람도 한눈에 읽히는 코드 (가독성) • 다른 사람이 수정하기 쉬운 코드 • 한 가지 일에 집중하는 코드 • 중복이 적은 코드 • 테스트가

zoosso.tistory.com

 

✔️ 작게 만들어라

 

✔️ 한가지만 해라

    → 내부에 다른 함수로 추출할 수 있는 section이 더 있는가?

 

✔️ 서술적인 이름 사용 (동사구 활용)

    ex) isOrderable(), hasAvailableProduct

 

 

예제 코드

static string renderPageWithSetupsAndTeardowns(PageData pageData, bool isSuite) {
    bool isTestPage = pageData.hasAttribute("Test");
    
    // 테스트 페이지 확인
    if (isTestPage) {
        WikiPage testPage = pageData.getWikiPage();
        string newPageContent;
        // 설정 페이지
        includeSetupPages(testPage, newPageContent, isSuite);
        newPageContent.append(pageData.getContent());

        // 해제 페이지
        includeTeardownPages(testPage, newPageContent, isSuite);
        pageData.setContent(newPageContent);
    }

    // HTML로 렌더링
    return pageData.getHtml();
}

 

하나의 함수에서 3가지 수행

1. 테스트 페이지인지 판단

2. 설정 페이지와 해제 페이지 넣기

3. 페이지를 HTML로 렌더링

 

함수 추출이 가능한지 의심하고

함수당 3~5줄 이내로 권장

static string renderPageWithSetupsAndTeardowns(PageData pageData, bool isSuite) {
    if (isTestPage(pageData))
    {
        includeSetupAndTeardownPages(pageData, isSuite);
    }
    return pageData.getHtml();
}

 

✔️ 적절한 함수 인자 개수

 일반적으로 인자가 적을수록 좋다.

    인자 순서를 기억할 필요가 없기 때문이다.

    ex) strcpy(target, source);

    target, source 순서를 외워야 하지만

    그 마저도 뒤바뀌는 경우가 있다.

 

• 경우에 따라서는 단항/이항 함수가 좋을 수 있다.

    ex) new Point(x, y);

 

• 3개 이상은 가능하면 피하는 것이 좋다.

• 인수가 2~3개 필요하다면 일부를 클래스 변수로 대체하는 것도 좋다.

• 출력 인수가 많을 경우 객체 / pair / tuple 사용이 좋다.

 

(예제) stream 먼저? name 먼저?

writeField(stream, name); → stream.writeField(name);

 

(예제) 기대값 먼저? 실제값 먼저?

assertEquals(expected, actual); → assertExpectedEqualsActual(expected, actual)


 

✔️ 부수 효과를 일으키지 마라!

class UserValidator {
private:
    Cryptographer cryptographer;

public:
    bool checkPassword(string userName, string password) {
        UserPtr pUser = UserGateway::findByName(userName);
        if (nullptr != pUser) {
            const auto& codedPhrase = pUser->getPhraseEncodedByPassword();
            string phrase = cryptographer.decrypt(codedPhrase, password);
            if ("Valid Password" == phrase) {
                Session::init(); // 함수명과 맞지 않는 부수효과
                return true;
            }
        }
        return false;
    }
};

 

함수명 checkPassword는 "비밀번호 확인" 하는 목적이다.

내부 로직에서 세션 초기화까지 하는 것은 부수효과이다.

checkPasswordAndInitSession으로 변경하는게 좀 더 명확하다.

 

 

길고 서술적인 이름이

짧고 어려운 이름/주석보다 좋다.


 

✔️ 명령과 조회 분리

객체 상태 변경 or 객체 정보 반환 중 하나만 해야한다.

// 속성 값 설정하고 bool 타입까지 반환해서는 안된다.
bool set(string attribute, string value);

// 언뜻보면 괜찮아 보이지만 명령과 조회가 같이 있는 형태
if(set(“username”, “unclebob”))

 

 

하나의 기능을 하는 함수로 명확히 분리

if (attributeExists("username")) {
    setAttribute("username", "hyun");
}

 

✔️ 오류 코드보다 예외 사용

if (deletePage(page) == E_OK) {
        if (registry.deleteReference(page.name) == E_OK) {
            if (configKeys.deleteKey(page.name.makeKey()) == E_OK) {
                logger.log("page deleted");
            }
            else {
                logger.log("configKey not deleted");
            }
        }
        else {
            logger.log("deleteReference from registry failed");
        }
}
else {
    logger.log("delete failed");
    return E_ERROR;
}

 

아래 코드는 "명령과 조회 분리"을 위반하는 형태다.

deletePage(page) == E_OK

(오류 유형이 많아 진다면 중첩도 많아진다.)

 

 

try/catch를 사용하면 오류 처리 코드가

원래 코드에서 분리되므로 코드가 깔끔해 진다.

 

try {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
} catch (const exception& e) {
    logError(e);
}

 

 

try, catch 블록에 있는 로직을 함수로 추출하여

내려가기 규칙에 맞게 작성하는 것도 좋다.

* 오류 처리도 한가지 작업(함수)

void delete(const Page& page) {
    try {
        deletePageAndAllReferences(page);
    } catch (const exception& e) {
        logError(e);
    }
}

// 정상 동작 함수
void deletePageAndAllReferences(const Page& page) {
    deletePage(page);
    registry.deleteReference(page.name);
    configKeys.deleteKey(page.name.makeKey());
}

// 오류 동작 함수
void logError(const exception& e) {
    logger.log(e.getMessage());
}

 

 

📌 [클린코드] 형식 (Format)

 

[클린코드] 형식 (Format)

✔️ 개념은 빈 행으로 분리 글에 문단이 있듯이 코드에도 빈 행으로 구분해주면 좋다. public List getFlaggedCells() { List flaggedCells = new ArrayList(); for (Cell cell : gameBoard) if (cell.isFlagged(..

zoosso.tistory.com


✔️ Switch 문

switch 문을 작게 만들기 어렵다.

본질적으로 N가지를 처리하기 때문이다.

그렇기에 switch문을 완전히 피할 방법은 없다.

 

팩토리 메소드 패턴 (Factory Method)을 활용하거나

전략 패턴을 사용해서 switch 문을 최소화할 수 있다

 

 

✔️ Flag 인수

함수로 Bool 값을 넘기는 경우가 많다.

if (TRUE)
{
    ...
}
else // FALSE
{
    ...
}

 

함수가 여러 가지를 처리한다고 명시한 것과 마찬자지로

true/false에 따라 두 가지 함수로 나누는게 원칙상 맞다.

 

 

Q) 그렇다면 아래의 경우는 어떨까?

A) 개인적으로는 로직에 따라서 판단할 문제인 것 같다.

 

VOID setDMAEngine(bool en)
{
    DMAEngine.stateOn = en;
}

 

 

참고로, 구조적 프로그래밍 원칙에 따르면

모든 함수와 블록에 입구와 출구가 하나여야 된다.

• 함수에서 return문 1개

• loop안에서 return, break, continue 사용 X

  (함수가 충분히 작다면 어느정도 사용 O)

• goto는 절대로 사용 X

반응형

'까망 동네 > 클린 코드' 카테고리의 다른 글

[클린코드] 객체와 자료구조  (0) 2022.07.19
[클린코드] 형식 (Format)  (0) 2022.07.18
[클린코드] 주석 Comment  (0) 2022.07.18
[클린코드] 의미 있는 이름  (0) 2022.07.17
클린 코드(Clean Code)란?  (0) 2022.07.17

댓글