250x250
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 클린코드
- @RequiredArgsConstructor
- @NoArgsConstructor
- 스택 큐 차이
- 정렬
- 클래스
- mysql
- 마크다운 테이블
- query
- 선형 리스트
- 내부 정렬
- 연결 리스트
- 자료구조
- WebClient
- 계산 검색 방식
- 배열
- 리스트
- CleanCode
- code
- 쿠키
- 코드
- 쿼리메소드
- JsonNode
- 클린
- 빅 오 표기법
- 마크다운
- java
- 트리
- 인터페이스
- @ComponentScan
Archives
- Today
- Total
Developer Cafe
클린코드 3장 함수 본문
728x90
함수를 만들 때 최대한 ‘작게!’ 만들어라.
// bad
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();
}
// good
// 되도록 한 함수당 3~5줄 이내로 줄이는 것을 권장한다.
public static String renderPageWithSetupsAndTeardowns( PageData pageData, boolean isSuite) throws Exception {
if (isTestPage(pageData))
includeSetupAndTeardownPages(pageData, isSuite);
return pageData.getHtml();
}
블록과 들여쓰기
중첩구조(if/else, while문 등)에 들어가는 블록은 한 줄이어야 한다. 각 함수 별 들여쓰기 수준이 2단을 넘어서지 않고, 각 함수가 명백하다면 함수는 더욱 읽고 이해하기 쉬워진다.
한 가지만 해라
함수는 한가지를 해야 한다. 그 한가지를 잘 해야 한다. 그 한가지만을 해야 한다.
지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 하는 것이다.
함수를 여러 섹션으로 나눌 수 있다면 그 함수는 여러작업을 하는 셈이다.
함수 당 추상화 수준은 하나로
// bad
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);
}
}
// good
public abstract class Employee {
public abstract boolean isPayday();
public abstract Money calculatePay();
public abstract void deliverPay(Money pay);
}
-----------------
public interface EmployeeFactory {
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}
-----------------
public class EmployeeFactoryImpl implements EmployeeFactory {
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType {
switch (r.type) {
case COMMISSIONED:
return new CommissionedEmployee(r) ;
case HOURLY:
return new HourlyEmployee(r);
case SALARIED:
return new SalariedEmploye(r);
default:
throw new InvalidEmployeeType(r.type);
}
}
}
서술적인 이름을 사용하라
코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다” - 워드
함수 인수
함수에서 이상적인 인수 개수는 0개(무항). 인수는 코드 이해에 방해가 되는 요소이므로 최선은 0개이고, 차선은 1개뿐인 경우이다. 출력인수(함수의 반환 값이 아닌 입력 인수로 결과를 받는 경우)는 이해하기 어려우므로 왠만하면 쓰지 않는 것이 좋겠다.
- 플래그 인수
플래그 인수는 추하다. 쓰지마라. bool 값을 넘기는 것 자체가 그 함수는 한꺼번에 여러가지 일을 처리한다고 공표하는 것과 마찬가지다. ex> render(bool isTest); - 이항 함수
단항 함수보다 이해하기가 어렵다. Point 클래스의 경우에는 이항 함수가 적절하다. 2개의 인수간의 자연적인 순서가 있어야함 Point p = new Point(x,y); 무조건 나쁜 것은 아니지만, 인수가 2개이니 만큼 이해가 어렵고 위험이 따르므로 가능하면 단항으로 바꾸는게 옳다 - 삼항 함수
이항 함수보다 이해하기가 훨씬 어려우므로, 위험도 2배 이상 늘어난다. 삼항 함수를 만들 때는 신중히 고려하라. - 인수 객체
인수가 많이 필요할 경우, 일부 인수를 독자적인 클래스 변수로 선언할 가능성을 살펴보자 x,y를 인자로 넘기는 것보다 Point를 넘기는 것이 더 낫다. - 인수 목록
때로는 String.format같은 함수들처럼 인수 개수가 가변적인 함수도 필요하다. String.format의 인수는 List형 인수이기 때문에 이항함수라고 할 수 있다. - 동사와 키워드
// 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야한다.
writeField(name);
// 함수이름에 키워드(인수 이름)을 추가하면 인수 순서를 기억할 필요가 없어진다.
assertExpectedEqualsActual(expected, actual);
부수 효과를 일으키지 마라
부수효과는 거짓말이다. 함수에서 한가지를 하겠다고 약속하고는 남몰래 다른 짓을 하는 것이므로, 한 함수에서는 딱 한가지만 수행할 것! 아래 코드에서 Session.initialize();는 함수명과는 맞지 않는 부수효과이다.
// bad
// 일반적으로 출력 인수는 피해야 한다.
// 함수에서 상태를 변경해야 한다면 함수가 속한 객체 상태를 변경하는 방식을 택하라.
public class UserValidator {
private Cryptographer cryptographer;
public boolean checkPassword(String userName, String password) {
User user = UserGateway.findByName(userName);
if (user != User.NULL) {
String codedPhrase = user.getPhraseEncodedByPassword();
String phrase = cryptographer.decrypt(codedPhrase, password);
if ("Valid Password".equals(phrase)) {
Session.initialize();
return true;
}
}
return false;
}
}
명령과 조회를 분리하라
함수는 뭔가 객체 상태를 변경하거나, 객체 정보를 반환하거나 둘 중 하나다. 둘 다 수행해서는 안 된다.
// 같은 경우에는 속성 값 설정 성공 시 true를 반환하므로 괴상한 코드가 작성된다.
public boolean set(String attribute, String value);
// 그러므로 명령과 조회를 분리해 혼란을 주지 않도록 한다.
if(set(“username”, “unclebob”))...
오류코드보다 예외를 사용하라
try/catch를 사용하면 오류 처리 코드가 원래 코드에서 분리되므로 코드가 깔끔해 진다.
// bad
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;
}
// good
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 void logError(Exception e) {
logger.log(e.getMessage());
}
반복하지 마라
중복은 모든 소프트웨어에서 모든 악의 근원이므로 늘 중복을 없애도록 노력해야한다.
728x90
'책을읽읍시다' 카테고리의 다른 글
클린코드 6장 객체와 자료 구조 (0) | 2022.09.05 |
---|---|
클린코드 5장 형식 맞추기 (0) | 2022.08.05 |
클린코드 4장 주석 (0) | 2022.08.05 |
클린코드 2장 의미있는이름 (0) | 2022.08.05 |
클린코드 1장 클린코드 (0) | 2022.08.05 |
Comments