(p.42) 함수를 만드는 첫째 규칙은 '작게!'다. 함수를 만드는 둘째 규칙은 '더 작게!'다.
함수는 100줄을 넘어서는 안 된다. 아니 20줄도 길다.
블록과 들여쓰기
다시 말해, if 문/els e문/while 문 등에 들어가는 블록은 한 줄이어야 한다는 의미다. 대게 거기서 함수를 호출한다. 그러면 바깥을 감싸는 함수(enclosing function)가 작아질 뿐 아니라, 블록 안에서 호출하는 함수 이름을 적절히 짓는다면, 코드를 이해하기도 쉬워진다.
(p.44) 함수는 한 가지를 해야 한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다.
여기서 말하는 한 가지는 추상화 수준이 하나인 단계만 수행하는 것을 의미한다.
위에서 아래로 코드 읽기 : 내려가기 규칙
한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다.
즉, 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아진다.
(p.47) switch 문은 작게 만들기 어렵다. 본질적으로 switch 문은 N가지를 처리한다.
각 switch 문을 저차원 클래스에 숨기고 절대로 반복하지 않는 방법이 있다. 물론 다형성(polymorphism)을 이용한다.
나쁜 코드 목록 3-4 Payroll.java
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);
}
}
(p.47) 목록 3-4 위 함수에는 몇가지 문제가 있다.
함수가 길다.
'한 가지' 작업만 수행하지 않는다.
SRP(Single Responsibility Principle) 단일 책임 원칙을 위반한다.
OCP(Open Closed Principle) 개방-폐쇠 원칙을 위반한다.
가장 큰 문제는 isPayday(Employee e, Date date); 혹은 deliverPay(Employee e, Money pay); 같이 구조가 _동일한 함수가 반복_하는 것이다.
변경된 코드 목록 3-5 Employee and Factory
public abstract class Employee {
public abstract boolean isPayday();
public abstract Money calculatePay();
public abstract void deliverPay(Money pay);
}
----------------
pubic 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(r);
default:
throw new InvalidEmployeeType(r.type);
}
}
}
(p.48) 이 문제를 해결한 코드가 목록 3-5다.
switch 문을 추상 팩토리(ABSTRACT FACTORY)에 꽁꽁 숨긴다. 아무에게도 보여주지 않는다.
팩토리는 switch 문을 사용해 적절한 Employee 파생 클래스의 인스턴스를 생성한다.
calculatePay, isPayday, deliverPay 등과 같은 함수는 Employee 인터페이스를 거쳐 호출된다. 그러면 다형성으로 인해 실제 파생 클래스의 함수가 실행된다.
(p.49) 일반적으로 나는 switch 문을 단 한 번만 참아준다. 다형적 객체를 생성하는 코드 안에서다. 이렇게 상속 관계를 숨긴 후에는 절대로 다른 코드에 노출하지 않는다.
(p.49) 서술적인 이름을 사용하면 개발자 머릿속에서도 설계가 뚜렷해지므로 코드를 개선하기 쉬워진다.
모듈 내에서 함수 이름은 같은 문구, 명사, 동사를 사용한다.
좋은 예. includeSetupAndTeardownPages, includeSetupPages, includeSuiteSetupPage, includeSetupPage 등
(p.50) 함수 인수
0개(무항) : 이상적인 인수 개수
1개(단항) : 그 다음으로 이상적인 인수 개수
2개(이항) : 그 다음으로 이상적인 인수 개수
3개(삼항) : 가능한 피하는 편이 좋다.
4개(다항) : 특별한 이유가 필요하다. 특별한 이유가 있어도 사용하면 안 된다.
(p.51) 이벤트 함수는 조심해서 사용한다. 이벤트라는 사실이 코드에 명확히 드러나야 한다. 그러므로 이름과 문백을 주의해서 선택한다.
(p.54) 부수 효과를 일으키지 마라!
(p.56) 명령과 조회를 분리하라!
(p.57) 오류 코드보다 예외를 사용하라.
(p.60) 반복하지 마라!
(p.61) 그러므로 함수를 작게 만든다면 간혹 return, break, continue를 여러 차례 사용해도 괜찮다. 오히려 때로는 단일 입/출구 규칙보다 의도를 표현하기 쉬워진다.
(p.61) 내가 함수를 짤 때도 마찬가지다. 처음에는 길고 복잡하다. 들여쓰기 단계도 많고 중복된 루프도 많다. 인수 목록도 아주 길다. 이름은 즉흥적이고 코드는 중복된다. 하지만 나는 그 서투른 코드를 빠짐없이 테스트하는 단위 테스트 케이스도 만든다. 그런 다음 나는 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복을 제거한다. 때로는 전체 클래스를 쪼개기도 한다. 이 와중에도 코드는 항상 단위 테스트를 통과한다.
(p.62) 함수는 그 언어에서 동사며, 클래스는 명사다. 프로그래밍 언어라는 수단을 사용해 좀 더 풍부하고 좀 더 표현력이 강한 언어를 만들어 이야기를 풀어간다.
(p.62) 여기서 설명한 규칙을 따른다면 길이가 짧고, 이름이 좋고, 체계가 잡힌 함수가 나오리라. 하지만 진짜 목표는 시스템이라는 이야기를 풀어가는 데 있다는 사실을 명심하기 바란다.
📗 오늘 읽은 소감
3장 함수 부분을 보면서 이해가지 않는 부분도 있고 이해가는 부분에서는 이때까지 내가 짰던 코드가 클린하지 않다는 사실을 새삼 깨닫게되었다. 한 가지 함수에 여러 기능들을 넣었던 기억이 너무 많다. 특히 최근 진행했던 [Cafe24toSmartstore] 프로젝트에서 함수의 첫째 규칙 "작게!"를 너무 많이 어겼다. 그래서 한 번씩 내가 짰던 코드를 한 번씩 볼 때마다 이해가 잘 가지 않거나 헷갈리는 부분들이 있다. 이번에 클린 코드 책을 읽으면서 배웠던 점들을 차근차근 적용해보면서 최대한 프로젝트의 코드들을 클린하게 만들어봐야겠다.
그나마 위안이 되었던 점은 이 책의 저자도 처음에는 길고 복잡한 코드를 쓰고 그 다음에 코드를 다듬는 작업을 한다고 한다. 조금 다르다면 처음부터 단위 테스트 케이스를 만든다는 점이다. 나도 이 책의 저자처럼 단위 테스트에 대해 따로 공부를 해서 TDD(Test Driven Development)를 적극 적용하도록 노력 해봐야겠다.
📙 궁금한 점, 잘 이해되지 않는 점
(p.45) 추상화 수준이란 말이 나오는데 추상화 수준은 무엇일까? 또한, (p.46) getHtml()은 추상화 수준이 아주 높다. String pagePathName = PathParser.render(pagepath); 는 추상화 수준이 중간이다. .append("\n")와 같은 코드는 추상화 수준이 아주 낮다. 라는 구절에서 추상화 수준 단계에 대해서도 궁금하다.
(p.47) SRP를 위반한다. OCP를 위반한다. 와 같이 객체지향프로그래밍의 5대 원칙 SOLID 중 Single Responsibility Principle과 Open Closed Principle에 관해 나오는데 SOLID에 대해 정확히 몰라 어떻게 위반했는지 잘 모르겠다.