as-is
class Store:
def communicate_user(self):
def manage_products(self):
def manage_money(self):
to-be
Class CounterManager:
def communicate_user(self):
Class ProductManager:
def manage_products(self):
Class Owner:
def manage_money(self):
class Store:
def __init__(self, counter_manager: CounterManager,
product_manager: ProductManager, owner: Owner):
self.counter_manager = counter_manager
self.product_manager = product_manager
self.owner = owner
def sell_product(self):
self.counter_manager.communicate_user()
...
as-is
class LowCohesion:
def __init__(self):
self.a = ...
self.b = ...
self.c = ...
def process_a(self):
print(self.a)
def process_b(self):
print(self.b)
def process_c(self):
print(self.c)
to-be
class HighCohesion:
def __init__(self):
self.abc = ...
def process_a(self):
self.abc.process_a()
def process_b(self):
self.abc.process_b()
def process_c(self):
self.abc.process_c()
as-is
class Developer:
def coding(self):
print("코딩을 합니다")
class Designer:
def design(self):
print("디자인을 합니다")
class Analyst:
def analyze(self):
print("분석을 합니다")
class Company:
def __init__(self, employees): #구체 클래스에 의존
self.employees = employees
# employee가 다양해질 때마다 여기를 계속 변경해야 함
def make_work(self):
for employee in self.employees:
if isinstance(employee, Developer):
employee.coding()
elif isinstance(employee, Designer):
employee.design()
elif isinstance(employee, Analyst):
employee.analyze()
to-be
class Employee(metaclass=abc.ABC): # abc: abstract base class
job = ...
@abc.abstractmethod
def work(self):
print(f"{self.job}을 합니다")
class Developer(Employee):
job = "코딩"
class Designer(Employee):
job = "디자인"
class Analyst(Employee):
job = "분석"
class Company:
def __init__(self, employees: List[Employee]): # 추상 클래스에 의존
self.employees = employees
# 변경에 닫힘
def make_work(self):
for employee in self.employees:
employee.work()
as-is
from enum import Enum
class ErrorCodes(Enum):
VALUE_ERROR="VALUE_ERROR"
def we_can_raise_error():
...
return ERROR_CODES.VALUE_ERROR
def use_ugly_function():
result = we_can_occur_error()
if result == ErrorCodes.VALUE_ERROR:
# 처리 코드
to-be
def we_can_raise_exception():
if ...:
raise ValueError("값 에러 발생")
def use_pretty_exception():
try:
we_can_occur_exception()
except ValueError as e:
# 처리 코드
class WithParameterCustomException(Exception):
def __init__(self, msg, **kwargs): # **kwargs를 사용하여 키워드 인자를 받습니다.
self.msg = msg
self.kwargs = kwargs
def __str__(self): # self 파라미터를 추가해야 합니다.
# kwargs를 문자열로 변환하여 메시지에 포함시킵니다.
# self(self.kwargs) 대신 str(self.kwargs)를 사용합니다.
return f"message {self.msg} with parameter {str(self.kwargs)}"
# 예외를 발생시킬 때 kwargs를 **를 사용하여 언패킹합니다.
raise WithParameterCustomException("문제가 있습니다", name="grab")
def we_can_raise_error():
raise Exception("Error!")
# BAD: 로그만 남기고 raise는 빠트림.
def use_ugly_function2():
try:
we_can_raise_error()
except Exception as e:
print(f"에러 발생{e}")
# GOOD
def use_awesome_function():
try:
we_can_raise_error()
except Exception as e:
logging.error(...) # Error Log 남기기
notify_error(...) # 예측 불가능한 외부 I/O 이슈라면 회사 내 채널에 알리기(이메일, 슬랙 etc)
raise OtherException(e) # 이 함수를 호출하는 다른 함수에서 추가로 처리해야 한다면 에러 전파하기
as-is
def act_1():
try:
we_can_raise_error1()
except:
#handling
def act_2():
try:
we_can_raise_error2()
except:
#handling
def act_3():
try:
we_can_raise_error3()
except:
#handling
# 에러가 날 지점을 한눈에 확인할 수 없습니다.
# act_1이 실패하면 act_2가 실행되면 안 된다면? 핸들링하기 어려워집니다.
def main():
act_1()
act_2()
act_3()
to-be
def act_1():
we_can_raise_error1()
def act_2():
we_can_raise_error2()
def act_3():
we_can_raise_error3()
def main():
try:
act_1()
act_2()
act_3()
except Exception1 as e1:
...
except Exception1 as e1:
...
except Exception1 as e1:
...