morijwana
수로그
morijwana
전체 방문자
오늘
어제
  • 분류 전체보기
    • 강의노트
    • Machine Learning
      • Pandas
      • NLP
    • Computer Science
      • Linux
      • TIL
    • Development
      • React
      • Swift
      • Javascript
    • 스터디 기록
      • Clean Code
      • 구글 BERT의 정석
      • 개발도서
      • 기타
    • Problem Solving
      • Baekjoon
      • ICPC Sinchon
    • 끄적
      • 끄적끄적
      • 요리왕

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • Solution Challenge
  • 회고
  • gdsc
  • word2vec
  • 프레임워크없는프론트엔드개발
  • Bert
  • 자연어처리
  • 구부정스터디
  • 구글BERT의정석
  • Pandas
  • 데이터사이언스
  • 백준
  • nlp
  • ML
  • GDSC Sookmyung
  • 개발도서
  • 프로그래밍언어론
  • 민트하임스터디
  • cs224n
  • Python

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
morijwana

수로그

스터디 기록/Clean Code

[클린 코드 Clean Code] 3장 - 함수

2022. 4. 9. 14:18

Introduction

function testTableHtml(PageData: PageData, includeSuiteSetup: boolean) {
	let wikiPage: WikiPage = pageData.getWikiPage();
	let stringBuffer: string = "";

	if (pageData.hasAttribute("Test")) {
		if (includeSuiteSetUp) {
			let suiteSetup: WikiPage = PageCrawlerImpl.getInheritedPage(
				SuiteResponder.SUITE_SETUP_NAME, wikiPage
			);
			if (suiteSetup !== null) {
				let pagePath: WikiPagePath =
					suiteSetup.getPageCrawler().getFullPath(suiteSetup);
				let pagePathName: string = PathParser.render(pagePath);
				stringBuffer = "!include -setup ." + pagePathName + "\\n";
			}
		}
		let setUp: WikiPage = PageCrawlerImpl.getInheritedPage("setUp", wikiPage);
		if (setUp !== null) {
			let setUpPath: WikiPagePath = wikiPage.getPageCrawler().getFullPath(setUp);
			let setUpPathName: string = PathParser.render(setUpPath);
			stringBuffer += ("!include -setup ." + setUpPathName + "\\n";
		}
	}
	stringBuffer += pageData.getContent();
	if (pageData.hasAttribute("Test") {
		let tearDown: WikiPage = PageCrawlerImpl.getInheritedPage("TearDown", wikiPage);
		if (tearDown !== null) {
			let tearDownPath: WikiPageDown = wikiPage.getPageCrawler().getFullPath(tearDown);
			let tearDownPathName: string = PathParser.render(tearDownPath);
			stringBuffer += ("!include -setup ." + tearDownPathName + "\\n");
		}
		if (includeSuitSetup) {
			let suiteTearDown: WikiPage = PageCrawlerImpl.getInheritedPage(
				SuiteResponder.SUITE_TEARDOWN_NAME,
				wikiPage
			);
			if (suiteTearDown !== null) {
				let pagePath: WikiPagePath = suiteTearDown.getPageCrawler().getFullPath(suiteTearDown);
				let pagePathName: string = PathParser.render(pagePath);
				stringBuffer += ("!include -setup ." + pagePathName + "\\n");
			}
		}
	}
	pateData.setContent(buffer.toString());
	return pageData.getHtml();
}
  • Java의 오픈 소스 테스트 도구 라이브러리인 FitNesse에 포함된 함수
  • 설정(setup) 페이지와 해제(teardown) 페이지를 테스트 페이지에 넣은 후, 해당 페이지를 HTML로 렌더링

 

refactoring

function renderPageWithSetUpsAndTearDowns(pageData: PageData, isSuite: boolean) {
	let isTestPage: boolean = pageData.hasAttribute("Test");
	if (isTestPage) {
		let testPage: WikiPage = pageData.getWikiPage();
		let newPageContent: string = "";
		includeSetUpPages(testPage, newPageContent, isSuite);
		newPageContent.concat(pageData.getContent());
		includeTearDownPages(testPage, newPageContent, isSuite);
		pageData.setContent(newPageContent);
	}
	return pageData.getHtml();
}
  • 리팩토링한 코드가 읽기 쉽고 이해하기 쉬운 이유는?
    • 직관적인 함수명, 변수명. isTestPage, includeSetUpPages, includeTearDownPages
    • 적절한 추상화
    • 실행 플로우를 파악하기 쉬움 - 케이스에 따른 실행될 코드와 실행되지 않을 코드

 

작게 만들어라

  • 필자의 경험에 따른 결론: 어찌됐든 작은 함수가 좋다.
  • 얼마나 작아야 좋을까?
    • 한 함수는 오직 하나의 일만 할 만큼 작아야 한다.
    • 2~4줄이 적당하다.
    • 중첩 구조가 생기지 않아야 한다.

 

한 가지만 해라

  • 한 가지만 하고, 그 한 가지를 잘 하도록 해라.
  • 이 함수가 하나의 일만 하고 있는지 어떻게 아는가?
    • 하나의 일만 하는 함수는 함수 내 코드의 추상화 수준이 하나다.
    • 하나의 일만 하는 함수는 단순히 다른 이름이 아닌, 의미 있는 다른 이름으로 다른 함수를 추출해낼 수 없다.

 

부수 효과가 없도록 해라.

  • 어떤 함수가 원래 의도와는 다른 일을 야기하는 경우다.
  • ‘한 함수는 하나의 일만 하라’는 규칙을 지키지 않았을 때 생기는 일이다.
  • 함수형 프로그래밍은 부수 효과를 일으키지 않는다.

 

switch 문을 깨끗하게 만들어보자

public Money calculatePay(Employee e) throws InvalidExployeeType {
    switch (e.type) {
        case COMMISSIONED:
            return calculateCommissionedPay(e);
        case HOURLY:
            return calculateHourlyPay(e);
        case SALARIED:
            return calculateSalariedPay(e);
        default:
            throw new InvalidExployeeType(e.type);
    }
}

리팩토링 전에는 직원 유형마다 함수를 하나씩 만들어 관리했다.

public abstract class Employee {
    public abstract boolean isPayday();
    public abstract Money calculatePay();
    public abstract void deliverPay(Money pay);
}

public interface EmployeeFactory {
    public Employee makeEmployee(EmployeedRecord r) throws InvalidEmployeeType;
}

public class EmployeeFactoryImpl implements EmployeeFactory {
    public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
        switch (r.type) {
            case COMMISSIONED:
                return new CommissionedEmployyee(r);
            case HOURLY:
                return new HourlyEmployee(r);
            case SALARIED:
                return new SalariedEmployee(r);
            default:
                thorw new InvalidEmployeeType(r.type);
        }
    }
}

리팩토링 과정에서 다음과 같은 변화가 있었다.

  • 추상 Factory에 switch문을 숨겼다.
  • 내부 switch문에서는 type 속성에 따라 Employee 추상 클래스를 상속한 자식 클래스를 생성하여 반환하도록 했다.
  • 이제 다형성을 이용하여 코드의 중복을 피하면서 각 파생 클래스에서 재정의된 메서드가 실행될 수 있다.
Employee em = makeEmployee(record);
// em의 타입은 Employee 추상 클래스를 구현한
// CommissionedEmployee, HourlyEmployee, SalariedEmployee 중 하나
em.isPayday() // 타입에 따라 재정의된 함수 호출 가능

 

서술적인 이름을 사용해라

  • 가독성을 높여 다른 사람이 코드를 이해하는 데 들이는 시간을 최소화할 수 있다.
  • 주석 없이도 함수의 역할을 짐작할 수 있도록 지어야 한다.
    • foo(), bar(), tmpFunc() - X / makeWordVectors(), cleanSession() - O
  • 이름은 서술적이면서도 일관성이 있어야 한다.
    • create_index_to_word_dict(), update_word_to_index_dict()
  • 이름 안의 여러 단어가 쉽게 읽혀야 한다.
    • Camel case, Snake case - 보통 언어에 따라 갈린다.
  • 길어도 괜찮다. (진짜?)

 

함수의 인수는 적을수록 좋다

  • 인수가 많으면 어떻게 되는가?
    • 개념을 이해하기 어렵다.
    • 인수가 들어가야 할 순서를 외워야 한다.
    • 모든 경우를 테스트하기 어렵다.
  • 인수는 아예 받지 않거나, 받아도 1개만 받는 것이 바람직하다.
  • 궁금증: 인수가 없거나 적으면 함수를 재사용하기 어렵지 않을까?
    • 애초에 한 가지 일만 잘 하는 함수들은 복잡하지 않아서 인수를 많이 받지 않아도 되겠다.
  • 2개 이상의 인수를 받는 함수들은 클래스의 구성 메서드로 만들어 사용할 수 있는 가능성이 있다.
저작자표시 (새창열림)
    morijwana
    morijwana
    행복한 휴학생의 devlog

    티스토리툴바