작게 만들기
- 블록, 들여쓰기
- if/else문, while문 등에 들어가는 블록은 한 줄이어야 함
- 보통 여기서 함수 호출함 (감싸는 함수도 작아지고 코드 이해도 쉬워짐) - 중첩 구조 생길만큼 함수 커지면 안됨 (들여쓰기 수준은 1,2단 넘으면 안됨)
- if/else문, while문 등에 들어가는 블록은 한 줄이어야 함
하나만 하기
함수는 한 가지만을 잘 해야 함
- 지정된 함수 이름 아래에서 추상화 수준 하나인 단계만 수행 - 한 가지 작업만 하는 것
- 의미 있는 이름으로 다른 함수 추출 가능 - 그 함수는 여러 작업 하는 것
함수 당 추상화 수준 하나로 할 것
- 함수가 확실히 '한 가지' 작업만 하려면 함수 내 모든 문장의 추상화 수준이 동일해야 함
- 추상화 수준
- getHtml() - 아주 높음
- String pagePathName = PathParser.render(pagepath); - 중간
- .append("\n") - 아주 낮음
- 한 함수 내에 추상화 수준 섞으면 특정 표현이 근본 개념인지 세부사항인지 구분하기 어려움
- 위->아래 코드 읽기 : 내려가기 규칙
- 한 함수 다음에 추상화 수준 한 단계 낮은 함수 옴
-> 위-아래로 프로그램 읽으면 함수 추상화 수준이 한 번에 한 단계씩 낮아짐 - 각 함수가 다음 함수 소개하게 만들기, 각 함수가 일정한 추상화 수준 유지할 것
- 한 함수 다음에 추상화 수준 한 단계 낮은 함수 옴
Switch문
- 작게 만들기 어려움, 한 가지 작업만 하는 switch문 만들기 어려움 (본질적으로 switch문은 N가지 처리함)
- 다형성 이용 (Polymorphism) - 각 switch문을 저차원 클래스에 숨기고 절대 반복하지 않는 방법
- switch문을 추상 팩토리에 숨김
팩토리는 switch문을 사용해 적절한 Employee 파생 클래스의 인터페이스 생성
calculatePay 등과 같은 함수는 Employee 인터페이스 거쳐 호출됨
다형성으로 실제 파생 클래스의 함수가 실행됨
- switch문을 추상 팩토리에 숨김
public abstract class Employee{
public abstract boolean isPayday();
public abstract boolean calculatePay();
}
---
public interface EmployeeFactory{
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType;
}
---
public class EmployeeFactoryImpl implements EmployeeFactory{
public Employee makeEmployee(EmployeeRecord r) throws InvalidEmployeeType{
switch문
}
}
서술적인 이름 사용하기
- 코드 읽으면서 짐작했던 기능을 각 루틴이 그대로 수행하기
- 함수가 작고 단순할수록 서술적인 이름 고르기 수워짐
- 길고 서술적인 이름이 짧고 어려운 이름보다, 길고 서술적인 주석보다 좋음
- 여러 단어가 쉽게 읽히는 명명법 사용
- 여러 단어를 사용해 함수 기능을 잘 표현하는 이름 선택
- 서술적인 이름 사용 시 코드 개선이 쉬움
- 이름 붙일 때 일관성 있어야 함
- 모듈 내에서 함수 이름은 같은 문구, 명사, 동사 사용
- includesSetupAndTeardownPages, includeSetupPages, includeSuiteSetupPage, includeSetupPage 등
함수 인수
- 이상적인 인수 개수는 0개
- 다음은 1개, 다음은 2개
- 3개 이상 피하는 것이 좋음
- 4개 이상은 특별한 이유 필요함 (있어도 사용 X)
- 인수는 개념을 이해하기 어렵게 만듬
- 인수가 많으면 갖가지 인수 조합으로 함수 검증하는 테스트 케이스 작성해야 함
- 출력 인수는 입력 인수보다 이해하기 어려움
많이 쓰는 단항 형식
- 인수에 질문 던지는 경우 - boolean fileExists("MyFile")
- 인수를 뭔가로 변환해 결과 반환하는 경우 - InputStream fileOpen("MyFile")
- 이벤트 함수 - 입력 인수만 있음 (출력은 없음) - passwordAttemptFailedNtimes(int attempts)
- 입력 인수를 변환하는 함수면 변환 결과는 반환값으로 돌려주기
플래그 인수
- 끔찍함
이항 함수
- 인수가 1개인 함수보다 이해하기 어려움
- Point p = new Point(0,0) 처럼 이항 함수가 적절한 경우도 있음
- 인수 2개는 한 값을 표현하는 두 요소
- 자연적인 순서 있음
- 가능하면 단항 함수로 바꿔야 함
삼항 함수
- 인수 2개인 함수보더 훨씬 어려움 - 신중히 고려해야 함
- assertEquals(1.0,amount,.001) - 인수 3개일 가치 O (부동소수점 비교가 상대적이라는 사실 - 중요 사항)
인수 객체
- 인수가 2~3개 필요하면 일부를 독자적인 클래스 변수로 선언할 가능성 짚어 보기
인수 목록
- 인수 개수가 가변적인 함수도 필요함 - String.format
- 가변 인수 전부를 동등 취급하면 List형 인수 하나로 취급 가능
- 가변 인수 취하는 함수는 단,이,삼항 함수로 취급 ㄱㄴ (넘어서면 문제 O)
동사와 키워드
- 단항 함수는 함수와 인수가 동사/명사 쌍 이뤄야 함 - write(name), writeField(name)
- 함수 이름에 인수 이름 넣기 (함수 이름에 키워드 추가하기) - assertExpectedEqualsActual(expected, actual)
부수 효과 일으키지 말기
- 함수에서 한 가지 하겠다고 하고 다른 것 하는 것 - 시간적인 결합 / 순서 종속성 초래 가능함
- 예상치 못하게 클래스 변수 수정할 수 있음
- 인수나 전역 변수 수정
출력 인수
- appendFooter(s); 보다 report.appendFooter(); 로 호출하는 것이 좋음
함수 선언부 : public void appendFooter(StringBuffer report); - 일반적으로 출력 인수 피해야 함 - 함수에서 상태 변경해야 하면 함수가 속한 객체 상태 변경하는 방식 사용
명령과 조회 분리하기
- 수행하기와 답하기 둘 중 하나만 해야 함
- 객체 상태 변경 / 객체 정보 반환 둘 중 하나만 하기
오류 코드보다 예외 사용하기
- 명령 함수에서 오류 코드 반환하는 방식 : 명령/조회 분리 규칙을 위반함
- if문에서 명령 표현식으로 사용하기 쉬워서
- if(deletePage(page)==E_OK)
- 오류 코드 반환하면 호출자는 오류 코드 바로 처리해야 함 (문제)
- 예외 사용하면 오류 처리 코드가 원래 코드에서 분리됨 - 깔끔해짐
Try/Catch 블록 뽑아내기
- 코드 구조에 혼란 일으킴
- 정상 동작과 오류 처리 동작 섞음
- 별도 함수로 뽑아내는 것이 좋음
오류 처리도 한 가지 작업임
- 함수에 try 키워드 있으면 함수는 try로 시작해서 catch/finally로 끝나야 함
Error.java 의존성 자석
public enum Error{
OK,
INVALID,
NO_SUCH,
LOCKED,
OUT_OF_REOSURCES,
WAITING_FOR_EVENT;
}
- 다른 클래스에서 Error enum을 import해서 사용해야 함
-> enum이 변하면 이걸 사용하는 클래스 전부를 재컴파일, 재배치 해야함 - 예외를 사용하면 새 예외는 Exception 클래스에서 파생됨 -> 재컴파일, 재배치 필요없음
반복하지 말기
- 알고리즘 중복하지 말기
- 중복 없애는 것에 include 방법 사용해보기
- 중복 없애거나 제어하기 위해 나온 원칙과 기법
- 관계형 데이터베이스에 정규 형식 만듬 - 자료에서 중복 제거하려고
- 객체 지향 프로그래밍은 코드를 부모 클래스로 몰아 중복 제거함
- 구조적 프로그래밍, AOP, COP - 중복 제거 전략임
구조적 프로그래밍
- 모든 함수와 함수 내 모든 블록에 입구, 출구가 하나만 존재해야 함
- 함수는 return 문이 하나여야 함
- 루프 안에서 break, continue 사용하면 안됨
- goto 절대 안됨
- 함수가 클 때 상당한 이익 제공함
(함수 작으면 return, break, continue 사용해도 ㄱㅊ goto는 큰 함수에서만 의미 있음)
함수 짜는 방법
- 처음에는 길고 복잡하고 들여쓰기 많고 중복루프 많고 인수 목록 길고 이름 즉흥적, 코드 중복됨
(코드는 단위 테스트 통과함) - 코드 다듬고 함수 만들고 이름 바꾸고 중복 제거, 메서드 줄이고 순서 바꾸기, 전체 클래스 쪼개기
(코드는 항상 단위 테스트 통과해야함)
함수는 시스템 언어에서 동사, 클래스는 명사
시스템은 구현할 프로그래밍이 아닌 풀어갈 이야기로 여겨볼 것
시스템에서 발생하는 모든 동작 설명하는 함수계층이 언어임