티스토리 뷰
작게 만들어라!
* 블록 (if , else, while ) 은 한 줄이어야 한다. 따라서 함수내의 들여쓰기는 1단이나 2단을 넘어가면 안된다.
한가지만 해라!
* 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지작업만 한다.
* 단순히 다른 표현이 아니라 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.
Switch 문
* Switch 문은 한가지 작업만 하도록 만들기 어렵다. 즉 Switch 문은 N가지 일을 처리한다. 다형성을 이용하면 저 차원 클래스에 숨기고 절대로 반복하지 않을 수 있다.
1 2 3 4 5 6 7 8 9 10 11 12 | public Money calculatePay(Employee e) throws InvalidEmployeeType { switch (e.type) { case COMMISSIONED: return calculateCommissionedPay(e); case HOURLY: return calcuateHorlyPay(e); case SALARIED: return calculateSalariedPay(e); default: throw new InvalidEmployeetype(e.type); } } | cs |
위 함수는 여러가지 문제점이 있다.
1. 함수가 길다.
2. 한 가지 작업만 수행하지 않는다.
3. SRP (Single Responsibility Principle) 를 위반한다.
4. OCP (Open Closed Principle) 를 위반한다.
- 새로운 직원 유형을 추가할 대 마다 코드를 변경하기 때문이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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 SalariedEmployee(e); default: throw new InvalidEmployeetype(e.type); } } } | cs |
서술적인 이름을 사용하라!
* 코드를 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행한다면 깨끗한 코드라 불러도 되겠다.
* 길고 서술적인 이름이 짧고 어려운 이름보다 좋다.
함수 인수
* 함수에서 이상적인 인수 개수는 0개이다. 그 다음은 1개 다음은 2개, 3개는 가능한 피하는 편이 좋다.
* 함수에 인수 1개를 넘기는 이유로 가장 흔한 두가지.
1. 인수에 질문을 던지는 경우 ex) boolean fileExists("MyFile")
2. 인수를 뭔가 변환해 결과를 반환한다. ex) InputStream fileOpen("MyFile")
단항 형식의 함수를 만들때는 위 두가지를 확실히 구분하여 이름을 짓는다.
* 플래그 인수는 추하다
1. 플래그를 인수로 받는 함수는 대놓고 플래그에 따라서 다른 처리를 하겠다고 공표한다.
* 이항 함수
1. Point P = new Point(0, 0) 가 좋은 예다.
-직교 좌표계 점은 일반적으로 2개의 인수를 취해야 한다. 하나의 인수를 취하는 편이 오히려 어색하다.
2. 아주 당연하게 여겨지는 이항 함수 assertEquals(expected, actual) 에도 문제가 있다.
- expected 와 actual 의 위치를 인위적으로 기억해야 한다.
3. 따라서 이항 함수는 가능한 단항 함수로 변경한다.
* 삼항 함수
1. 인수가 3개인 함수는 2개인 함수보다 훨씬 더 이해하기 어렵다.
2. 순서, 주춤, 무시로 야기되는 문제가 두 배 이상 늘어난다.
* 인수 객체
1. 인수가 2-3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성을 짚어본다.
Circle makeCircle(double x, double y, double radius) =>
Circle makeCircle(Point center, double radius)
* 인수 목록
1. 때로는 인수 개수가 가변적인 함수도 필요하다.
String.format("%s worked %.2f hours.", name, hours);
가변 인수를 모두 List 로 취급한다면 하나의 인수로 취급할 수 있다. 실제로 그렇게 하고있다.
public String format(String format, Object... args)
* 동사와 키워드
1. 단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다.
write(name) : '이름' 이 무엇이든 '쓴다' => writeField(name) : '이름'이 '필드'
부수 효과를 일으키지 마라!
* 부수 효과는 거짓말이다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class UserValidator { private Cryptographer crytographer; 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; } } | cs |
* 여기서 함수가 일으키는 부수 효과는 Session.initialize() 호출이다.
1. checkPassword 함수는 이름 그대로 암호를 확인한다.
2. 이름만 봐서는 세션을 초기화 하는지 알 수 없다.
3. 따라서 함수 이름만 보고 함수를 호출하는 사용자는 사용자를 인증하면서 기존 세션 정보를 지워버릴 수 있다.
4. 개선한다면 checkPasswordAndInitializeSession 물론 한 가지만 한다는 규칙을 위반하지만....
* 출력 인수
appendFooter(s);
- 위 함수는 이름만 보고는 어떤 일을 하는지 정확하게 알 수 없다. s 를 Footer 에 붙이는지, s에 Footer에 붙이는지...
public void appendFooter(StringBuffer report)
- 선언부를 보면 분명하진다.
report.appendFooter() 와 같이 호출하는게 명확하다.
명령과 조회를 분리하라!
* 함수는 무언가를 수행하거나 무언가를 답하거나 둘 중 하나만 해야 한다.
public boolean set(String attribute, String value);
이 함수는 attribute인 속성을 찾아 값을 value로 설정한 후 성공하면 true, 실패하면 false를 반환한다.
그로 인해 다음과 같은 괴상한 코드가 생성된다.
if (set("username", "unclebob"))...
아래와 같이 수정하면 명확하다.
if (attributeExists("username")) {
setAttribute("username", "unclebob");
....
}
오류 코드보다 예외를 사용하라!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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: } | cs |
1 2 3 4 5 6 7 8 9 | try { deletePage(page); registry.deleteReference(page.name); configKeys.deleteKey(page.name.makeKey()); } catch (Exception e) { logger.log(e.getMessage()); } | cs |
반복하지 마라!
* 중복은 코드 길이가 늘어날 뿐 아니라 알고리즘이 변하면 반복되는 곳 모두 수정해야 한다.
'JAVA > Clean Code' 카테고리의 다른 글
4장 주석 (0) | 2018.05.02 |
---|
- Total
- Today
- Yesterday
- mariada-connector
- MariaDB
- circurit breaker
- AbstractMethodError
- rate limit
- aurora
- RouteDefinition
- HashMap
- dynamodb
- ConcurrentHashMap
- DyanomoDB
- msyql-connector-java
- Lazy
- router
- spring cloud gateway
- Seperate Chaining
- N+1
- custom config data convertion
- Flux
- reative
- mariadb-connector-j
- notify()
- ResultSet
- getBoolean
- RoutePredication
- referencedColumnName
- notifyAll()
- GlobalFilter
- wait()
- reactor
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |