2023. 11. 16. 23:39ㆍBook/이펙티브 자바
객체 생성과 파괴
들어가기 전 요약
- 사용하는 자원에 따라 동작이 달라지는 클래스는 정적 유틸리티 클래스나 싱글톤 방식이 적합하지 않다.
- 의존 객체 주입이란 인스터스를 생성할 때 필요한 자원을 넘겨주는 방식이다.
- 이 방식의 변형으로 생성자에 자원 팩토리를 넘겨줄 수 있다.
- 의존 객체 주입을 사용하며 클래스의 유용성, 재사용성, 테스트 용이성을 개선할 수 있다.
많은 클래스가 하나 이상의 자원에 의존한다. 가령 맞춤법 검사기는 사전(Dictionary)에 의존하는데, 이런 클래스를 정적 유틸리티 클래스(아이템4)로 구현한 모습을 드물지 않게 볼 수 있다.
정적 유틸리티를 잘못 사용한 예)
public class SpellChecker {
//자원을 직접 명시
private static final Dictionary dictionary = new Dictionary();
private SpellChecker() {}
public static final SpellChecker INSTANCE = new SpellChecker();
public static boolean isValid(String word) {
/*
* SpellCheck 코드 생략
*/
return dictionary.contains(word);
}
public static List<String> suggestions(String typo) {
/*
* SpellCheck 코드 생략
*/
return dictionary.closeWordsTo(typo);
}
}
싱글톤을 잘못 사용한 예)
public class SpellChecker {
//자원을 직접 명시
private final Dictionary dictionary = new Dictionary();
private SpellChecker() {}
public static final SpellChecker INSTANCE = new SpellChecker();
public boolean isValid(String word) {
/*
* SpellCheck 코드 생략
*/
return dictionary.contains(word);
}
public List<String> suggestions(String typo) {
/*
* SpellCheck 코드 생략
*/
return dictionary.closeWordsTo(typo);
}
}
============================================================================================
class SpellCheckerTest {
@Test
void isValid() {
assertTrue(SpellChecker.INSTANCE.isValid("test"));
}
}
두 방식 모두 Dictionary를 Mocking 하기 어려워 테스트하기 불편하다(static 한 클래스를 mocking 할 수는 있다. 권장x)
또한 실전에서는 언어별로 다른 사전이 있다. SpellChecker가 여러 사전을 사용할 수 있도록 해보자.
간단하게 Dictionary 필드에서 final 한정자를 제거하고 다른 사전으로 교체하는 메서드를 추가할 수 있다.
하지만 이 방식은 오류를 내기 쉽고 멀티스레드 환경에서는 쓸 수 없다.
==> 사용하는 자원에 따라 동작이 달라지는 클래스에는 정적 유틸리티 클래스나 싱글톤 방식이 적합하지 않다!
클래스(SpellChecker)가 여러 자원 인스턴스를 지원해야 하며, 클라이언트가 원하는 자원(dictionary)을 사용해야한다.
==> 이 조건을 만족하는 간단한 패턴이 인스턴스를 생성할 때 생성자에 필요한 자원을 넘겨주는 방식이다.(DI)
public class SpellChecker {
private final Dictionary dictionary;
public SpellChecker(DefaultDictionary dictionary) {
this.dictionary = dictionary;
}
public boolean isValid(String word) {
/*
* SpellCheck 코드 생략
*/
return dictionary.contains(word);
}
public List<String> suggestions(String typo) {
/*
* SpellCheck 코드 생략
*/
return dictionary.closeWordsTo(typo);
}
}
=================================================================
class SpellCheckerTest {
@Test
void isValid() {
SpellChecker spellChecker = new SpellChecker(new DefaultDictionary());
}
}
Dictionary를 인터페이스화 하고 원하는 구현체를 생성할 때 넘겨주면 유연하고 테스트하기 용이하게 할 수 있다.
이를 의존 객체 주입 패턴이라고 한다.
이 패턴의 쓸만한 변형으로, 생성자에 자원 팩토리를 넘겨주는 방식이 있다. 팩토리란 호출할 때마다 특정 타입의 인스턴스를 반복해서 만들어주는 객체를 말한다.(팩터리 메서드 패턴)
public class SpellChecker {
//Dictionary 인터페이스
private final Dictionary dictionary;
//DictionalFactory 인터페이스
public SpellChecker(DictionaryFactory dictionaryFactory) {
this.dictionary = dictionaryFactory.getDictionary();
}
public boolean isValid(String word) {
/*
* SpellCheck 코드 생략
*/
return dictionary.contains(word);
}
public List<String> suggestions(String typo) {
/*
* SpellCheck 코드 생략
*/
return dictionary.closeWordsTo(typo);
}
}
====================================================================================
public interface Dictionary {
boolean contains(String word);
List<String> closeWordsTo(String typo);
}
public interface DictionaryFactory {
Dictionary getDictionary();
}
====================================================================================
public class DefaultDictionaryFactory implements DictionaryFactory{
@Override
public Dictionary getDictionary() {
return new DefaultDictionary();
}
}
public class MockDictionaryFactory implements DictionaryFactory{
@Override
public Dictionary getDictionary() {
return new MockDictionary();
}
}
=====================================================================================
Dictionary도 인터페이스를 사용하고 Factory도 인터페이스를 사용하기 때문에 클라이언트 코드의 변경 없이 제품을 변경할 수 있다.(개방 폐쇄 원칙)
'Book > 이펙티브 자바' 카테고리의 다른 글
Item7 - 다 쓴 객체 참조를 해제하라 (0) | 2023.11.18 |
---|---|
Item6 - 불필요한 객체 생성을 피하라 (0) | 2023.11.17 |
Item4 - 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2023.11.15 |
Item3 - 생성자나 열거 타입으로 싱글톤임을 보증하라. (1) | 2023.11.14 |
Item2 - 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2023.11.12 |