일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- batch insert
- 동시성
- AWS
- jdbc
- Cache
- @RequestMapping
- 이펙티브 자바
- @Transaction(readOnly=true)
- 정적 팩터리 메서드
- oauth2.0
- Git
- awspring
- spring
- mockito
- fetch join
- Convention
- MySQLTransactionRollbackException
- injellij
- spring-cloud-starter-aws
- 데드락
- @controller
- 성능테스트
- JPA
- Cannotacquirelockexception
- assert
- OIDC
- Batch
- ngrinder
- Hibernate
- N + 1
- Today
- Total
정리정리
정적 팩토리 메서드 장점 본문
정적 팩터리 메서드란?
정적 팩터리 메서드란 생성자를 static 메서드로 감싸 해당 클래스의 인스턴스를 반환하는 메서드입니다.
자바의 boxed class나 collections에서도 많이 볼 수 있죠.
그렇다면 정적 팩터리 메서드에 어떤 장점이 있는지 이펙티브 자바 책을 기반으로 알아보겠습니다.
정적 팩터리 메서드 장점
1. 생성자가 이름을 가질 수 있음
클린코드 책의 초반부부터 강조하는 내용이 바로 '이름'입니다. 잘 지어진 이름을 통해 클라이언트는 원하는 동작을 기대하고 함수나 메서드를 호출을 합니다.
하지만 생성자는 그 자체만으로 어떤 특징을 가진 인스턴스를 생성하는지 알기 힘듭니다. 특히나 생성자의 파라미터가 늘어나고, 생성자의 종류가 많아질수록 언제 어떤 생성자를 사용해야 할지 알기 힘들어지겠죠.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
...
//OAuth 유저
public Member(String socialId, String userName, String nickname, String email) {
...
}
//일반 유저
public Member(String userName, String nickname, String email, String password) {
...
}
}
이와 같이 Member를 만드는 생성자들이 있다고 가정을 해보겠습니다.
이 Member 클래스를 사용하는 클라이언트 입장에서는 어떤 생성자를 사용해야 OAuth 유저를 저장하는지, 또는 일반 유저를 저장하는지 클래스를 확인해야 알 수 있습니다.
게다가 주석을 달아야 어떤 행동을 하는지 파악할 수 있다는 것 자체가 코드스멜이라고 볼 수 있겠죠.
정적 팩터리 메서드는 이런 부분을 다음처럼 정말 쉽게 해결해 줍니다.
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
...
public static createOAuthMember(String socialId, String username, String email) {
return new Member(...);
}
public static createBasicMember(String userName, String email, String password) {
return new Member(...);
}
}
이렇게 코드를 작성을 하면 클라이언트는 굳이 Member 클래스를 확인하지 않아도 어떤 메서드가 어떤 멤버 엔티티를 만드는지 알 수 있게 됩니다.
2. 호출할 때마다 인스턴스를 새로 생성하지 않아도 됨
정적 팩터리 메서드는 생성자를 캡슐화 함으로써 항상 인스턴스를 새로 생성하지 않아도 되는 것을 가능하게 합니다.
덕분에 불변 객체를 만들고 싱글턴으로 사용을 하거나 캐싱과 같은 동작을 할 수 있어, 쓸데없는 리소스 낭비를 하지 않아도 됩니다.
@jdk.internal.ValueBased
public final class Boolean implements java.io.Serializable, Comparable<Boolean>, Constable {
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
@IntrinsicCandidate
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
...
}
대표적인 예로 Boxed Class의 Boolean이 있습니다.
Boolean의 valueOf를 호출하게 되면 새로 Boolean 인스턴스를 반환하는 것이 아닌, 미리 불변 객체로 선언된 두 개의 싱글턴 인스턴스를 리턴하게 됩니다.
이런 식으로 스스로 본인 클래스의 인스턴스를 제어하는 클래스를 인스턴스 통제(instance-controlled) 클래스라고 한다고 합니다.
3. 반환 타입의 하위 타입 객체를 반환할 수 있음
정적 팩터리 메서드는 생성자와 달리 반환 타입을 지정할 수 있고, 이 말은 곧 다형성을 적용시킬 수 있다는 뜻이 됩니다.
public class Dog {}
public class Maltese extends Dog {}
public class Poodle extends Dog {}
public class Dogs {
public static Dog maltese() {
return new Maltese();
}
public static Dog poodle() {
return new Poodle();
}
}
반환 타입을 인터페이스 또는 추상 클래스로 두고 필요에 따라 알맞은 구현 객체를 리턴함으로써, 클라이언트는 구현 클래스에 대해 자세히 알지 않아도 올바른 동작을 할 거라는 기대를 가지고 개발을 할 수 있습니다.
덕분에 객체 지향의 핵심 중 하나인 추상화를 지킬 수 있게 됩니다.
4. 매개 변수에 따라 반환할 하위 객체를 정할 수 있음
사실 3번과 비슷한 맥락이라고 볼 수 있습니다.
반환할 하위 객체를 반환할 수 있기 때문에 매개변수를 통해 반환할 하위 객체 또한 정할 수 있겠죠.
다만 개인적으로 이건 장점이라고 보기 어렵다는 생각이 들었습니다.
이펙티브 자바 책에서 예시로 든 EnumSet을 보면서 그 이유를 얘기해보겠습니다.
책에서 예시로 든 EnumSet을 보면, Enum의 원소 개수에 따라 RegularEnumSet 또는 JumnoEnumSet을 리턴합니다.
이 구현을 보면 EnumSet이 본인의 자식 클래스인 RegularEnumSet과 JumboEnumSet을 참조하고 있습니다.
오히려 추상화가 깨지고, 부모가 자식을 참조하는 역참조가 발생하면서 자식의 변화에 부모가 변할 수 있는 가능성이 생긴 이상한 상황이라는 생각이 드네요.
이 예시보다는 차라리 팩터리 패턴 예시가 조금 더 낫지 않았을까 하는 생각이 들었습니다.
참고
Effective Java 3/E
'Java' 카테고리의 다른 글
[Java] assert 알아보기 (0) | 2024.05.20 |
---|