반응형
규칙 1. 생성자 대신 정적 팩토리 메소드를 고려해보자
정적 메소드에는 이름이 존재한다.
- 정적 팩토리 메소드에는 이름이 존재하기 때문에 생성자보다 반환될 객체의 특성을 더 잘 설명할 수 있다. 생산성을 높여주고 가독성을 높인다는 부분에서 공감한다. 대신 메소드의 이름을 잘 작명해야 하겠지?
- 아래 예시에서 reviewA 와 reviewC 는 단순히 생성자만을 활용했다. 파라미터의 개수가 작아서 관리하기 쉬울 수 있지만 파라미터 개수가 많아진다면?
- reviewB 와 reviewD 가 생성된 것처럼 정적 메소드를 활용하면 좀 더 이해하기 쉽게 표현할 수 있다.
public static void main(String[] args) {
Review reviewA = new Review("배송 굳", 5); // 생성자 기반
Review reviewB = Review.createReviewWithScore("배송 굳굳", 5); // 정적 메소드 기반
Review reviewC = new Review("배송 굳"); // 생성자 기반
Review reviewD = Review.createReviewWithZeroScore("배송 굳굳"); // 정적 메소드 기반
}
public class Review {
private final String reviewContent; // "배송이 빨라서 좋았아요"
private final int score; // 5점
private final LocalDateTime createdAt; // 2022-01-01 12:12:12
public Review(String reviewContent, int score) {
this.reviewContent = reviewContent;
this.score = score;
this.createdAt = LocalDateTime.now();
}
public Review(String reviewContent) {
this.reviewContent = reviewContent;
this.score = 0;
this.createdAt = LocalDateTime.now();
}
public static Review createReviewWithScore(String reviewContent, int score) {
return new Review(reviewContent, score);
}
public static Review createReviewWithZeroScore(String reviewContent) {
return new Review(reviewContent);
}
}
나는 아래처럼 빈 객체를 만들기 위해 정적 메소드를 많이 사용한다.
public class Review {
...
@Builder
public Review() {
....
}
public static Review empty() {
return Review.builder().build();
}
}
호출할 때마다 새로운 객체를 생성할 필요가 없다.
- 변경 불가능 클래스 또는 캐시를 두고 재사용하면 매번 새로운 객체를 만들 필요가 없다.
- 객체를 만드는 비용이 클 때, 효과적일 수 있다.
public class Review {
private static final Review EMPTY = new Review("Empty");
public Review(String content) {
this.content = content;
...
}
public static Review empty() {
return EMPTY;
}
}
public statis void main(String[] args) {
Review emptyReview = Review.empty();
}
반환값 자료형의 하위 자료형 객체를 반환할 수 있다.
- 반환되는 객체의 클래스를 유연하게 설정할 수 있다.
- public 으로 선언되지 않은 클래스의 객체를 반환할 수 있다.
- 반환 타입의 하위 타입이기만 하면 어떤 클래스의 객체를 반환하든 상관이 없다.
형인자 자료형 (parameterized type) 객체를 만들 때 편리하다
// 형인자를 연달아 두 번 사용하게 되므로 복잡한 코드가 작성됨
Map<String, List<String>> m = new HashMap<String, List<String>>();
// HashMap 클래스가 제공하는 newInstance() 제네릭 정적 팩토리 메소드
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
// 좀 더 간결하게 작성할 수 있음
Map<String, List<String>> m = HashMap.newInstance();
규칙 2. 생성자 인자가 많을 때는 Builder 패턴 적용을 고려해보자
- 점측정 생성자 패턴은 잘 동작하지만 인자 수가 늘어나면 클라이언트 코드를 작성하기 어려워지고, 무엇보다 읽기 어려운 코드가 되고 만다.
- 빌더 패턴은 인자가 많은 생성자나 정적 팩토리가 필요한 클래스를 설계할 때, 특히 대부분의 인자가 선택적 인자인 상황에 유용하다.
Lombok 라이브러리에서 제공하는 @Builder 어노테이션을 활용하면 쉽게 빌더 패턴을 적용할 수 있다.
public class A {
...
@Builder // <-- 빌더 어노테이션
public A() {
}
}
규칙 3. private 생성자나 enum 자료형은 싱글턴 패턴을 따르도록 설계하자
- 싱글턴은 객체를 하나만 만들 수 있는 클래스인데, 싱클턴 클래스는 클라이언트 테스트를 하기 어려워진다.
- 원소가 하나인 enum 자료형을 통해 싱글턴을 구현할 수 있다.
규칙 4. 객체 생성을 막을 때는 private 생성자를 사용하자
- 정적 메소드나 필드만 모아둔 클래스가 필요한 경우에는 private 생성자를 클래스에 넣어 객체 생성을 방지할 수 있다.
public class AClass {
private AClass() {
throw new AssertionError();
}
...
}
규칙 5. 불필요한 객체는 만들지 말자
- 아래처럼 매번 호출될 때마다 새로 생성할 필요가 없는 객체들은 재사용할 수 있도록 구현해야 한다.
String s = new String("string"); // x
Strng s = "string"; // o
규칙 6. 유효기간이 지난 객체 참조는 폐기하자
- Garbage collector 가 있지만 필요없는 만기 참조는 null 을 할당하여 제거할 수 있다.
- 하지만 매번 객체 사용이 끝나면 그 참조를 null 처리를 할 필요가 없다. 프로그램만 난잡해진다.
- 규범이라기 보다는 예외적인 조치가 되어야 한다.
규칙 7. 종료자 사용을 피하자
- 보통 종료자는 더 이상 참조되지 않는 객체에 할당된 공간을 garbage collector 가 정리하는 곳으로 사용된다 (자바의 try-finally)
- 종료자는 반드시 실행안될 수도 있기 때문에 중요 상태 정보는 종료자로 갱신하면 안된다.
- 종료자를 사용하면 프로그램 성능이 심각하게 떨어진다.
- 그래도 사용한다면, 종료 메소드 호출을 잊을 경우에 대비하는 안전망으로서의 역할을 수행할 수 있다. (그리고 항상 로그를 남겨두자)
반응형
'독서 정산' 카테고리의 다른 글
[독서정산] 개발자의 글쓰기 (0) | 2023.03.30 |
---|---|
[독서정산] 컨버티드: 마음을 훔치는 데이터분석의 기술 (0) | 2023.03.18 |
[독서정산] 함께 자라기 - 애자일로 가는 길 (2) | 2023.03.01 |
댓글