객체지향 프로그래밍 세계는 각자 역할 ∙ 책임을 가진 객체들이 협력하며 요청과 응답을 주고 받는 세계이다.
또한, 객체들은 상태(필드)를 가지고 행동(함수)을 통해 요청에 응답하며 협력한다.
그러므로, 행동(함수)는 객체지향 세계에서 협력을 위한 중요한 키워드이다.
그렇다면, 중요한 키워드인 행동(함수)를 깨끗한 코드로 만들려면 어떻게 해야할까❓❓
클린코드에서 제시하는 깨끗한 함수의 규칙들을 살펴보자.
함수를 만드는 첫번째 규칙은 "작게!"이다.
클린코드에서는 함수가 작을 수록 좋다는 증거는 없지만 저자의 경험을 바탕으로 작은 함수가 좋다고 말한다.
나도 작은 기능으로 세분화된 함수 일수록 읽기가 좋고 기능을 수정하기도 편하다는 것에 전적으로 동의한다❗
두번째 규칙은 "한 가지만!"이다.
함수가 "한 가지"만 하는지 판단하는 방법은 함수에서 다른 함수를 추출 할수 있는지 보는것이다.
의미 있는 이름으로 다른 함수를 추출 할 수 있다면 그 함수는 여러 작업을 하는 셈이다
예를들어, 아래의 코드를 살펴보자
public static Car makeCar(String carName, int position){
boolean hasCarName = NameValidator.hasName(carName);
if (hasCarName) {
return new Car(carName, position);
}
return null;
}
위 코드는 Car 객체를 만드는 정적 팩토리 메서드의 코드이다.
(따로 확인해보지 않은 코드이기에 문법 오류가 있을수도 있다.)
간단한 문단으로 함수의 기능을 보면 다음과 같다.
TO makeCar, 자동차 이름이 있는지 확인 한 이후에 자동차 이름이 있다면 자동차 객체를 만들어 반환한다.
위 문단을 얼핏보면 Car객체를 만든다는 한가지 일을 잘해내고 있는 것처럼 보인다.
하지만 함수를 자세히 살펴보면 makeCar함수가 자동차 이름이 있는지 검사하고, Car객체를 반환하는 기능을 하고 있다.
makeCar함수가 검증과 반환 두가지 일을 하고있다는 것이다!
그렇다면 아래와 같이 고쳐 볼수 있다.
public static Car makeCar(String carName, int position){
if (hasCarName(carName)) {
return new Car(carName, position);
}
return null;
}
위 예시와 같이 함수내부에서 다른 함수를 추출 할 수 있는지 본다면, 함수가 "한 가지"만을 잘해내고 있는지 알 수 있다.
함수 내부에서 호출되는 함수들의 추상화 수준이 각자 다르다면 안된다.
추상화 수준이 각기 다른 함수들이 하나의 함수 내부에 섞여 있으면 코드를 읽는 사람은 헷갈린다.
특정 표현이 근본 개념인지 아니면 세부사항인지 구분하기 어려워지기 때문이다.
그렇다면 추상화 수준을 일치시키는 방법은 무엇이 있을까?
위에서 아래로 읽히게 코드를 구현하는 것이다. 한 함수 다음에는 추상화 수준이 한 단계 낮은 함수가 온다. 즉, 위에서 아래로 프로그램을 읽으면 함수 추상화 수준이 한 단계씩 낮아지게 만드는 것이다.
클린코드에서는 이를 내려가기 규칙이라고 한다.
이전 포스팅에서 이야기 한것과 같이 이름은 깨끗한 코드를 위해 매우 중요하다.
이것은 함수에도 적용된다. (글 하단에 좋은 이름을 위한 규칙 링크 첨부)
함수가 하는 일을 읽는 사람에게 더 잘 표현하기위해 서술적인 이름을 써야한다!
짧게 함수의 기능을 잘표현 할 수 있다면 좋겠지만 불가능한 경우가 많다.
그렇기에 길더라도 서술적으로 함수의 기능을 잘 표현 할수 있는 이름을 만들어주어야 한다.
이름을 짓는데 시간이 많이 들더라도 괜찮다!
좋은 이름이 이후에 프로그램 유지보수 시간을 훨씬 줄여줄 것이다!
함수에서 이상적인 인수 개수는 0개다. 다음은 1개고, 다음은 2개다.
3개는 피하는 것이 좋고 4개 이상은 특별한 이유가 필요하다.
왜 함수에 인수 개수가 적은 것이 좋다고 하는 것일까❓
인수가 적을수록 함수를 이해하기 쉬워지기 때문이다.
예를들어, 자동차 이름을 입력받기위해 inputCarName함수를 호출한다고 하자.
입력을 받기위해 Scanner를 사용한다고 했을 때,
inputCarName(scanner) 보다는 inputCarName()를 호출하여 내부에서 Scanner를 사용하는게 읽는사람 입장에서 이해하기 더 쉽다.
inputCarName(scanner)은 코드를 읽는 사람이 이름입력 함수 호출 시점에서 별로 중요하지 않은 세부사항인 Scanner를 알아야 한다는 문제를 가지기도 한다.
테스트 관점에서 보아도 인수가 많은 것은 문제를 가진다.
여러가지 인수 조합으로 함수를 검증하는 테스트 케이스를 작성한다고 할 때, 인수가 많을수록 테스트해야할 조합은 부담스럽게 많아 질 수 있기 때문이다.
그러므로 최선은 입력 인수가 없는 경우 이며, 차선은 입력 인수가 1개 뿐인 경우이다.
함수를 구현할 때, 인수를 줄이기 위해 노력해보자!
함수에 인수 1개를 넘기는 이유로 흔한 경우가 몇가지 있다.
하나는 인수에 질문을 던지는 경우다. boolean fileExists("MyFile")이 좋은 예다.
다른 하나는 인수를 변환해 결과를 반환하는 경우이다. int parseStringToInt("2022")가 예이다.
또한, 단항 함수 형식이 이벤트인 경우가 있다.
위와 같은 경우가 아니라면 단항 함수는 가급적 피해야 한다!
함수에게 boolean 인수를 넘기는 것은 끔찍하다!
왜냐하면, 함수가 한번에 여러 가지를 처리한다는 것(boolean 인수가 참이면 이걸하고 거짓이면 저걸 한다는 것)을 대놓고 말하는 것이기 때문이다!!
인수가 2개인 함수는 인수가 1개인 함수보다 이해하기 어렵다. 예를 들어
writeField(name)은 writeField(outputStream, name)보다 이해하기 쉽다. 둘다 의미는 명백하지만 전자는 더 쉽게 읽히고 빠르게 이해된다. 반면, 후자는 첫 인수를 무시해야 한다는 것을 깨닫는데 시간이 걸린다. 또한 무시한 인수는 오류의 원인이 되기도 한다!
프로그래밍을 하다보면 이항 함수가 불가피하게 필요한 경우도 생기지만 최대한 지양해 보자!
인수가 3개인 함수는 인수가 2개인 함수보다 훨씬 이해하기 어렵다. 순서를 이해하고 무시해야 하는 것을 깨닫는데 더 많은 시간이 필요하고 무시를 통해 오류가 발생하는 위험성도 더 높기 때문이다.
그래서 삼항 함수를 만드는 것은 신중히 고려해야 한다!
함수의 의도나 인수의 순서와 의도를 드러내기 위해서는 좋은 함수이름이 필수이다!
또한, 단항 함수는 함수/인수가 동사/명사로 쌍을 이루어야 한다.
함수 이름을 지을때는 함수, 인수의 의도를 드러낼수 있게끔 해야 한다!
위에서 이야기한 것처럼 함수는 "한 가지만"해야 한다.
부수 효과는 한 가지만 하겠다고 약속하고 몰래 다른 짓을 하는 것이다.
그러므로 부수효과를 일으키지 않게끔 함수를 잘 구현해야한다!
함수는 무언가를 수행하거나 무언가에 답하거나 둘 중 하나의 일만 해야한다. 하지만, 둘 다 동시에 해서는 안된다.
함수는 객체 상태를 변경하거나 아니면 객체 정보를 반환하거나 둘 중 하나이다.
두가지 일을 하나의 함수가 한다면 혼란을 초래 할 수 있다!
명령/조회를 분리하도록 함수를 구현하자!
명령 함수에서 오류 코드를 반환하는 방식은 명령/조회 분리 규칙을 미묘하게 위반한다. 자칫하면 if문에서 명령을 표현식으로 사용하기 쉬운 탓이다.
또한, 오류 코드를 반환한다는 것은 클래스든 열거형 변수든 어딘가에 오류 코드를 정의한다는 것이다.
이렇게 되면 오류 코드를 사용하는 클래스는 오류 코드들과 의존 관계가 형성된다. 만약, 오류코드를 수정하게 되면 오류코드를 사용하는 클래스 전부를 다시 컴파일해야 한다. 그래서 오류코드(클래스 또는 열거형)는 변경이 어렵다.
그래서 오류 코드 보다는 예외를 사용해야한다!
예외를 사용하면, 오류 코드가 원래의 코드에서 분리 됨으로 명령/조회 분리 규칙을 지킬수 있다.
그리고, 예외를 사용하게 되면 새로운 예외를 추가해도 예외는 Exception 클래스에서 파생된다. 따라서, 재 컴파일, 재 배치 없이도 새로운 예외 클래스를 추가 할 수 있다.
오류코드보다는 예외를 사용해 함수를 구현하자!
코드의 중복은 소프트웨어에서 만악의 근원이다!
코드의 중복이 발생하는 부분을 찾아 함수로 만들어 반복을 제거해야 한다!
객체지향 세계에서 함수는 객체끼리의 협력을 위한 중요한 도구이다.
이러한 함수를 깨끗하게 잘 만드는 것은 매우 중요 할 것이다!
앞으로 코딩을 하면서 깨끗한 함수의 규칙들을 지켜나가며 코딩을 해나가기 위해 노력해야겠다!😤😤
클린 코드 - 의미 있는 이름 글 링크
https://velog.io/@gudonghee2000/%ED%81%B4%EB%A6%B0%EC%BD%94%EB%93%9C-%EC%9D%98%EB%AF%B8-%EC%9E%88%EB%8A%94-%EC%9D%B4%EB%A6%84