Home Creational Patterns
Post
Cancel

Creational Patterns

Abstract Factory

Abstract Factory란?

  • 패턴은 관련이 있거나 의존적인 객체들의 가족을 생성하기 위한 인터페이스를 제공하는 생성 패턴
  • 패턴은 구체적인 클래스에 의존하지 않고 객체의 가족을 생성하기 용이
  • 서로 관련된 객체들을 하나의 주제나 개념으로 묶어 생성

장점

  • 관련 제품군 생성: 관련 제품(또는 객체)들을 하나의 가족으로 캡슐화하여, 가족 내 제품들이 서로 잘 협력할 수 있도록 함

  • 제품 호환성 보장: 팩토리를 통해 제품을 생성함으로써 생성된 객체들이 서로 호환될 수 있도록 보장

  • 제품 가족 전환의 용이성: 런타임 시 팩토리 객체를 변경함으로써 제품 가족을 쉽게 교체할 수 있어, 클라이언트 코드를 수정하지 않고도 전체 제품 세트를 교체에 용이

Abstract Factory vs Factory Method

  • Factory Method: 조건에 따른 생성을 팩토리 클래스로 위임하여, 팩토리 클래스에서 객체를 생성하는 패턴
  • Abstract Factory: 서로 관련이 있는 개체들을 통째로 묶어서 팩토리 클래스로 만들고, 이들 팩토리를 조건에 따라 생성하도록 다시 팩토리를 만들어서 객체를 생성하는 패턴

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
from __future__ import annotations
from abc import ABC, abstractmethod


class AbstractFactory(ABC):
    """
    The Abstract Factory interface declares a set of methods that return
    different abstract products. These products are called a family and are
    related by a high-level theme or concept. Products of one family are usually
    able to collaborate among themselves. A family of products may have several
    variants, but the products of one variant are incompatible with products of
    another.
    """

    @abstractmethod
    def create_iphone(self) -> AbstractProductIphone:
        pass

    @abstractmethod
    def create_galaxy(self) -> AbstractProductGalaxy:
        pass


class AppleFactory(AbstractFactory):

    def create_iphone(self) -> AbstractProductIphone:
        return Iphone15Pro()

    def create_galaxy(self) -> AbstractProductGalaxy:
        raise NotImplementedError("AppleFactory does not create Galaxy products.")


class SamsungFactory(AbstractFactory):
    def create_galaxy(self) -> AbstractProductGalaxy:
        return GalaxyS24()

    def create_iphone(self) -> AbstractProductIphone:
        raise NotImplementedError("SamsungFactory does not create iPhone products.")


class AbstractProductIphone(ABC):

    @abstractmethod
    def applepay(self) -> str:
        pass


class AbstractProductGalaxy(ABC):

    @abstractmethod
    def samsungpay(self) -> str:
        pass


class Iphone15Pro(AbstractProductIphone):
    def applepay(self) -> str:
        return "애플페이"


class GalaxyS24(AbstractProductGalaxy):
    def samsungpay(self) -> str:
        return "삼성페이"


def client_code(factory: AbstractFactory) -> None:
    """
    The client code works with factories and products only through abstract
    types: AbstractFactory and AbstractProduct. This lets you pass any factory
    or product subclass to the client code without breaking it.
    """
    try:
        product_a = factory.create_iphone()
        print(f"{product_a.applepay()}")
    except NotImplementedError as e:
        print(e)

    try:
        product_b = factory.create_galaxy()
        print(f"{product_b.samsungpay()}")
    except NotImplementedError as e:
        print(e)


if __name__ == "__main__":
    client_code(AppleFactory())
    client_code(SamsungFactory())

Singleton

Singleton이란?

  • 전역 변수를 사용하지 않고 객체를 하나만 생성
  • 생성된 객체를 어디에서든지 참조할 수 있도록 하는 패턴

장점

  • 단 한개의 인스턴스를 보장
  • 어디에서든 참조 가능

메타클래스란?

  • 메타클래스(metaclass)는 클래스의 행동을 정의하는 데 사용되는 클래스
  • 파이썬에서 클래스는 객체로 간주되며, 메타클래스는 이러한 클래스의 생성 및 초기화를 제어할 수 있는 특수한 클래스
  • 기본적으로 클래스는 type이라는 메타클래스로 생성되며, type은 클래스를 생성하는 데 사용되는 내장 메타클래스이다

코드

1. 메타클래스 사용 Singleton 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
### 메타데이터 사용 방법
class SingletonMeta(type):
    """
    The Singleton class can be implemented in different ways in Python. Some
    possible methods include: base class, decorator, metaclass. We will use the
    metaclass because it is best suited for this purpose.
    """

    _instances = {}

    def __call__(cls, *args, **kwargs):
        """
        Possible changes to the value of the `__init__` argument do not affect
        the returned instance.
        """
        if cls not in cls._instances:
            instance = super().__call__(*args, **kwargs)
            cls._instances[cls] = instance
        return cls._instances[cls]


class Singleton(metaclass=SingletonMeta):
    def some_business_logic(self):
        """
        Finally, any singleton should define some business logic, which can be
        executed on its instance.
        """

        # ...


if __name__ == "__main__":
    # The client code.

    s1 = Singleton()
    s2 = Singleton()

    if id(s1) == id(s2):
        print("Singleton works, both variables contain the same instance.")
    else:
        print("Singleton failed, variables contain different instances.")

2. 클래스 사용 Singleton 구현

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def some_logic(self):
        pass

3. Decorator 사용 Singleton 구현

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def singleton(cls):
    instances = {}

    def get_instance(*args, **kwargs):
        if cls not in instances:
            instances[cls] = cls(*args, **kwargs)
        return instances[cls]

    return get_instance


@singleton
class Singleton:
    def some_logic(self):
        pass


# 사용 예
s1 = Singleton()
s2 = Singleton()

if id(s1) == id(s2):
    print("Singleton works, both variables contain the same instance.")
else:
    print("Singleton failed, variables contain different instances.")

This post is licensed under CC BY 4.0 by the author.

ADSP 중요 포인트

Structural Patterns