23p. 클래스를 싱글턴으로 만들면 이를 사용하는 클라이언트를 테스트하기가 어려워질 수 있다. 타입을 인터페이스로 정의한 다음 그 인터페이스를 구현해서 싱글턴이 아니라면 싱글턴 인스턴스를 가짜(mock) 구현으로 대체할 수 없기 때문이다.
그런데 인터페이스를 만들면 이러한 불편함을 해소할 수 있다 !
예를 들면 하나의 차(싱글톤)를 여러 사람이 운전 할 수 있다고 가정하자.
그럼 Car
라는 인터페이스를 만들고, 그걸 구현한 MyCar
클래스를 만들고, Car
타입의 객체를 사용하는 Person
클래스를 만들면 될 것이다.
그럼 Person
이라는 클래스를 테스트 해보자.
@ExtendWith(MockitoExtension.class)
class PersonTest {
@Mock
Car mockCar;
@Test
void 운전() {
// given
Person person = new Person(mockCar);
// when
person.drive();
// then
boolean driveStatue = person.isDriveStatue();
Assertions.assertThat(driveStatue).isTrue();
}
}
인터페이스가 있다면, 그 인터페이스 타입의 Mock(가짜 객체)
를 사용해서 테스트할 수 있다.
Mock
객체는 실제 객체를 다양한 조건으로 인해 제대로 구현하기 어려울 경우 만들어 사용한다.
Class<?> aClass = Class.forName("패키지명+클래스명");
class path
만 알고도 객체를 만들어내 낼 수 있다.private Elvis() {
if (created) {
throw new UnsupportedOperationException("can't be created by constructor.");
}
created = true;
}
readResolve()
메서드를 선언하고, 만들어져 있는 싱글톤 객체를 반환하게 한다.private Object readResolve() {
return INSTANCE;
}
아이템3에서는 객체를 싱글톤으로 만드는 방법에 대해 알아본다.
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding()
}
간결하다는 장점이 있다.
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding()
}
장점1
싱글톤 말고 새로운 객체를 반환하게 만들고 싶을 때 생성자 구현부를 수정하면 된다. 그러면 객체를 사용하는 입장에서의 코드 (Elvis.getInstance()
) 수정 할 필요 없게 된다.
장점2
정적 팩터리를 제네릭 싱글턴 팩토리로 만들 수 있다.
제네릭 싱글턴 팩토리 : 제네릭으로 타입설정 가능한 인스턴스를 만들어두고, 반환 시에 제네릭으로 받은 타입을 이용해 타입을 결정하는 것
public class Elvis<T> {
private static final Elvis INSTANCE = new Elvis();
private Elvis(){
}
public static <T> Elvis<T> getInstance() {
return (Elvis<T>) INSTANCE;
}
}
객체를 원하는 타입으로 제공할 수 있다는 장점이 있다.
장점3
정적 팩터리의 메서드 참조를 공급자(supplier
)로 사용할 수 있다.
(이 부분은 내가 java8을 잘 모르기도 하고,,, 지금 당장 찾아보는 게 의미 없을 것 같아서 나중에 작성하겠다...^^)
public enum Elvis {
INSTANCE;
public String getName() {
return "Elvis";
}
public void leaveTheBuilding() { ... }
}
대부분의 상항에서는 원소가 하나뿐인 열거타입을 만드는 게 가장 좋은 방법이다.
열거타입은 무조건 싱글톤을 보장하기 때문이다.
위 두 가지 방식에서는 리플랙션, 역직렬화 상황에서 새로운 인스턴스가 생길 가능성이 있지만
열거 타입은 애초에 하나의 객체만 생성되고, new
로 객체를 새로 생성할 수도 없다.
리플랙션이나 직렬화에서도 Enum
에 대한 특징이 반영되어 있기 때문에 제 2의 객체가 생성될 걱정을 안해도 된다.