오류 코드보다 예외를 사용
아래 코드는 오류 플래그를 설정하거나 호출자에게 오류 코드를 반환하는 방법으로 작성되어 있다.
public class DeviceController{
...
public void sendShutDown(){
DeviceHandle handle = getHandle(DEV1);
//디바이스 상태 점검
if(handle != DeviceHandle.INVALID){
//레코드 필드에 디바이스 상태 저장
retrieveDeviceRecord(handle);
//디바이스가 일시정지 상태가 아니라면 종료
if(record.getStatus() != DEVICE_SUSPENDED){
pauseDevice(handle);
clearDeviceWorkQueue(handle);
closeDevice(handle);
}else{
logger.log("Device suspended. Unable to shut down")
}
} else {
logger.log("Invalid handle for: " + DEV1.toString());
}
}
...
}
위와 같은 방법은 코드 자체가 복잡해진다.
함수를 호출한 즉시 오류를 확인해야 하기 때문이다.
차라리 오류가 발생하면 예외를 던지는 편이 낫다. 그러면 논리가 오류처리 코드와 뒤섞이지는 않는다.
아래 코드는 예외를 던지는 코드로 수정해보았다.
public class DeviceController{
...
public void sendShutDown(){
try{
tryShutDown();
} catch (DeviceShutDownError e){
logger.log(e);
}
}
private void tryToShutDown() throws DeviceShutDownError{
DeviceHandle handle = getHandle(DEV1);
DeviceRecord record =. etrieveDeviceRecord(handle);
pauseDevice(handle);
clearDeviceWorkQueue(handle);
closeDevice(handle);
}
private DeviceHandle getHandle(DeviceId id){
...
throw new DeviceShutDownError("Invalid handle for: " + id.toString());
...
}
...
}
코드가 좀 더 깨끗하고 품질 성도 더 개선되었다.
앞서 디바이스를 종료하는 알고리즘과 오류를 처리하는 알고리즘도 분리되었다.
이제 이러한 코드로 각 개념을 독립적으로 살펴보고 이해할 수 있다.
Try-Catch-Finally
try 블록에 들어가는 코드를 실행하면 어느 시점에서든 문제가 발생했을 때 실행이 중단된 후 catch 블록으로 넘어갈 수 있다.
어떤 면에서 try 블록은 트랜잭션과 비슷하고 해당 블록에서 무슨 일이 생기든지 catch 블록은 프로그램 상태를 일관성 있게 유지해야 한다.
그러면 try 블록에서 무슨 일이 생기든지 호출자가 기대하는 상태를 정의하기 쉬워진다.
아래 코드는 파일을 열어 직렬화된 객체 몇 개를 읽어 들이는 코드로 파일이 없으면 예외를 던지는 단위 테스트이다.
@Test(expected = StorageException.class)
public void retrieveSectionShouldThrowOnInvalidFileName(){
sectionStore.retrieveSection("invalid - file");
}
단위 테스트에 맞춰 다음 코드를 작성했다.
public List<RecordedGrip> retrieveSection(String sectionName){
//실제로 구현할 때까지 비어 있는 더미를 반환
return new ArrayList<RecordedGrip>();
}
이러면 코드가 예외를 던지지 않으므로 단위 테스트는 실패한다.
잘못된 파일 접근을 시도하게끔 구현을 변경하자.
public List<RecordedGrip> retrieveSection(String sectionName){
try{
FileInputStream stream = new FileInputStream(sectionName)
}catch (Exception e){
throw new StorageException("retrieval error", e)
}
return new ArrayList<RecordedGrip>();
}
코드가 예외를 던지므로 단위 테스트도 통과할 것이고 리팩터링을 하자면 catch 블록에서 예외 유형을 좁혀 실제로
FileInputStream 생성자가 던지는 FileNotFoundException을 잡아낸다
public List<RecordedGrip> retrieveSection(String sectionName){
try{
FileInputStream stream = new FileInputStream(sectionName)
}catch (FileNotFoundException e){
throw new StorageException("retrieval error", e)
}
return new ArrayList<RecordedGrip>();
}
'클린 코드' 카테고리의 다른 글
[클린 코드] 객체와 자료구조(4) (0) | 2022.01.27 |
---|---|
[클린 코드] 객체와 자료구조(3) (0) | 2022.01.26 |
[클린 코드] 객체와 자료구조(2) (0) | 2022.01.26 |
[클린 코드] 객체와 자료구조(1) (0) | 2022.01.19 |
[클린 코드] 형식 맞추기(2) (0) | 2022.01.17 |