반응형

정적 유틸리티를 잘못 사용한 예 - 유연하지 않고 테스트하기 어렵다

public class SpellChecker {
    private static final Lexicon dictionary =  ...;

    private SpellChecker() {} // 객체 생성 방지

    public static boolean isValid(String word) { ... }
    public static List<String> suggestions(String type) { ... }
}

싱글턴을 잘못 사용한 예 - 유연하지 않고 테스트하기 어렵다.

public class SpellChecker {
    private final Lexicon dictionary = ...;

    private SpellChecker(){}
    public static SpellChecker INSTANCE = new SpellChecker(...);

    public boolean isValid(String word) { ... }
    public List<String> suggestions(String typo) { ... }
}

두 방식 모두 사전 하나로 이 모든 쓰임에 대응하기 어렵다.

- 사용하는 자원에 따라 동작이 달라지는 클래스에 정적 유틸리티 클래스나 싱글턴 방식이 적합하지 않음

- 클래스가 여러 자원 인스턴스를 지원해야하며 클라이언트가 원하는 자원(dictionary)을 사용해야 함

- 인스턴스가 생성할 때 생성자에 필요한 자원을 넘겨주는 방식

- 의존 객체 주입은 유연성과 테스트 용이성을 높여준다.

public class SpellChecker {
    private final Lexicon dictionary;

    public SpellChecker(Lexicon dictionary){
        this.dictionary = Objects.requireNonNull(dictionary);
    }

    public boolean isValid(String word){}
    public List<String> suggestions(String typo){}
}

의존 객체 주입 패턴

생성자, 정적 팩터리, 빌더 모두에 똑같이 응용할 수 있음

팩터리란 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체

팩터리 메서드 패턴이 가장 쓸만한 변형

자바 8에서 소개한 Supplier 인터페이스가 팩터리를 표현한 완벽한 예

이 방식은 사용해 클라이언트는 자신이 명시한 타입의 하위 타입이라면 무서이든 생성할 수 있는 팩터리를 넘길 수 있음

Mosaic create(Supplier<? extends Title> titleFactory) {}

의존 객체 주입이 유연성과 테스트 용이성을 개선해주긴 하지만, 의존성이 수천 개나 되는 큰 프로젝트에서는 코드를 어지럽게 만듬

스프링 같은 의존 객체 주입 프레임워크를 사용하면 이런 어질러짐을 해소할 수 있음

정리

클래스가 내부적으로 하나 이상의 자원에 의존하고, 그 자원이 클래스 동작에 영향을 준다면 싱글턴과 정적 유틸리티 클래스는 사용하지 않는 것이 좋음
의존 객체 주입이라는 이 기법은 클래스의 유연성, 재사용성, 테스트 용이성을 기막히게 개선해줌

추가 스프링에서 의존성 주입 방법 3가지

1. Field 주입

@Service 
public class FieldInjectionService {

	@Autowired 
    private ExampleService exampleService; 
    
    public void getBuLogic(){ 
    	exampleService.businessLogic(); 
    } 
    
}

 

2. Setter 주입

@Service 
public class SetterInjectionService{

    private ExampleService exampleService; 
    
    @Autowired 
    public void setExampleService(ExampleService exampleService){ 
    	this.exampleService = exampleService; 
    } 
    
    public void getBuLogic(){ 
    	exampleService.businessLogic(); 
    } 
    
}

 

3. Constructor 주입

@Service 
public class ConstructorInjectionService { 

    private final ExampleService exampleService; 
    
    public void ConstructorInjectionService(final ExampleService exampleService) { 
    	this.exampleService = exampleService; 
    } 
    
    public void getBuLogic(){ 
    	exampleService.businessLogic(); 
    } 
    
 }

장점

  1. 불변 객체를 만들 수 있음
  2. 순환참조를 막을 수 있음
  3. NPE 방지

Lombok 라이브러리를 사용하여 생성자 자동생성 어노테이션 이용한 개선

@Service
@RequredArgsConstructor
public class ConstructorInjectionService{

    private final ExampleService exampleService;

    public void getBuLogic(){
        exampleService.businessLogic();
    }
}

 

 

728x90
반응형

회사 코드에 EntityManager를 사용하여 try~catch~finally를 사용하여 finally에서 해당 EntityManager 인스턴스를 close()하고 자원을 반납하고 있었습니다. 

하지만 이펙티브 자바에서는 [아이템 9. try-finally 대신 try-with-resources를 사용하라.] 라는 내용이 존재합니다. 

그래서 EntityManager를 try-with-resources로 리팩터링을 하려고 했지만, AutoClosable이 지원하지 않는다고 IDE에서 말해주고 있었습니다. 관련 글을 찾던 중 블로그에 해당 이슈는 오픈소스에 fix issue로 등록이 되어 있고 특정 버전 이후에 추가가 되었다고 합니다.

제가 회사에서 쓰는 java persistence-api 라이브러리는 2.x로 AutoClosable을 지원하지 않았고, 3.x대로 올라가면서 Jakarta Persistence API -> 3.1.0 지원하게 된것을 알게되었습니다.

EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("default");
try(EntityManager entityManager = entityManagerFactory.createEntityManager()){

}

 

package jakarta.persistence;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaDelete;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.CriteriaUpdate;
import jakarta.persistence.metamodel.Metamodel;
import java.util.List;
import java.util.Map;

public interface EntityManager extends AutoCloseable {
    void persist(Object var1);

    <T> T merge(T var1);

    void remove(Object var1);

해당 Jakarta Persistence API의 EntityManager 인터페이스를 보면 알 수 있듯이 해당 클래스는 AutoCloseable을 상속하고 있는 것을 알 수 있습니다.

public interface AutoCloseable {
    /**
     * Closes this resource, relinquishing any underlying resources.
     * This method is invoked automatically on objects managed by the
     * {@code try}-with-resources statement.
     *
     * <p>While this interface method is declared to throw {@code
     * Exception}, implementers are <em>strongly</em> encouraged to
     * declare concrete implementations of the {@code close} method to
     * throw more specific exceptions, or to throw no exception at all
     * if the close operation cannot fail.
     *
     * <p> Cases where the close operation may fail require careful
     * attention by implementers. It is strongly advised to relinquish
     * the underlying resources and to internally <em>mark</em> the
     * resource as closed, prior to throwing the exception. The {@code
     * close} method is unlikely to be invoked more than once and so
     * this ensures that the resources are released in a timely manner.
     * Furthermore it reduces problems that could arise when the resource
     * wraps, or is wrapped, by another resource.
     *
     * <p><em>Implementers of this interface are also strongly advised
     * to not have the {@code close} method throw {@link
     * InterruptedException}.</em>
     *
     * This exception interacts with a thread's interrupted status,
     * and runtime misbehavior is likely to occur if an {@code
     * InterruptedException} is {@linkplain Throwable#addSuppressed
     * suppressed}.
     *
     * More generally, if it would cause problems for an
     * exception to be suppressed, the {@code AutoCloseable.close}
     * method should not throw it.
     *
     * <p>Note that unlike the {@link java.io.Closeable#close close}
     * method of {@link java.io.Closeable}, this {@code close} method
     * is <em>not</em> required to be idempotent.  In other words,
     * calling this {@code close} method more than once may have some
     * visible side effect, unlike {@code Closeable.close} which is
     * required to have no effect if called more than once.
     *
     * However, implementers of this interface are strongly encouraged
     * to make their {@code close} methods idempotent.
     *
     * @throws Exception if this resource cannot be closed
     */
    void close() throws Exception;
}

 

https://k3068.tistory.com/98

 

왜? EntityManager는 AutoCloseable 구현하고 있지 않은가?

EntityManager는 쓰레드 간에 공유를 하지 않고 사용 후 바로 정리해야 한다고 한다. public void run(ApplicationArguments args) throws Exception { EntityManager em = entityManagerFactory.createEntityManager(); EntityTransaction transa

k3068.tistory.com

 

https://www.inflearn.com/questions/231224/entitymanager-%EB%8A%94-%EC%99%9C-autocloseable-%EC%9D%84-%EC%A7%80%EC%9B%90%ED%95%98%EC%A7%80-%EC%95%8A%EB%82%98%EC%9A%94

 

EntityManager 는 왜 AutoCloseable 을 지원하지 않나요? - 인프런 | 질문 & 답변

안녕하세요문득 궁금한점이 생겨서 질문해봅니다.항상 사용하고 버려야 한다면 AutoCloseable지원하여 try-with-resource 문을 사용할 수 있도록 도움을 주면 좋을 것 같다고 생각이 들었지만시용해볼

www.inflearn.com

 

728x90
반응형

정적 메서드와 정적 필드만을 담은 클래스

1) java.lang,Math, java.util.Arrays과 같이 기본 타입 값이나 배열 관련 메서드들을 모아놓음
2) java.util.Collections처럼 특정 인터페이스를 구현하는 객체를 생성해주는 정적 메서드를 모아놓을 수 있음
3) final 클래스와 관련된 메서드들

추상 클래스로 만드는 것으로는 인스턴스화를 막을 수 없다.

private 생성자를 추가하면 클래스의 인스턴스활르 막을 수 있음

인스턴스를 만들 수 없는 유틸리티 클래스

public class UtilityClass {

    //기본 생성자가 만들어지는 것을 막는다(인스턴스화 방지용)
    private UtilityClass(){
        throw new AssertionError();
    }
}
  • 위 코드는 어떤 환경에서도 클래스가 인스턴스화되는 것을 막아줌
  • 상속을 불가능하게 하는 효과도 존재함
  • 하위 클래스가 상위 클래스의 생성자에 접근할 길이 막힘
728x90

+ Recent posts