짧으면서도 '한 가지'만 하는 함수
함수를 만드는 첫 번째 규칙은 '작게!'다!. 함수를 만드는 둘째 규칙은 '더 작게!'다.
각 함수가 이야기 하나를 표현할 수 있게 작아야 함
함수는 한 가지를 해야한다. 그 한 가지를 잘 해야 한다. 그 한 가지만을 해야 한다
함수가 한 가지만 하는가?
한 가지 작업만 하는 함수는 자연스럽게 섹션으로 나누기가 어렵다.
함수가 확실히 '한 가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일 해야 한다.
핵심 : 짧으면서도 '한 가지'만 하는 함수
본질적으로 switch문은 n가지를 처리한다. 그렇기 때문에 다형성을 이용해서 저차원 클래스에 숨겨 반복하지 않도록 한다.
좋은 이름 : 함수가 하는 일을 좀 더 잘 표현하는 이름
서술적인 이름을 사용하면 개발자 머릿속에서도 설계가 뚜렷해지므로 코드를 개선하기가 쉬워짐
함수 이름을 정하는 방법
함수에서 이상적인 인수 개수
0개(무항) > 1개(단항) > 2개(이항) > 3개(삼항) > 4개(다항)
인수는 개념을 이해하기 어렵게 만든다.
최선은 입력 인수가 없는 경우이며, 차선은 입력 이눗가 1개뿐인 경우다.
함수에 인수 1개를 넘기는 경우
함수로 부울 값을 넘기는 것은 그 함수가 한꺼번에 여러 가지를 처리한다는 것을 말하는 것이므로 지양한다.
가능하면 단항 함수로 바꾸도록 애써야 한다.
이항 함수가 적절한 경우도 있다.
Point p = new Point(0, 0);
인수 2개는 한 값으 표현하는 두 요소이며 두 요소 사이에는 x좌표 다음에 y좌표라는 자연적인 순서도 있다.
assertEquals(expected, actual)
두 인수는 자연적인 순서가 없다. expected 다음에 actual이 온다는 순서를 인위적으로 기억해야 한다.
함수를 볼 때 주춤하는 경우가 생긴다. 그렇기 때문에 삼항 함수를 만들 때는 신중이 고려해야한다.
인수가 2-3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성을 짚어본다.
Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);
인수 개수가 가변적인 함수도 필요하다. 가변 인수를 취하는 함수는 단한, 이항, 삼항 함수로 취급할 수 있다. 하지만 이를 넘어서는 인수를 사용할 경우에는 문제가 있다.
ex) String.format
함수의 의도나 인수의 순서와 의도를 제대로 표현하려면 좋은 함수 이름이 필수다.
함수에서 한 가지를 하겠다고 약속하고 함수 이름과는 상관없는 다른 일을 하는 경우가 있다. 이를 지양해야한다.
일반적으로 인수를 함수 입력으로 해석한다. 객체 지향 언어에서는 출력 인수를 사용할 필요가 거의 없다.
일반적으로 출력 인수는 피해야 한다.
함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다.
if (set("username", "unclebob"))...
"username"이 "unclebob"으로 설정되어 있는지 확인하는 코드인지, "username"을 "unclebob"으로 설정하는 코드인지 분간이 안가는 문제가 발생한다.
해결책 : 명령과 조회를 분리해서 코드를 작성한다.
if(attributeExists("username")) {
setAttribute("username", "unclebob");
...
}
오류 코드를 반환하면 여러 단계로 중첩되는 코드를 야기한다. 그리고 오류 코드를 곧바로 처리해야 하는 문제에 부딪힌다.
반면 오류 코드 대신 예외를 사용하면 오류 처리 코드가 원래 코드에서 분리 되기 때문에 코드가 깔끔해진다.
try/catch 블록은 정상 동작과 오류 처리 동작을 뒤섞는다. 그러므로 try/catch 블록을 별도 함수로 뽑아내는 편이 좋다.
함수는 한 가지 작업만 해야 한다. 오류 처리도 한 가지 작업에 속한다. 그러므로 오류를 처리하는 함수는 오류만 처리해야 한다.
오류 코드를 사용하게 되면 오류를 정의 해야 한다. 오류를 정의한 부분이 변경이 되면 그 부분을 사용하는 클래스 전부를 다시 컴파일 하고 다시 배치해야 하는 문제가 생기게 된다.
오류 코드 대신 예외를 사용하게 되면 새 예외는 Exception클래스에서 파생 되기 때문에 재컴파일/재배치 없이도 새 예외 클래스에 추가할 수 있다.
중복은 문제다
중복을 없애면 모듈 가독성이 크게 높아진다. 따라서 중복을 제거하기 위해 노력해야 한다.
데이크스트라 : 모든 함수와 함수 내 모든 블록에 입구와 출구가 하나만 존재해야 한다. 함수는 return문이 하나여야 한다
함수가 작다면 별 이익을 제공하지 못하나 함수가 아주 클 때는 상당한 이익을 제공한다.
"처음에는 길고 복잡하다. ... 하지만 나는 그 서투른 코드를 빠짐없이 테스트하는 단위 테스트 케이스도 만든다. 그런 다음 나는 코드를 다듬고, 함수를 만들고, 이름을 바꾸고, 중복으 제거한다. 메서드를 줄이고 순서를 바꾼다. 최종적으로 이 장에서 설명한 규칙을 따르는 함수가 얻어진다."
"처음부터 탁 짜내지 않는다. 그게 가능한 사람은 없으리라."
대가 프로그래머는 시스템을 '구현할' 프로그램이 아니라 '풀어갈' 이야기로 여긴다. 프로그래밍 언어라는 수단을 사용해 좀 더 풍부하고 좀 더 표현력이 강한 언어를 만들어 이야기를 풀어간다.
코드를 짜다보면 대부분 함수를 짜는데에 시간을 모두 보냈던 것 같다. 함수가 프로그램을 동작하게 하는 거니까. 그래서 이 챕터를 보면서 함수를 짜는게 참 어렵다는 생각이 많이 들었던 것 같다. '이걸 어떻게 하지?'라는 생각이 들어서 자신감이 조금 떨어졌었는데 마지막 결론 부분을 읽고 다시 함수를 짜는 것에 대한 기대감이 생겼다.
"진짜 목표는 시스템이라는 이야기를 풀어가는 데 있다는 사실을 명심하기 바란다. 여러분이 작성하는 함수가 분명하고 정확한 언어로 깔끔하게 같이 맞아떨어져야 이야기를 풀어가기가 쉬워진다는 사실을 기억하기 바란다."
프로그래밍 언어를 통해 함수를 만들고 그 함수를 통해 전체적인 프로그램의 이야기가 풀어나간다고 생각하니까 마음이 조금 가벼워졌다. 글도 계속 고치고 고치고 고치다보면 더 깔끔하고 좋은 글이 되는 것처럼. 내가 짜는 함수도 전체적인 프로그램도 고치고 고치고 고치다보면 더 깔끔하고 좋은 프로그램이 될 것 같다.
그 과정에서 '더 좋은' '더 깔끔한' 함수에 초점을 맞추고 집착하기 보다는 '어떻게 짜야 더 전체적인 이야기를 쉽게 볼 수 있을까?'에 초점을 맞추면 좋을 것 같다는 생각이 든다.