클린 코드, 로버트 C. 마틴, 박재호 이해영 옮김, 인사이트
뭔가 잘못될 가능성은 늘 존재한다. 뭔가 잘못되면 바로 잡을 책임은 바로 우리 프로그래머에게 있다.
- 130p
오류 코드보다 예외를 사용하라
...
목록 7-2는 오류를 발견하면 예외를 던지는 코드다.
public class DeviceController {
...
public void sendShutDown() {
try {
tryToShutDown();
} catch (DeviceShutDownError e) {
logger.log(e);
}
}
private void tryToShutDown() throws DeviceShutDownError {
DeviceHandle handle = getHandle(DEV1);
DeviceRecord record = retrieveDeviceRecord(handle);
pauseDevice(handle);
clearDeviceWorkQueue(handle);
closeDevice(handle);
}
private DeviceHandle getHandle(DeviceID id) {
...
throw new DeviceShutDownError("Invalid handle for: " + id.toString());
...
}
...
}
- 131p
다음은 파일이 없으면 예외를 던지는지 알아보는 단위테스트다.
@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);
stream.close();
} 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);
stream.close();
} catch (FileInputException e) {
throw new StorageException("retrieval error", e);
}
return new ArrayList<RecordedGrip>();
}
- 132 ~ 133p
미확인 예외를 사용하라
예외에 의미를 제공하라
호출자를 고려해 예외 클래스를 정의하라
정상흐름을 정의하라
null을 반환하지 마라
null을 전달하지 마라
- 133 ~ 142p
다음은 두 지점 사이의 거리를 계산하는 간단한 메서드다.
public class MetricsCalculator {
public double xProjection(Point p1, Point p2) {
return (p2.x - p1.x) * 1.5; // 일부러 절차 지향적인 클래스로 만든것
}
...
}
...
다음과 같이 새로운 예외 유형을 만들어 던지는 방법이 있다.
public class MetricsCalculator {
public double xProjection(Point p1, Point p2) {
if(p1 == null || p2 == null) {
throw InvalidArgumentException(
"Invalid argument for MetricsCalculator.xProjection");
}
return (p2.x - p1.x) * 1.5; // 일부러 절차 지향적인 클래스로 만든것
}
...
}
다음은 또 다른 대안이다.
public class MetricsCalculator {
public double xProjection(Point p1, Point p2) {
assert p1 != null : "p1 should not be null";
assert p2 != null : "p2 should not be null";
return (p2.x - p1.x) * 1.5;
}
}
- 141p
인터페이스 제공자와 인터페이스 사용자 사이에는 특유의 긴장이 존재한다. 패키지 제공자나 프레임워크 제공자는 적용성을 최대한 넓히려 애쓴다. 더 많은 환경에서 돌아가야 더 많은 고객이 구매하니까. 반면, 사용자는 자신의 요구에 집중하는 인터페이스를 바란다. 이런 긴장으로 인해 시스템 경계에서 문제가 생길 소지가 많다.
- 144p
java.util.Map을 살펴보자. ... 아래 목륵을 보면 첫째가 clear()메서다. 즉, Map 사용자라면 누구나 Map 내용을 지울 권한이 있다는 말이다. ... Sensor라는 객체를 담은 Map을 만들려면 다음과 같이 Map을 생성한다.
Map sensors = new HashMap();
Sensor 객체가 필요한 코드는 다음과 같이 Sensor 객체를 가져온다.
Sensor s = (Sensor)sensors.get(sensorId);
위와 같은 코드가 한 번이 아니라 여러 차례 나온다. 즉 Map이 반환하는 Object를 올바른 유형으로 변환할 책임은 Map을 사용하는 클라이언트에게 있다. ... 대신 다음과 같이 제네릭스를 사용하면 코드 가독성이 크게 높아진다.
public class Sensors {
private Map sensors = new HashMap();
public Sensor getById(String id) {
return (Sensor) sensors.get(id);
}
}
경계 인터페이스인 Map을 Sensors 안으로 숨긴다. 따라서 Map 인터페이스가 변하더라도 나머지 프로그램에는 영향을 미치지 않는다. 제네릭스를 사용하든 하지 않든 더 이상 문제가 안 된다.
- 143 ~ 146p
경계 살피고 익히기
- 146p
log4j 익히기
- 147p
좀 더 구글을 뒤지고, 문서를 읽어보고, 테스트를 돌린 끝에 목록 8-1을 얻었다. 그동안 log4j가 돌아가는 방식을 상당히 많이 이해했으며 여기서 얻은 지식을 간단한 단위 테스트 케이스 몇 개로 표현했다.
public class LogTest {
private Logger logger;
@Before
public void initialize() {
logger = Logger.getLogger("logger");
logger.removeAllAppenders();
Logger.getRootLogger().removeAllAppenders();
}
@Test
public void basicLogger() {
BasicConfigurator.configure();
logger.info("basicLogger");
}
@Test
public void addAppenderWithStream() {
logger.addAppender(new ConsoleAppender(
new PatternLayout("%p %t %m%n"),
ConsoleAppender.SYSTEM_OUT));
logger.info("addAppenderWithStream");
}
@Test
public void addAppenderWithoutStream() {
logger.addAppender(new ConsoleAppender(
new PatternLayout("%p %t %m%n")));
logger.info("addAppenderWithoutStream");
}
}
- 148p
학습 테스트는 공짜 이상이다.
- 149p
아직 존재하지 않는 코드 사용하기
- 150p
깨끗한 경계
- 151p
'Book' 카테고리의 다른 글
[독서 기록] 클린 코드 11장, 12장 , 13장 외 / 시스템, 창발성, 동시성 (1) | 2022.11.13 |
---|---|
[독서 기록] 클린 코드 9장, 10장 / 단위 테스트, 클래스 (0) | 2022.11.09 |
[독서 기록] 클린 코드 4장, 5장, 6장 / 주석, 형식 맞추기, 객체와 자료 구조 (0) | 2022.11.09 |
[독서 기록] 클린 코드 3장 / 함수 (0) | 2022.11.08 |
[독서 기록] 클린 코드 1~2장 / 깨끗한 코드, 의미 있는 이름 (0) | 2022.11.08 |