[python] class method (@classmethod)

About_work·2024년 2월 20일
0

python 기초

목록 보기
35/56

1. class method 언제사용해?

  • 모든 인스턴스에 공통적인 기능을 수행 할 때
    • 클래스 변수에 접근하거나 수정: 모든 인스턴스에 공통적으로 적용되는 값을 관리할 때 유용
  • 인스턴스 생성 전에 필요한 사전 설정 작업을 수행을 하거나, 다양한 조건에 따라 다른 인스턴스를 생성해야 할 때
    • 팩토리 메소드 패턴 구현

1.1. 모든 인스턴스에 공통적인 기능을 수행

  • 아래의 코드 예시에서는, 모든 인스턴스에 공통적인 속성인 count를 관리하는 간단한 예
  • 이 속성은 클래스가 생성된 인스턴스의 수를 추적
  • 클래스 메소드를 사용하여 count를 증가시키고, 모든 인스턴스에서 이 값을 조회할 수 있음
class MyObject:
    # 클래스 변수로 인스턴스 수를 추적
    count = 0

    def __init__(self):
        # 인스턴스가 생성될 때마다 count를 증가시킴
        MyObject.increase_count()

    @classmethod
    def increase_count(cls):
        # 클래스 메소드를 통해 클래스 변수 접근 및 수정
        cls.count += 1

    @classmethod
    def get_count(cls):
        # 클래스 메소드를 통해 클래스 변수 값을 반환
        return cls.count

# 인스턴스 생성
obj1 = MyObject()
print(MyObject.get_count())  # 출력: 1

obj2 = MyObject()
print(MyObject.get_count())  # 출력: 2

# 클래스 메소드는 인스턴스를 통해서도 호출할 수 있지만, 클래스 레벨의 작업을 수행
print(obj1.get_count())  # 출력: 2

1.2. "인스턴스를 생성하기 전에 필요한 사전 설정 작업을 수행"

  • 사전 설정 작업은 인스턴스의 초기화 과정에 필요한 다양한 설정 값들을 준비하거나, 특정 조건을 만족하는 객체만 생성되도록 하는 등의 용도로 사용될 수 있습니다.
  • 클래스 메소드를 이용하면, 이러한 설정 작업을 클래스 레벨에서 관리할 수 있어 객체 생성 과정을 더욱 유연하게 제어
  • 아래 예시는 설정 파일을 읽어, 해당 설정에 따라 다른 종류의 인스턴스를 생성하는 간단한 팩토리 메서드를 구현
class Database:
    def __init__(self, db_type, connection_string):
        self.db_type = db_type
        self.connection_string = connection_string

    def connect(self):
        print(f"{self.db_type}에 연결: {self.connection_string}")

    @classmethod
    def from_config(cls, config_dict):
        # 설정 사전에서 필요한 정보를 읽어옴
        db_type = config_dict['db_type']
        connection_string = config_dict['connection_string']
        
        # 사전 설정에 따라 Database 인스턴스 생성
        return cls(db_type, connection_string)

# 설정 사전 예시
config = {
    "db_type": "MySQL",
    "connection_string": "your_connection_string_here"
}

# 클래스 메서드를 사용하여 인스턴스 생성
db_instance = Database.from_config(config)
db_instance.connect()
  • 이와 같은 패턴은 애플리케이션의 설정이 복잡하거나, 여러 단계의 준비 과정을 거쳐야 하는 객체를 생성해야 할 때 특히 유용
  • 클래스 메소드를 사용함으로써, 객체 생성 로직을 단순화하고, 코드의 가독성과 유지 보수성을 향상시킬 수 있음
  • 이 코드에서 Database 클래스는 데이터베이스에 연결하기 위한 정보(db_typeconnection_string)를 갖고 있음
  • 클래스 메소드 from_config는 설정 정보를 담고 있는 사전을 인자로 받아, 이 정보를 기반으로 Database 인스턴스를 생성
  • 이 방법을 사용하면, 인스턴스 생성 로직을 클래스 내부에 캡슐화하여,
    • 클라이언트 코드가, 인스턴스 생성 과정에 대해 자세히 알 필요 없이 객체를 쉽게 생성할 수 있습니다.

1.3."다양한 조건에 따라 다른 인스턴스를 생성"

  • 다양한 조건에 따라 다른 인스턴스를 생성하기 위해 클래스 메소드를 사용하는 방법은 객체 지향 프로그래밍에서 매우 유용

  • 이러한 접근 방식은 특히 팩토리 메소드 패턴에서 자주 볼 수 있으며, 객체 생성 로직을 캡슐화하여

    • 클라이언트 코드가 직접 생성자를 호출하지 않고도 필요한 객체를 얻을 수 있게 합니다.
  • 클래스 메소드를 사용하면, 인자에 따라 다양한 유형의 인스턴스를 조건적으로 생성할 수 있으며, 이 과정을 한 곳에서 관리할 수 있어 코드의 유연성과 가독성을 높일 수 있음

  • 아래 예시에서는 간단한 채팅 애플리케이션을 위한 메시지 객체를 생성하는 과정을 구현

  • 메시지 유형(텍스트 메시지, 이미지 메시지, 음성 메시지 등)에 따라 다른 인스턴스를 생성하는 클래스 메소드를 사용

class Message:
    def __init__(self, content):
        self.content = content

    def display(self):
        raise NotImplementedError("Subclass must implement abstract method")

class TextMessage(Message):
    def display(self):
        print(f"TextMessage: {self.content}")

class ImageMessage(Message):
    def display(self):
        print(f"ImageMessage: {self.content}")

class VoiceMessage(Message):
    def display(self):
        print(f"VoiceMessage: {self.content}")

class MessageFactory:
    @classmethod
    def create_message(cls, message_type, content):
        if message_type == 'text':
            return TextMessage(content)
        elif message_type == 'image':
            return ImageMessage(content)
        elif message_type == 'voice':
            return VoiceMessage(content)
        else:
            raise ValueError("Unknown message type")

# 클라이언트 코드
text_msg = MessageFactory.create_message('text', 'Hello, world!')
text_msg.display()

image_msg = MessageFactory.create_message('image', 'image_url.jpg')
image_msg.display()

voice_msg = MessageFactory.create_message('voice', 'voice_clip.mp3')
voice_msg.display()
  • 이 예시에서 MessageFactory 클래스에 정의된 클래스 메소드 create_messagemessage_type 인자를 기반으로 조건에 따라 다른 유형의 메시지 인스턴스를 생성

  • 이 방식을 통해 클라이언트 코드는 구체적인 메시지 유형 클래스에 의존하지 않고도 필요한 메시지 객체를 유연하게 생성할 수 있음

  • create_message 메소드는 간단한 조건문을 사용하여 요청된 메시지 유형에 맞는 적절한 서브클래스의 인스턴스를 반환

  • 이 패턴은 다음과 같은 이점을 제공합니다:

    • 확장성: 새로운 메시지 유형이 추가되어야 할 경우, MessageFactory 클래스만 수정하면 되므로 유지 보수가 용이
    • 유연성: 클라이언트 코드는 생성하려는 객체의 구체적인 클래스를 몰라도 됩니다. 이는 코드의 결합도를 낮추고 유연성을 향상
    • 캡슐화: 객체 생성 로직이 한 곳(MessageFactory 클래스)에 집중되어 있어, 복잡한 생성 조건이나 로직을 쉽게 관리할 수 있음

2. 팩토리 메서드 패턴? (Factory Method Pattern)

  • 팩토리 메소드 패턴(Factory Method Pattern)은 객체 지향 프로그래밍에서 사용하는 디자인 패턴 중 하나
  • 이 패턴은 객체 생성 처리를 서브 클래스로 분리하여, 객체 생성에 필요한 인터페이스를 정의하는 데 사용
  • 즉, 부모 클래스에서는 객체를 생성하는 메소드를 정의하고, 이 메소드의 구현은 서브 클래스에서 담당하게 합니다.
  • 이를 통해 객체 생성 과정의 유연성과 확장성을 높일 수 있으며, 객체 생성의 책임을 각각의 서브 클래스에 할당함으로써 코드의 재사용성도 증가시킵니다.
  • 팩토리 메소드 패턴의 핵심은 부모 클래스에서 생성할 객체의 클래스를 직접 지정하지 않고, 대신 서브 클래스가 어떤 클래스의 인스턴스를 생성할지를 결정하게 하는 것입니다. 이 방법으로, 클라이언트 코드와 인스턴스를 생성할 클래스 간의 결합도를 낮출 수 있습니다.

예시

  • 상황을 가정해 보겠습니다. 간단한 문서 편집 애플리케이션을 개발한다고 했을 때, 다양한 종류의 문서(예: TextDocument, DrawingDocument, SpreadsheetDocument 등)를 생성해야 한다고 가정해 봅시다.

  • 이 때, 팩토리 메소드 패턴을 사용하여 문서 생성 로직을 구현할 수 있습니다.

  • 먼저, 모든 문서 타입의 부모 클래스 역할을 하는 Document 클래스와,

  • 문서를 생성하는 메소드를 정의하는 DocumentCreator 클래스를 만듭니다.

  • 그리고 각 문서 타입마다 DocumentCreator의 서브 클래스를 생성하여, 해당 타입의 문서를 생성하는 로직을 구현

from abc import ABC, abstractmethod

class Document(ABC):
    @abstractmethod
    def use(self):
        pass

class TextDocument(Document):
    def use(self):
        print("TextDocument: 사용 중")

class DrawingDocument(Document):
    def use(self):
        print("DrawingDocument: 사용 중")

class SpreadsheetDocument(Document):
    def use(self):
        print("SpreadsheetDocument: 사용 중")

class DocumentCreator(ABC):
    @abstractmethod
    def create_document(self):
        pass

class TextDocumentCreator(DocumentCreator):
    def create_document(self):
        return TextDocument()

class DrawingDocumentCreator(DocumentCreator):
    def create_document(self):
        return DrawingDocument()

class SpreadsheetDocumentCreator(DocumentCreator):
    def create_document(self):
        return SpreadsheetDocument()

# 클라이언트 코드
def client_code(creator: DocumentCreator):
    document = creator.create_document()
    document.use()

# 각 문서 타입에 맞는 Creator 인스턴스를 생성하여 사용
client_code(TextDocumentCreator())
client_code(DrawingDocumentCreator())
client_code(SpreadsheetDocumentCreator())
  • 이 예시에서는 DocumentCreator 클래스가 팩토리 메소드(create_document)를 정의하고, 이 메소드의 구현은 각 문서 타입에 맞는 서브 클래스(TextDocumentCreator, DrawingDocumentCreator, SpreadsheetDocumentCreator)에서 담당합니다. 클라이언트 코드는 DocumentCreator 인터페이스를 통해 각각의 문서 인스턴스를 생성하므로, 생성하려는 문서의 타입을 변경하고 싶을 때 해당하는 DocumentCreator의 서브 클래스 인스턴스를 사용하면 됩니다. 이렇게 함으로써, 클라이언트 코드와 구체적인 문서 클래스 간의 결합도를 낮출 수 있습니다.
profile
새로운 것이 들어오면 이미 있는 것과 충돌을 시도하라.

0개의 댓글