클린코드: 클래스, 예외

jjin·2024년 3월 20일
0

클래스

SRP 단일책임원칙

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:
    	# 처리 코드

예외 클래스 잘 정의

  • 내장 빌트인 예외 클래스 활용 https://docs.python.org/ko/3/library/exceptions.html
    • AssertionError
    • AttributeError
    • EOFError
    • GeneratorExit
    • ImportError
    • ModuleNotFoundError
    • IndexError
    • KeyError
    • KeyboardInterrupt (ctrl-C or Delete)
    • MemoryError
    • NameError
    • NotImplementedError (사용자 정의 베이스 클래스에서 구현 추가 필요 나타냄)
    • OSError
    • OverflowError
    • RecursionError
    • ReferenceError
    • RuntimeError
    • StopIteration
    • TypeError
    • ZeroDivisionError ...
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:
    	...
profile
진짜

0개의 댓글

관련 채용 정보