Akashic Records

5.5 디자인 패턴 본문

Python for Beginners

5.5 디자인 패턴

Andrew's Akashic Records 2023. 3. 20. 14:48
728x90

디자인 패턴(Design Pattern)은 소프트웨어 디자인에서 반복적으로 발생하는 문제를 해결하기 위한 재사용 가능한 솔루션입니다. 여러 가지 디자인 패턴이 있으며, 각 패턴은 특정한 상황에서 적합한 방법론을 제공합니다. 파이썬에서도 다양한 디자인 패턴을 사용할 수 있습니다. 여기서는 대표적인 디자인 패턴을 소개하고 예시 코드를 작성하겠습니다.

싱글턴 패턴 (Singleton Pattern)

싱글턴 패턴은 클래스의 인스턴스가 하나만 생성되도록 보장하는 패턴입니다. 이 패턴은 전역 변수를 사용하지 않고, 전역적으로 접근 가능한 단일 인스턴스를 만들 때 사용됩니다.

예시 코드:

class Singleton:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

singleton1 = Singleton()
singleton2 = Singleton()

print(singleton1 is singleton2)  # 출력: True

팩토리 패턴 (Factory Pattern)

팩토리 패턴은 객체 생성을 캡슐화하여, 클래스를 직접 인스턴스화하는 대신 사용자가 요청한 타입의 객체를 생성하는 패턴입니다. 이 패턴은 객체 생성 로직을 분리하여 코드의 유연성을 높입니다.

예시 코드:

class Dog:
    def speak(self):
        return "Woof!"

class Cat:
    def speak(self):
        return "Meow!"

class AnimalFactory:
    def create_animal(self, animal_type):
        if animal_type == "Dog":
            return Dog()
        elif animal_type == "Cat":
            return Cat()
        else:
            raise ValueError("Invalid animal type")

factory = AnimalFactory()
animal = factory.create_animal("Dog")
print(animal.speak())  # 출력: Woof!

옵저버 패턴 (Observer Pattern)

옵저버 패턴은 한 객체의 상태가 변경될 때, 그 객체에 의존하는 다른 객체들에게 변경 사항을 자동으로 알리는 패턴입니다. 이 패턴은 객체 간의 느슨한 결합을 유지하면서 상태 변경을 효과적으로 전파할 수 있습니다.

예시 코드:

class Observer:
    def update(self, subject):
        pass

class Subject:
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        if observer not in self._observers:
            self._observers.append(observer)

    def detach(self, observer):
        self._observers.remove(observer)

    def notify(self):
        for observer in self._observers:
            observer.update(self)

class ConcreteSubject(Subject):
    def __init__(self, state):
        super().__init__()
        self._state = state

    def get_state(self):
       return self._state
   
    def set_state(self, state):
       self._state = state
       self.notify()
    
class ConcreteObserver(Observer):
	def init(self, name):
		self._name = name

	def update(self, subject):
		print(f"{self._name}: Subject state changed to {subject.get_state()}")

subject = ConcreteSubject("Initial State")

observer1 = ConcreteObserver("Observer 1")
observer2 = ConcreteObserver("Observer 2")

subject.attach(observer1)
subject.attach(observer2)

subject.set_state("New State") 
# 출력: Observer 1: Subject state changed to New State
# 출력: Observer 2: Subject state changed to New State

subject.detach(observer1)

subject.set_state("Another State") # 출력: Observer 2: Subject state changed to Another State

위의 예시 코드에서, `ConcreteSubject` 클래스는 상태를 변경할 때마다 관찰자들에게 알림을 전송하며, `ConcreteObserver` 클래스는 상태 변경 알림을 받아 처리합니다. 이처럼 옵저버 패턴은 상태 변경을 전파하고자 하는 객체들 사이의 관계를 유지하면서도 느슨한 결합을 가능하게 합니다.

데코레이터 패턴 (Decorator Pattern)

데코레이터 패턴은 객체에 새로운 기능을 동적으로 추가할 수 있는 패턴입니다. 이 패턴은 기존 코드를 수정하지 않고도 객체의 기능을 확장할 수 있습니다. 파이썬에서 데코레이터 패턴은 데코레이터 함수를 사용하여 구현할 수 있습니다.

예시 코드:

def bold_decorator(func):
    def wrapper():
        return f"<b>{func()}</b>"
    return wrapper

def italic_decorator(func):
    def wrapper():
        return f"<i>{func()}</i>"
    return wrapper

@italic_decorator
@bold_decorator
def hello():
    return "Hello, World!"

print(hello())  # 출력: <i><b>Hello, World!</b></i>

스트래티지 패턴 (Strategy Pattern)

스트래티지 패턴은 알고리즘을 정의하는 인터페이스를 만들고, 각 알고리즘을 캡슐화하여 교체 가능하게 하는 패턴입니다. 이 패턴은 알고리즘을 사용하는 클라이언트와 독립적으로 알고리즘을 변경하거나 확장할 수 있습니다.

예시 코드:

from abc import ABC, abstractmethod

class Strategy(ABC):
    @abstractmethod
    def execute(self, data):
        pass

class ConcreteStrategyA(Strategy):
    def execute(self, data):
        return sorted(data)

class ConcreteStrategyB(Strategy):
    def execute(self, data):
        return sorted(data, reverse=True)

class Context:
    def __init__(self, strategy):
        self._strategy = strategy

    def set_strategy(self, strategy):
        self._strategy = strategy

    def execute_strategy(self, data):
        return self._strategy.execute(data)

data = [1, 5, 3, 9, 7]
context = Context(ConcreteStrategyA())
print(context.execute_strategy(data))  # 출력: [1, 3, 5, 7, 9]

context.set_strategy(ConcreteStrategyB())
print(context.execute_strategy(data))  # 출력: [9, 7, 5, 3, 1]

퍼사드 패턴 (Facade Pattern)

퍼사드 패턴은 복잡한 서브시스템의 인터페이스를 단순화하는 패턴입니다. 클라이언트와 서브시스템 사이에 퍼사드 클래스를 생성하여 클라이언트가 서브시스템을 쉽게 사용할 수 있도록 합니다.

예시 코드:

class SubsystemA:
    def operation_a(self):
        return "Subsystem A: operation A"

class SubsystemB:
    def operation_b(self):
        return "Subsystem B: operation B"

class Facade:
    def __init__(self):
        self._subsystem_a = SubsystemA()
        self._subsystem_b = SubsystemB()

    def operation(self):
        result = []
        result.append(self._subsystem_a.operation_a())
        result.append(self._subsystem_b.operation_b())
        return result

facade = Facade()
print(facade.operation())  # 출력: ['Subsystem A: operation A', 'Subsystem B: operation B']

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

팩토리 메서드 패턴은 객체 생성을 서브클래스에 위임하는 패턴입니다. 이 패턴은 객체를 생성하는 인터페이스를 정의하고, 서브클래스에서 구체적인 객체를 생성하도록 합니다.

예시 코드:

from abc import ABC, abstractmethod

class Product(ABC):
    @abstractmethod
    def operation(self):
        pass

class ConcreteProductA(Product):
    def operation(self):
        return "ConcreteProductA: operation"

class ConcreteProductB(Product):
    def operation(self):
        return "ConcreteProductB: operation"

class Creator(ABC):
    @abstractmethod
    def factory_method(self):
        pass

    def operation(self):
        product = self.factory_method()
        return product.operation()

class ConcreteCreatorA(Creator):
    def factory_method(self):
        return ConcreteProductA()

class ConcreteCreatorB(Creator):
    def factory_method(self):
        return ConcreteProductB()

creator_a = ConcreteCreatorA()
print(creator_a.operation())  # 출력: ConcreteProductA: operation

creator_b = ConcreteCreatorB()
print(creator_b.operation())  # 출력: ConcreteProductB: operation

 

디자인 패턴은 소프트웨어 디자인에서 일반적으로 발생하는 문제에 대한 해결책을 제공합니다. 이러한 패턴들은 코드의 구조를 개선하고 유지 보수성, 확장성, 재사용성을 향상시키는데 도움이 됩니다. 파이썬과 같은 프로그래밍 언어에서 디자인 패턴을 사용하면, 프로젝트의 전반적인 품질을 높이고 개발자의 역량을 향상시킬 수 있습니다. 이외에도 다양한 디자인 패턴들이 있으며, 각 패턴은 특정한 상황에 맞게 적용하여 최적의 솔루션을

728x90

'Python for Beginners' 카테고리의 다른 글

6.2 에러 종류와 대처법  (0) 2023.03.21
6.1 예외 처리  (0) 2023.03.21
5.4 캡슐화  (0) 2023.03.20
5.3 다형성  (0) 2023.03.20
5.2 상속  (0) 2023.03.20
Comments