CleanCode 7章エラー処理
25846 ワード
1.エラーコードではなく例外を使用
既存のエラーコードは次のとおりです.
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 {
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());
...
}
...
}
2.Try-Catch-Filly文を書き出します
try-catch-finally文でtryブロックに入るコードを実行すると、任意の時点で実行が停止しcatchに移動します.
テスト駆動開発(TDD)方式での実施方法
1.単位テストの作成
@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>();
}
public List<RecordedGrip> retrieveSection(String sectionName) {
try {
FileInputStream stream = new FileInputStream(sectionName);
stream.close();
} catch (FileNotFoundException e) {
throw new StorageException("retrieval error", e);
}
return new ArrayList<RecordedGrip>();
}
3.未検査の例外の使用
検査異常
コンパイルフェーズで決定され、処理しなければならない例外.
(IOException, SQLException etc...)
未検査異常
実行フェーズで明示的な処理を強制しない例外を特定します.
(NullPointerException, IllegalArgumentException, IndexOuntOfBoundException, SystemException)
検査の例外はコストがかかるため、相応の利益があるかどうかを考慮する必要があります.
(OCP違反、メソッドは確認された例外を放出し、catchブロックが3つのステップの上にある場合は、その間のすべてのメソッドに例外を定義する必要があります)
4.例外の意味を提供する
->エラーメッセージに情報、失敗した演算名、および失敗タイプ
5.呼び出し元を考慮して例外クラスを定義する
// 1
ACMEPort port = new ACMEPort(12);
try {
port.open();
} catch (DeviceResponseException e) {
reportPortError(e);
logger.log("Device response exception", e);
} catch (ATM1212UnlockedException e) {
reportPortError(e);
logger.log("Unlock exception", e);
} catch (GMXError e) {
reportPortError(e);
logger.log("Device response exception");
} finally {
...
}
// 2
public class LocalPort {
private ACMEPort innerPort;
public LocalPort(int portNumber) {
innerPort = new ACMEPort(portNumber);
}
public void open() {
try {
innerPort.open();
} catch (DeviceResponseException e) {
throw new PortDeviceFailure(e);
} catch (ATM1212UnlockedException e) {
throw new PortDeviceFailure(e);
} catch (GMXError e) {
throw new PortDeviceFailure(e);
}
}
...
}
1番は外部ライブラリを呼び出し、すべての異常は呼び出し者によって検出されます.一方、第2の方法は、呼び出しライブラリAPIを迂回して異常タイプを返すことによって簡略化される
6.nullを返さない
nullを返す習慣が悪い
// 1
List<Employee> employees = getEmployees();
if(employees != null) {
for(Employee e : employees) {
totalPay += e.getPay();
}
}
// 2
List<Employee> employees = getEmployees();
for(Employee e : employees) {
totalPay += e.getPay();
}
public List<Employee> getEmployees() {
if (..직원이 없다면..)
return Collections.emptyList();
}
1番のようにnullを返すのではなく、2番のように空のlistを返すかassert文を使用します.Reference
この問題について(CleanCode 7章エラー処理), 我々は、より多くの情報をここで見つけました https://velog.io/@miiunii/CleanCode-7장-오류처리テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol