한 가지
를 하는지 판단하는 방법
- 지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행해야 한다.
- 의미 있는 이름으로 다른 함수를 추출할 수 있다면 그 함수는 여러 작업을 하는 셈이다.
switch
문은 n가지를 처리한다. (다중 if/else 문 포함)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);
}
}
switch
문은 작게 만들기 어렵지만, 다형성을 이용하여 switch
문을 추상 팩토리
에 숨겨 다형적 객체를 생성하는 코드 안에서만 사용하도록 한다.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);
}
}
}
switch
문은 불가피하게 써야될 상황이 많으므로, 상황에 따라서는 사용 할 수도 있다.최선은 입력 인수가 없는 경우이며, 차선은 입력 인수가 1개뿐인 경우다. 그 이상은 피하는 편이 좋다. 왜냐하면 코드를 읽는 사람이 입력 인수를 이해해야 한다.
또한, 테스트 케이스 작성시에도 인수가 많을수록 테스트 조합이 부담스러워진다.
출력 인수는 입력 인수보다 이해하기 어렵다.
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;
}
}
// 중첩된 오류 처리 코드
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;
}
// try-catch 문을 이용하여 오류 처리 코드를 분리
try {
deletePage(page);
registry.deleteReference(page.name);
configKeys.deleteKey(page.name.makeKey());
} catch {
logger.log(e.getMessage());
}
try-catch
문을 별도 함수로 뽑아내는 편이 좋다.// 위 코드를 메소드로 추출
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());
}
한 가지
작업에 속한다. 그러므로 오류만 처리해야 마땅하다.break
나 continue
를 사용해선 안되며 goto
는 절대로 안된다.모든 시스템은 프로그래머가 설계한 도메인 특화 언어
로 만들어진다. 함수는 그 언어에서 동사며, 클래스는 명사다.
위에서 설명한 규칙을 따른다면 길이가 짧고, 이름이 좋고, 체계가 잡힌 함수를 만들 수 있다.