July 27, 2022
어떤 프로그램이든 가장 기본적인 단위가 함수다.
함수는 한 가지를 해야 한다. 그 한가지를 잘 해야 한다. 그 한가지만을 해야 한다.
지정된 함수 이름 아래에서 추상화 수준이 하나인 단계만 수행한다면 그 함수는 한 가지 작업만 한다.
위에서 아래로 코드 읽기 : 내려가기 규칙
다형성을 이용하여 사용한다.
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);
}
}
}
인수에 질문을 던지는 경우
boolean fileExists("MyFile")
인수를 뭔가로 반환해 결과를 반환하는 경우
InputStream fileOpen("MyFile")
이벤트 함수
passwordAttemptFailedNtimes(int attempts)
위 세가지 경우가 아니라면 단항 함수는 가급적 피한다.
이항 함수가 적절한 경우도 있다.
Point p = new Point(0, 0)
인수가 2-3개 필요하다면 일부를 독자적인 클래스 변수로 선언할 가능성을 짚어본다.
Circle makeCircle(double x, double y, double radius);
Circle makeCircle(Point center, double radius);
단항 함수는 함수와 인수가 동사/명사 쌍을 이뤄야 한다.
writeFiled(name)
함수 이름에 키워드(인수 이름)을 추가한다. 인수 순서를 기억할 필요가 없어진다.
assertExpectedEqualsActual(expected, actual)
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;
}
}
Session.Initialize()
호출은 함수명과 맞지 않는 부수효과이다.checkPassword
함수는 세션을 초기화해도 괜찮은 특정 상황에서만 호출이 가능하다. 객체 지향 언어에서는 출력 인수를 사용할 필요가 거의 없다.
이전 방식
public void appendFooter(StringBuffer report)
권장 방식
report.appendFooter()
함수는 객체 상태를 변경하거나, 객체 정보를 반환하거나 둘 중 하나다. 둘 다 하면 혼란을 초래한다.
public boolean set(String attribute, String value)
명령과 조회를 분리하여 혼란을 주지 않도록 해야한다.
if (attributeExists("username"))
{
setAttribute("username","unclebob");
...
}
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());
}
다익스트라의 구조적 프로그래밍 원칙