실습 내용 저장소: https://github.com/Hyeon9mak/student-information-system

메서드는 한 가지 일만 하도록 해야한다

“객체의 상태를 바꾸거나 정보를 반환하는 것 중 한가지만을 하도록 메서드를 디자인 해야 한다.”

즉 필드를 바꾸는 동작(setter)을 수행하면 그것만 하고, 필드를 반환하는 동작(getter)을 수행하면 그것만 해야한다. 하나의 메서드에서 두 가지 일을 모두 처리하면 객체의 복잡도가 커지고 불변성을 유지하기 어렵다.


유틸리티 메서드

인수를 전달 받아 인수에 대한 연산만 진행한 후 (필드 데이터를 건드리지 않음) 반환하는 메서드를 유틸리티 메서드 라고 부른다.

이러한 유틸리티 메서드가 반복되는 패턴을 함수형 프로그래밍 패턴이라 부른다.

단순히 유틸리티 메서드를 사용하기 위해 객체(인스턴스)를 생성하는 것은 여러 상태에 놓인 필드 데이터를 조작할 일이 없으므로 의미가 없다. 그러므로 유틸리티 메서드는 클래스 메서드(static)로 만드는 것이 좋다.


private 생성자

생성자를 private로 선언하면 클래스 내부의 코드에서만 새로운 인스턴스를 생성할 수 있다. 불필요한 인스턴스가 중복 생성되는 것을 막을 수 있을 뿐만 아니라, 팩터리 메서드를 통해 생성자가 인자를 통해 어떤 필드 데이터를 초기화 하는지까지 메서드 네이밍을 통해 밝힐 수 있다. 자세한 내용은 이펙티브 자바 아이템 1 을 참고하면 좋다.


정적 초기화 블록

생성자는 클래스의 인스턴스를 생성할 때 실행된다. 그러나 때때로 JVM이 처음 클래스를 읽어 들일 때 초기화를 진행시키기 원하는 경우가 생길 것이다. 이 때 정적(static) 초기화 블록을 이용하면 된다.

// 예시 코드
import java.util.Date;
public class St {

    static {        // 정적 초기화 블록
        long now = System.currentTimeMillis();
        then = new Date(now + 86400000);
    }

    public static Date then;

}

정적 초기화 블록은 클래스 코드 내에서 메서드나 생성자 내부가 아니라면 어디든지 위치 시킬 수 있다. 단, 주의할 점으로 정적 초기화 블록은 예외를 발생시켜서는 안된다.


클래스 변수

클래스(static) 변수는 일반적인 인스턴스 변수와 유효기간이 다르다. 인스턴스 변수는 객체가 유지되는 동안 유효하지만, 클래스 변수는 다른 코드에서 클래스를 처음 참조하면 생성되고 유지된다. 클래스 변수는 메모리 상에서 클래스 하나당 한 개씩만 유지될 수 있다.

클래스 변수는 보통 인스턴스의 개수를 추적하거나, 인스턴스를 생성하지 않고 연산을 진행하기 위해 사용하곤 한다. 이 때문에 책에서는 아래와 같은 예시로 설명을 하고 있다.

“Counter 클래스를 만들어서 생성된 인스턴스의 개수를 추적하는 것은 낭비이다.”

단순히 인스턴스 ‘개수’만을 추적하기 위해선 낭비지만, 인스턴스들을 ArrayList와 같은 Collection 자료구조에 삽입하는 형태를 취한다면 이야기가 달라진다. 바로 일급 컬렉션 형태를 가질 수 있게 되는 것이다. 일급 컬렉션 형태를 가지게 된다면 Collection 자료구조가 제공하는 Count 메서드를 통해 개수도 파악할 수 있다. 이러한 일급 컬렉션 필드를 클래스 변수로 선언하면 된다.

다른 클래스 변수와 마찬가지로, 일급 컬렉션 필드 역시 public으로 직접 접근을 허용하는 것은 절대 옳지 못하다. private 로 숨긴 후, 여러가지 기능들을 메서드 형태로 제공해서 “객체에 메세지를 보내라” 규칙까지 지키는 효과를 누리자.


클래스 변수와 클래스 메서드 사용시 주의점

클래스 메서드를 사용할 떄는 무조건 클래스 명을 기입하는 것이 좋다.

CourseSession(String department, String number, Date startDate) {
    this.department = department;
    this.number = number;
    this.startDate = startDate;
    // inrementCount(); 이렇게 사용하지 않는다!
    CourseSession.incrementCount();
}

incrementCount가 클래스 메소드임을 클래스명 기입을 통해 밝힘으로서 의도를 명확히 하는 것이다. 클래스 변수 또한 마찬가지다. 다른 클래스에서 사용할 때 클래스명을 기입하지 않으면 혼란을 초래할 수 있다.


팩터리 메서드 구현시 고려할 점

생성자는 인스턴스 생성과 동시에 필드의 값을 초기화 하는 것으로 주 된 목적을 가진다. 이 때문에 필드의 값을 초기화하는데 필요한 데이터들을 검증하는 등의 과정은 팩터리 메서드로 이동 시키고, 생성자에서는 주 목적만 철저히 수행하도록 하는 것이 어울린다.

우테코 프리코스를 진행하는 과정에서 필드 데이터 검증 메서드가 static이 된다는 것을 이상하게 여겨서 생성자 쪽에서 검증을 진행했는데, 이번 Lesson04 초반에서 배운 유틸리티 메서드의 특성을 생각하면 검증 메서드는 필드 데이터를 변환하지 않으므로 static이 맞다는 것을 새삼 느낀다.


제프의 정적 코드 규칙

“정적 코드가 필요해질 때까지 정적 코드를 사용하지 않는다.”

static에 대해 이해하기 전까진 사용하지 말라는 규칙이다. 우테코 프리코스 1주차 미션 중 static에 대한 이해가 전무한 상태로 미션을 진행하다보니 결국 static으로 떡칠 된 프로그램이 완성되게 되었던게 기억난다. 역시 까불지 말고 자바 기본서부터 읽었어야 했다. 😅


JUnit assert 메서드에 메세지 추가

assertTrue("not enough for FT status", student.isFullTime());

JUnit의 assert... 메서드들에는 메세지를 추가할 수 있는 인자 공간을 제공한다. 메세지 인자 추가를 통해 보다 더 명확한 테스트 의도를 밝힐 수 있다. 혹은 테스트 메서드 자체의 네이밍을 통해 테스트 의도를 명확히 밝힐 수 있도록 하자.


댓글남기기