(주의)Abstract Factory Pattern은 파이썬 abc를 말하는게 아님

Kanto(칸토)·2024년 11월 12일
1

오브젝트

목록 보기
2/4

반전이다. 추상클래스를 사용한 Factory가 Abstract Factory Pattern이 아니었다. 그러니까 Abstract Factory Pattern은 파이썬 추상클래스 abc를 의미했던 것이 아니라 Factory의 사용 자체를 추상화했다는 말이다. 추상팩토리는 이해하기 쉬운 패턴이 아니지만 주로 framework level에서 사용되는 걸 생각해보면 상당히 고급 패턴이라는 생각이 든다.

from abc import ABC, abstractmethod

# 0. Abstract Factory Interface
class NotificationFactory(ABC):
    """Abstract Factory Interface"""

    @abstractmethod
    def create_email_notification(self):
        pass

    @abstractmethod
    def create_sms_notification(self):
        pass

    @abstractmethod
    def create_push_notification(self):
        pass

어떤 알림을 만들어내는 factory 이다. 그런데 위 추상 클래스만 봐서는 알림을 만들어내는 것 같기는 한데 실제로 메써드가 무엇을 반환하는지는 나와있지 않다. 아마도 알림을 수행할 수 있는 어떤 객체가 나와야 할 것이다.

실제 Factory 클래스를 만들었다(Concrete Class라고 한다). 이제 이 실제 Factory에서는 진정 뭔가를 생산하고 있는 것이 보인다.

첫 번째 Factory 클래스인 FastNotifFactory 에서는 email과, sms, push를 수행하는 실제 객체들이 생성되어 있다. 이름에서 보듯이 Fast{어쩌고저쩌고}Notification 이다.

두 번째 Facotry 클래스인 SendBlueFactory 에서도 email과, sms, push를 수행하는 실제 객체들이 생성되어 있다. SendBlue라는 스타일을 가진 객체들이다.

# 1. Concrete Factories
class FastNotifFactory(NotificationFactory):
    """Concrete Factory for FastNotif"""

    def create_email_notification(self):
        return FastNotifEmailNotification()

    def create_sms_notification(self):
        return FastNotifSMSNotification()

    def create_push_notification(self):
        return FastNotifPushNotification()

class SendBlueFactory(NotificationFactory):
    """Concrete Factory for SendBlue"""

    def create_email_notification(self):
        return SendBlueEmailNotification()

    def create_sms_notification(self):
        return SendBlueSMSNotification()

    def create_push_notification(self):
        return SendBluePushNotification()

위 두 클래스에서는 모두 create_email_notification create_sms_notification create_push_notification 를 구현하고 있다. 파이썬 추상 클래스이기 때문에 반드시 규격대로 구현이 되어야 하는 것은 물론이다. 규격대로 구현이 되어있는 것의 장점은 바로 다형성을 활용할 수 있다는 것이다. 이제 이 다형성을 어떻게 활용할 것인지만 남아있다.

바로 Client에서 사용될 것이다. 여기서 Client의미를 너무 복잡하게 생각하지 말고, 그냥 Factory를 이용하고자 하는 또 다른 객체로 생각해두자.

Factory를 이용하려고 하는데, 여기서 이용하는 방법이 바로 합성(Composition)이다.

그러니깐 Factory 객체를 인자로 집어 넣을 것이다. 설명을 위해 pseudo code를 보여주면

factory = FastNotifFactory()
client(factory)

와 비슷한 방식이 된다.

이제 client 클래스의 모습을 보자.
위의 짧은 pseudo code를 거꾸로 따라 갈 것이다.

client인 send_notification 함수는 이렇게 생겼다. 그러니까, 이 client는 factory를 받을 것이고 factory 객체가 무엇이든 간에 email_notification객체, sms_notification객체, push_notification객체를 생성하고 send 메써드를 수행할 것이다.

def send_notification(factory):
    """Send Notifications using the specified factory.

    This function demonstrates how to use the selected factory to create and send
    email, SMS, and push notifications.

    Args:
        factory (NotificationFactory): The factory object to create notifications.

    Returns:
        None
    """
    email_notification = factory.create_email_notification()
    sms_notification = factory.create_sms_notification()
    push_notification = factory.create_push_notification()

    email_notification.send()
    sms_notification.send()
    push_notification.send()

그럼 factory 객체를 클라이언트에서 선택하기만 하면 (FastNotifFactory 또는 SendBlueFactory) 다른 모든 것은 변함 없이 작동한다.

마지막으로 email_notification객체, sms_notification객체, push_notification객체가 생성되기 위한 클래스를 거꾸로 만들어 보자.

지면상 email_notification의 구현 클래스 하나만 만들었다. 이 경우 FastNotifFactory.create_email_notification() 메써드의 리턴 값으로 등장했던 FastNotifEmailNotification 클래스이다. (리턴 값에다가 미리 적어두었으니 당연히 작성해야겠지 않는가?)

class FastNotifEmailNotification(AbstractEmailNotification):
    """Concrete Product for Email Notifications via FastNotif"""

    def send(self):
        print("Sending Email via FastNotif")

    def format_content(self):
        print("Formatting Email content")

이 코드를 보면 AbstractEmailNotification도 필요한 것 같다. (없어도 되겠지만 인터페이스의 통일을 위해 사용했다고 이해하자)

class AbstractEmailNotification(ABC):
    """Abstract Product for Email Notifications

    This abstract class defines the interface for creating email notifications.

    Methods:
        send(): Abstract method for sending the email notification.
        format_content(): Abstract method for formatting the email content.
    """

    @abstractmethod
    def send(self):
        pass

    @abstractmethod
    def format_content(self):
        pass

끝이다.

마지막으로 pseudo code 대신 진짜 클라이언트 코드로 보자.

factory = FastNotifFactory() ## if 문을 사용해서 동적으로 받을 수도 있음
send_notification(factory)
# FastNotifFactory.send_email_notification().send()가 실행된다.
>>Sending Email via FastNotif

아주 좋은 Medium Article의 도움을 받아 옮겨보았습니다.
https://medium.com/@amirm.lavasani/design-patterns-in-python-abstract-factory-2dcae06e5d29

profile
ML Product Engineer

0개의 댓글