함수 인수
- 함수에서 이상적인 파라미터 개수는 0개. 적으면 적을 수록 좋고, 3개는 가능한 피하는 편이 좋다.
- 4개 이상은 특별한 이유가 필요하다. 특별한 이유가 있어도 사용하면 안된다.
- 테스트 관점에서도 인수가 많을 수록 어렵다. 갖가지 인수 조합으로 함수를 검증하는 테스트 케이스를 작성해야 한다.
- 인수가 3개를 넘어가면 인수마다 유효한 값으로 모든 조합을 구성해 테스트하기가 상당히 부담스러워진다.
단항 형식
- 함수에 인수를 1개 넘기는 이유로 가장 흔한 경우
- 인수에 질문을 던지는 경우
boolean fileExists("MyFile")
- 인수를 뭔가로 변환해 결과를 반환하는 경우
InputStream fileOpen("MyFile")
- 인수에 질문을 던지는 경우
이벤트
- 이벤트 함수는 입력 인수만 있고 출력 인수는 없다.
- 프로그램은 함수 호출을 이벤트로 해석해 입력 인수로 시스템 상태를 바꾼다.
passwordAttemptFailedNtimes(int attempts)
피해야하는 단항 함수
void includeSetupPageInto(StringBuffer pageText)
-> 변환 함수에서 출력 인수를 사용하면 혼란을 일으킨다. 입력인수를 변환하는 함수라면 변환 결과는 반환값으로 돌려준다.StringBuffer transform(StringBuffer in)
이StringBuffer transform(StringBuffer in)
보다 좋다.
이항 함수
- 이항 함수는 단항 함수보다 이해하기 어렵다
- 이항함수가 적절한 경우 :
Point p = new Point(0,0)
- 이항함수는 인자의 순서를 기억해야 되는 것이 불편하다.
assertEquals(Expected, actual)
삼항 함수
- 삼항 함수는 이항 함수보다 이해하기 어렵다.
assertEquals(message, expected, actual)
-> 매번 함수를 볼 때마다 주춤하게 만든다.
출력 인수
- 일반적으로 우리는 인수를 함수의 입력으로 해석한다.
명령과 조회를 분리하라
- 함수는 뭔가를 수행하거나 뭔가에 답하거나 둘 중 하나만 해야 한다. 둘 다 하면 안된다.
- 잘못된 예)
public boolean set(String attribute, String value);
: 이름이 attribute인 속성을 찾아 값을 value로 설정한 후 성공하면 true, 실패하면 false를 반환하는 함수- 사용하면
if(set("username", "Dua"))
이런 식으로 쓰게 된다 -> set이라는 단어가 모호해서 어떤 동작을 하는지 헷갈림 - ‘username을 Dua로 설정하고 성공하면 true’가 아닌, ‘username이 Dua로 설정되어 있다면’ 확인하는 코드 같아보이기도 함
- 해결책은 명령과 조회를 분리해 혼란을 애초에 뿌리뽑는 방법
1 2 3
if (attributeExists("username")){ setAttribute("username", "Dua"); }
- 사용하면
try-catch블록 뽑아내기
- try/catch블록은 코드 구조에 혼란을 일으키며, 정상 동작과 오류 처리 동작을 뒤섞는다.
- try/catch블록을 별도 함수로 뽑아내는 편이 좋다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void delete(Page page) {
try {
deletePageAndAllRefernces(page);
} catch (Exception e) {
logError(e);
}
}
private void deletePageAndAllReference(Page page) throws Exception {
deletePage(page);
registry.deleteRefernce(page.name);
configKey.deleteKey(page.name.makeKey());
}
private void logError(Exception e) {
logger.log(e.getMessage());
}
- delete함수는 모든 오류를 처리함
- 실제로 페이지를 제거하는 함수는 deletePageAndAllReference
- deletePageAndAllReference 함수는 예외를 처리하지 않음
- 정상 동작과 오류 처리 동작을 분리하면 코드를 이해하고 수정하는게 쉬워진다.
오류 처리도 한 가지 작업이다.
- 함수는 한 가지 작업만 해야 한다.
- 오류 처리도 한 가지 작업에 속한다.
- 그러므로 오류를 처리하는 함수는 오류만 처리해야 마땅하다.
Error.java 의존성 자석
- 오류코드를 반환한다 = 클래스든 열거형 변수든 어디선가 오류 코드를 정의한다는 뜻
의존성 자석
1
2
3
4
5
6
7
8
public enum Error {
OK,
INVALID,
NO_SUCH,
LOCKED,
OUT_OF_RESOURCES,
WAITING_FOR_EVENT;
}
- 다른 클래스에서 Error enum을 import해 사용
- Error enum이 변하면 Error enum을 사용하는 클래스를 전부 다시 컴파일하고 다시 배치해야 함 -> Error클래스 변경이 어려워짐
- 재컴파일/재배치가 번거롭기에 새 오류 코드를 추가하는 대신 기존 오류코드를 재사용함
- 오류코드 대신 예외를 사용하면 새 예외는 Exception 클래스에서 파생됨 -> 재컴파일/재배치 없이도 새 예외 클래스를 추가할 수 있다. =
OCP
중복은 소프트웨어에서 모든 악의 근원이다.
그 예로,
- 자료에서 중복을 제거할 목적으로 관계형 데이터베이스에 정규 형식을 만들었다.
- 객체지향 프로그래밍은 코드를 부모 클래스로 몰아 중복을 없앤다.
- 구조적 프로그래밍, AOP
Aspect Oriented Programming
, COPCommponent Oriented Programming
모두 어떤 면에서는 중복 제거 전략이다.