코딩관계론

코드를 오용하게 어렵게 만들라 본문

Clean code

코드를 오용하게 어렵게 만들라

개발자_티모 2023. 7. 10. 12:52
반응형

서론

 비합리적이거나 애매한 가정에 기반해서 코드가 작성되거나 다른 개발자가 잘못된 일을 하는 것을 막지 못했을 때 코드가 오용되기 쉽다. 사용지치침을 제공한다면 코드 오용 문제를 완화하는데 도움이 될 수 있디.

 하지만 이런 설명서는 코드의 세부 조항이기 때문에 간과되거나 최신 정보가 아닐 가능성도 존재한다. 그러므로 코드를 오용하기 어렵게 설계하고 작성하는 것이 중요하다.

불변 객체로 만드는 것을 고려하라

단순하다.

불변 객체의 상태는 생성된 시점으로부터 파괴되는 시점까지 그대로 유지된다. 즉, 프로덕션에서 해당 객체가 가진 값을 변하지 않게 하려는 추가적인 노력을 필요로 하지 않는다.

Thread-Safe하다.

기본적으로 멀티스레딩 환경에서의 문제는 여러 스레드가 같은 객체에 접근하여 데이터를 쓰는 작업을 할 때 발생한다. 여러 스레드에서 값을 수정하기 때문에 객체의 상태가 훼손되어 해당 객체를 공유하는 다른 스레드에도 영향을 끼치는 것이다. 하지만 불변 객체는 상태가 변하지 않으므로 그 어떤 스레드도 다른 스레드에 영향을 줄 수 없어 안심하고 공유할 수 있다.

예측가능하다. side-effect의 가능성이 적다.

객체가 불변 객체가 아니어서 setter가 열려있는 등 다른 코드에서 해당 객체의 값을 수정 가능하다면, 의도하고자 하지 않은 방향으로 프로그램이 작동할 수 있다. 불변 객체를 사용하면 값을 예측할 수 있으므로 예상치 못한 값으로 인한 side-effect의 가능성이 적어진다.

 

어떻게 불변객체로 만들까?

1. 객체를 생성할 때만 값을 할당하라 

이렇게 구성하면 클래스가 불변적이 되고, 버그를 방지할 수 있다, 하지만 이렇게 하면 일부 값이 반드시 필요하지 않거나 불변적인 클래스의 가변적인 버전을 만들어야 하는 경우 클래스를 보다 다용도로 구현해야하는 경우는 이런 불변적인 클래스가 별로 쓸모가 없다. 

class ImmutableObject:
    def __init__(self, value1, value2):
        self._value1 = value1
        self._value2 = value2

    @property
    def value1(self):
        return self._value1

    @property
    def value2(self):
        return self._value2

 

2. 불변성에 대한 디자인 패털을 사용하라

 

빌더패턴

객체의 생성 과정을 추상화하여 복잡한 객체의 생성을 단순화하는 디자인 패턴입니다. 주로 매개 변수가 많거나 선택적인 매개 변수가 있는 객체를 생성하는 데 사용됩니다

 

효과

한 클래스를 두 개로 나누는 효과를 갖는다 

1.값을 하나씩 설정할 수 있는 빌더 클래스 

2.빌더에 의해 작성된 불변적인 읽기 전용 클래스

 

주목해야할 점은 TextOptionBuilder 클래스는 필수 멤버인 글꼴 값을 설정함수가 아닌 생성자를 통해 입력받는 다는 것입니다.

따라서 잘못된 객체를 생성하는 코드를 작성할 수 없습니다. 

class TextOption:
    def __init__(self, font, font_size=None):
        self.font = font
        self.font_size = font_size

    def __str__(self):
        if self.font_size:
            return f"Font: {self.font}, Font Size: {self.font_size}"
        else:
            return f"Font: {self.font}"

class TextOptionBuilder:
    def __init__(self, font):
        self.font = font
        self.font_size = None

    def set_font_size(self, font_size):
        self.font_size = font_size
        return self

    def build(self):
        return TextOption(self.font, self.font_size)

 

빌더 패턴은 값의 일부가 선택 사항일 때 불변적 객체를 만드는 매우 유용한 방법이다

쓰기 시 복사 패턴

 

 

객체를 깊은 수준까지 불변적으로 만드는 것을 고려하라

클래스가 실수로 가변적으로 될 수 있는 일반적인 경우는 깊은 가변성 때문이다. 이 문제는 멈버 변수 자체가 가변적인 유형이고 다른 코드가 멤버 변수에 엑세스할 수 있는 경우에 발생한다.

 

그 경우는 아래와 같다

일반적으로 생각했을 때 불변 객체지만 dict를 그냥 반환함으로써 오용을 초래할 수 있다.

class ImmutableClass:
    def __init__(self, immutable_data, mutable_data):
        self.immutable_data = tuple(immutable_data)  # 불변 데이터는 튜플로 변환하여 저장
        self.mutable_data = dict(mutable_data)  # 가변 데이터는 사전으로 변환하여 저장

    def get_immutable_data(self):
        return self.immutable_data

    def get_mutable_data(self):
        return self.mutable_data  # 가변 데이터를 반환

    def __str__(self):
        return f'ImmutableClass(immutable_data={self.immutable_data}, mutable_data={self.mutable_data})'

해결책

방어적으로 복사 

class ImmutableClass:
    def __init__(self, immutable_data, mutable_data):
        self.immutable_data = tuple(immutable_data)  # 불변 데이터는 튜플로 변환하여 저장
        self.mutable_data = dict(mutable_data)  # 가변 데이터는 사전으로 변환하여 저장

    def get_immutable_data(self):
        return self.immutable_data)

    def get_mutable_data(self):
        return dict(self.mutable_data)  
        
    def __str__(self):
        return f'ImmutableClass(immutable_data={self.immutable_data}, mutable_data={self.mutable_data})'

 

불변적 자료구조를 사용하라

튜플은 불변적인 자료구조임으로 반환하기 전에 copy할 필요가 없다.

불변적인 자료구조를 사용하는 것은 클래스가 깊은 불변성을 갖도록 보장하기 위한 좋은 방법 중 하나다. 

class ImmutableClass:
    def __init__(self, immutable_data, mutable_data):
        self.immutable_data = tuple(immutable_data)  # 불변 데이터는 튜플로 변환하여 저장

    def get_immutable_data(self):
        return self.immutable_data
반응형

'Clean code' 카테고리의 다른 글

오류  (0) 2023.05.20
코드 계약  (0) 2023.05.18
코드 추상화  (0) 2023.05.15
파이썬 데코레이터(Decorator)  (0) 2023.02.26
[Clean code] 클래스  (0) 2023.01.11