📘 Read
클린 코드 | 함수
category
📘 Read
이번 장에서는 함수를 잘 만드는 법을 소개할 것이다.
주로 예제가 많이 나오니 코드를 천천히 이해하는 것이 중요하다. 처음 코드에서 어떻게 변화하고 어떤 방법을 적용시켰는지 보자.
추상화 수준
public static String testableHtml(PageData pageData, boolean includeSuiteSetup) throws Exception { Wikipage wikipage = pageData.getWikiPage(); StringBuffer buffer = new StringBuffer(); if (pageData.hasAttribute("Test")) { if (includeSuiteSetup) { WikiPage suiteSetup = PageCrawlerlmpl.getlnheritedPage( SuiteResponder.SUITE_SETUP_NAME, wikiPage); if (suiteSetup != null) { wikiPagePath pagePath = suiteSetup.getPageCrawler().getFullPath(suiteSetup); String pagePathName = PathParser.render(pagePath); buffer.append("include -setup .") .append(pagePathName) .append("\n"); } } WikiPage setup = PageCrawlerlmpl.getInheritedPage("SetUp", wikiPage); if (setup != null) { WikiPagePath setupPath = wikiPage.getPageCrawler().getFullPath(setup); String setupPathName = PathParser.render(setupPath); buffer.append("!include -setup .") .append(setupPathName) .append("\n"); } } buffer.append(pageData.getContent()); if (pageData.hasAttribute("Test")) { WikiPage teardown = pageCrawlerlmpl.getInheritedPage("TearDown", wikiPage); if (teardown != null) { WikiPagePath tearDownPath = wikiPage.getPageCrawler().getFullPath(teardown); String tearDownPathName = PathParser.render(tearDownPath); buffer.append("\n") .append("!include -teardown .") .append(tearDownPathName) .append("\n"); } if (includeSuiteSetup) { WikiPage suiteTeardown = PageCrawlerlmpl.getlnheritedPage( SuiteResponder.SUITE_TEARDOWN_NAME, wikiPage ); if (suiteTeardown != null) { Wikipagepath pagePath = suiteTeardown.getPageCrawler().getFullPath (suiteTeardown); String pagePathName = PathParser.render(pagePath); buffer.append("!include -teardown .") .append(pagePathName) .append("\n"); } } } pageData.setContent(buffer.toString()); return pageData.getHtml(); }
3-1 처음 코드
이 코드를 이해하지 못할 것이다. 추상화 수준도 너무 다양하고 코드도 너무 길다.
이상한 문자열을 사용하고 이상한 함수를 호출한다.
메서드 몇 개를 추출하고 이름 변경하고 구조를 조금 변경해보면 다음과 같은 코드가 나온다.
public static String renderPageWithSetupsAndTeardowns ( PageData, pageData, boolean isSuite ) throws Exception { boolean isTestPage = pageData.hasAttribute("Test"); if (isTestPage) { WikiPage testPage = pageData.getWikiPage(); StringBuffer newPageContent = new StringBuffer(); includeSetUpPages(testPage, newPageContent, isSuite); newPageContent.append(pageData.getContent()); includeTearDownPages(testPage, newPageContent, isSuite); pageData.setContent(newPageContent.toString()); } return pageData.getHtml(); }
3-2 리펙토링 버전
추상화 수준이란 무엇일까?
말 그대로 구체적으로 풀어 쓰기보다는 추상적으로 표현되어 있다면 추상화 수준이 높은 것이고, 추상화 되어 있지 않고 직접적인 코드는 추상화 수준이 낮다고 한다.
작게 만들어라!
함수를 만드는 첫번째 규칙은 ‘작게’다. 두번째 규칙은 ‘더’ 작게다.
가로 150자, 세로 100줄을 넘어서는 안된다. 사실 20줄도 길다. 3-2 리펙토링 버전 코드도 길다. 다음과 같이 줄일 수 있다.
public static String renderPageWithSetupsAndTeardowns(PageData pageData, boolean isSuite) throws Exception { if (isTestPage(pageData)) includeSetupAndTeardownPages(pageData, isSuite); return pageData.getHtml(); }
- if/else문 while문 등에 들어가는 블록은 한 줄이어야 한다.
- 중첩 구조가 생길말큼 함수가 커지면 안된다.
한 가지만 해라!
함수는 한 가지를 해야한다. 그 한 가지를 ‘잘’ 해야 한다. 그 한 가지‘만’을 해야한다.
함수가 한가지 기능을 수행하는지 판별하는 방법은 바로 함수 이름을 바꿔보는 것이다. 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.
함수 이름 변경 → 의미 있음? → 여러 작업을 하는 함수!
함수 당 추상화 수준은 하나로!
함수가 확실히 ‘한 가지’작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야한다.