Clean Code - 오류 처리
1. 오류 코드 대신 예외를 사용하라
# 오류 코드를 사용하는 예
def analysis_wafer(self, wafer):
if wafer.is_valid:
self.do_something(wafer)
else:
self.logger.error("analysis error")
# 예외 처리를 사용하는 예
def analysis_wafer(self, wafer):
try:
self.do_something(wafer)
except InvalidWaferError as error:
self.logger.error("%s : %s", wafer.get_id(), error)
2. try-catch문 (try-except)문을 먼저 사용하라
def try_except_in_python():
try:
# 예외가 발생할 수 있는 코드 블럭
except ExceptionType1 as error:
# 예외 타입 1 발생 시 처리 블럭
except ExceptionType2 as error:
# 예외 타입 2 발생 시 처리 블럭
else:
# 예외가 발생하지 않았을 때 실행되는 블럭
finally:
# 예외 여부와 관계 없이 실행되는 코드 블럭
3. 호출자를 고려한 예외 클래스 정의
- Exception class를 만드는데 가장 중요한 것은 "어떤 방식으로 예외를 잡을까?"이다.
- 써드 파티 라이브러리를 사용하는 경우 wrapping을 통해 라이브러리 교체 등의 변경이 있는 경우에 대응하기 쉬워진다.
- 라이브러리의 API 디자인에 종속적이지 않게 설계할 수 있다.
# 오류를 형편없이 분류한 사례
class WaferPredictor():
def get_wafer_data_from_etch(self):
EtchEquipment eqp = EtchEquipment("eqp_id")
try:
eqp.get_wafer_data()
...
except EquipmentConnectionError as error:
self.__report_equipment_error(e)
self.logger.error(error)
except InvalidWaferError as error:
self.__report_equipment_error(e)
self.logger.error(error)
except InOperationError as error:
self.__report_equipment_error(e)
self.logger.error(error)
# wrapping 클래스를 사용한 오류 분류
# wrapping 클래스
class Equipment():
def __init__(self, eqp_id):
self.__equipment = EtchEquipment(eqp_id)
def get_wafer_data_from_etch(self):
try:
self.__equipment.get_wafer_data()
...
except EquipmentConnectionError as error:
raise EquipmentError
except InvalidWaferError as error:
raise EquipmentError
except InOperationError as error:
raise EquipmentError
# 클라이언트 코드
class WaferPredictor():
def get_wafer_data_from_etch(self):
Equipment eqp = Equipment("eqp_id")
try:
eqp.get_wafer_data()
...
except EquipmentError as error:
self.__report_equipment_error(e)
self.logger.error(error)
4. 정상적인 상황을 정의하라
- catch 문에서 예외적인 상황을 처리해야하는 경우 코드가 지저분해질 수 있다.
- 마틴 파울러의 Special Case Pattern을 사용한다.
- 클라이언트 코드 입장에서는 예외 케이스에 대해 신경쓰지 않아도 된다.
- 예외 상황을 special case object 내에 캡슐화가 된다.
# 일반적인 예외처리
try:
loss_reason = LossClassification.classify_loss(wafer)
reasons.append(loss_reason)
except InvalidWaferError as error:
loss_reason = LossClassification.classify_loss(
self.__tune_wafer_info(wafer))
reasons.append(loss_reason)
# SPECIAL CASE PATTERN
try:
loss_reason = LossClassification.classify_loss(wafer)
reasons.append(loss_reason)
class LossClassification():
...
def classify_loss(self, wafer):
if wafer.is_invalid():
return TunedLossClassifaction.classify_loss(wafer)
return do_general_case_function(wafer)
class TunedLossClassification(LossClassification):
...
def classify_loss(self, wafer):
wafer = self.__tune_wafer_info(wafer)
return super().classify_loss(wafer)
5. Null을 리턴하지 마라
- null을 리턴하고 싶다면 위의 Special Case Object를 리턴하라
- 써드 파티 라이브러리에서 null을 리턴할 가능성이 있는 메서드가 있다면 Exception을 던지거나 Special Case Object를 리턴하는 메서드로 래핑한다.
- null을 리턴하는 것도 나쁘지만 null을 메서드로 넘기는 것은 더 안 좋은 코드를 만들어 낸다.
- null을 메서드의 파라미터로 넣어야하는 API를 사용하는 경우가 아니면 null을 메서드로 넘기지 마라.
- 가장 이상적인 해법은 null을 파라미터로 받지 못하게 하는 것이다.
'Architecture' 카테고리의 다른 글
[Clean Code 정리] TDD (0) | 2022.05.30 |
---|---|
[Clean Code 정리] 경계 (0) | 2022.05.29 |
[Clean Code 정리] 객체와 자료구조 (0) | 2022.05.29 |
[Clean Code 정리] 형식 맞추기 (0) | 2022.05.29 |
[Clean Code 정리] 주석 (0) | 2022.05.28 |
댓글