위 책을 보면서 정리한 글입니다.
Null에 대한 반복 처리는 코드의 독립성과 신뢰성을 떨어뜨린다.
단순 Null 확인에도 Null 객체를 별도로 정의해서 사용해야 할까
- 일회성이라면 만들 필요가 없다. 다만, Null 확인이 반복적으로 나타나거나 예외 처리를 위해 별도의 로직이 추가될 때
- 이 때는 Null 객체를 따로 정의하면 본래의 기능에 집중 가능해 코드의 가독성이 높아진다.
- 또한, 의도치 않게 Null 확인이 빠진 부분에서 이를 보완할 수 있는 코드가 되기도 한다.
전달받은 객체가 Null인지를 확인하고 예외 처리를 직접 하는 것이 문제가 될까
- Null 처리의 또 다른 문제는, 인터페이스를 사용하는 객체들이 Null에 대한 처리를 해야 한다는 것.
- 이는 의도하지 않은 기능이 포함되어 단일 책임의 원칙에 위배 된다.
- Null 객체의 핵심은 Null 상황에 대한 처리를 직접 하지 않고 해당 인터페이스를 정의한 객체로 위임한다는 것에 있다.
public class SnsWriter {
private List<String> accounts;
public SnsWriter(List<Account> accounts) {
this.accounts = accounts;
}
public void write(String message, String link, String imagePath) {
for(Account account : accounts) {
Writer writer = account.getWriter();
// Writer가 Null인 SNS에 대해서는 재인증 요청
if(writer != null) {
writer.write(message, link, imagePath);
} else {
// 사용자에게 인증 요청
new Author().requestAuth();
}
}
}
}
public interface Account {
// 해당 SNS에 필요한 게시물 등록 객체 가져오기
public Writer getWriter();
}
public class FaceAccount implements Account {
private String authKey;
public FaceAccount(String authKey) {
this.authKey = authKey;
}
@Override
public Writer getWriter() {
if(authKey != null) {
// 인증 정보가 있으면 게시물 등록 객체 전달
return new FaceWriter(authKey);
} else {
// 인증 정보가 없으면 Null 전달
return null;
}
}
}
public interface Writer {
public void write(String message, String link, String imagePath);
}
public class FaceWriter implements Writer {
private final String authKey;
public FaceWriter(string authKey) {
this.authKey = authKey;
}
@Override
public void write(String message, String link, String imagePath) {
// 게시물 등록
}
}
위 코드의 문제점
1. SnsWriter 객체에는 Account가 전달하는 Writer가 Null인지를 확인하고 이에 대한 예외 처리를 하는 로직이 포함되어 있다.
public class NoAuthWriter implements Writer {
@Override
public void write(String message, String link, String imagePath) {
// 사용자에게 인증 요청
new Author().requestAuth();
}
}
public class FaceAccount implements Account {
private String authKey;
public FaceAccount(String authKey) {
this.authKey = authKey;
}
@Override
public Writer getWriter() {
if(authKey != null) {
// 인증 정보가 있으면 게시물 등록 객체 전달
return new FaceWriter(authKey);
} else {
// Null - NoAuthWriter
return new NoAuthWriter();
}
}
}
public class AccountTest {
@Test
public void testGetWriter() throws Exception {
// Given
// 인증 정보가 없는 계정을 설정
FaceAccount faceAccount = new FaceAccount(nul);
// When
Writer writer = faceAccount.getWriter();
// Then
// Null Object로 반환되므로 Null이 아니다
Assert.assertNotNull(writer);
// 의도한 Writer와 동일한지 확인
Assert.assertEquals(NoAuthWriter.class, writer.getClass());
}
}
public class SnsWriter {
private List<String> accounts;
public SnsWriter(List<Account> accounts) {
this.accounts = accounts;
}
public void write(String message, String link, String imagePath) {
for(Account account : accounts) {
Writer writer = account.getWriter();
writer.write(message, link, imagePath);
}
}
}
반환된 객체가 Null일 때 추가로 처리되는 부분을 객체로 처리 가능하도록 개선
- 전달받은 객체가 Null일 때 처리 로직이 있는지 확인
- 객체를 전달하는 곳에서도 Null을 전달하는 코드가 있는지를 확인
- 공통으로 Null을 처리 할 수 있는 객체를 만든다.
- Null인 객체를 전달하는 경우에는 예외 처리를 수행하는 Null 객체를 전달.