Python은 배우기 쉽고 유연하며 강력한 매우 인기 있는 프로그래밍 언어로, 다양한 분야에서 널리 사용됩니다. 그러나 많은 사람들은 Python이 절차 지향 언어이며 객체 지향 프로그래밍 스타일을 잘 지원하지 못한다고 생각합니다.
이 생각은 잘못된 것입니다. Python은 객체 지향 프로그래밍을 지원할 뿐만 아니라 디자인 패턴도 효과적으로 적용할 수 있습니다.
디자인 패턴이란 무엇인가?
디자인 패턴(Design Pattern)은 널리 인정받고 검증된 프로그래밍 경험의 집합입니다. 이는 다양한 프로그래밍 시나리오에서 적용할 수 있는 일반적인 솔루션을 제공합니다. 디자인 패턴의 등장은 코드 재사용, 시스템 확장성, 코드 가독성 등 소프트웨어 개발의 일반적인 문제를 해결하기 위함입니다.
디자인 패턴을 사용하는 이유는?
디자인 패턴을 사용하는 이점은 다음과 같습니다:
코드 재사용: 디자인 패턴을 사용하면 코드를 분해하고 조합하여 코드 재사용을 달성할 수 있습니다.
시스템 확장성: 디자인 패턴은 시스템을 더 유연하게 만들고, 확장이 용이하며, 다양한 요구에 적응할 수 있게 합니다.
코드 가독성: 디자인 패턴을 사용하면 코드의 가독성을 높여 코드가 더 명확해집니다.
파이썬에서의 디자인 패턴
파이썬에서의 디자인 패턴은 다른 언어에서의 디자인 패턴과 유사하지만, 몇 가지 차이점이 있습니다. 파이썬에서의 디자인 패턴은 크게 세 가지로 나눌 수 있습니다: 생성 패턴, 구조 패턴, 행동 패턴.
이번 글에서는 몇 가지 일반적인 패턴을 설명합니다.
1. 팩토리 패턴 (Factory Pattern)
팩토리 패턴은 생성 패턴으로, 객체를 생성하는 가장 좋은 방법을 제공합니다. 이 패턴은 객체의 생성과 사용을 분리하여 객체 생성을 더 유연하게 만듭니다.
파이썬에서 팩토리 패턴을 사용하여 다양한 객체를 생성할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Dog : def __init__ (self) : self.name = "dog" class Cat : def __init__ (self) : self.name = "cat" class AnimalFactory : def create_animal (self, animal_type) : if animal_type == "dog" : return Dog() elif animal_type == "cat" : return Cat() else : return None factory = AnimalFactory() animal = factory.create_animal("dog" ) print(animal.name)
2. 싱글톤 패턴 (Singleton Pattern)
싱글톤 패턴은 클래스에 하나의 인스턴스만 존재하도록 보장하며, 전역 접근 지점을 제공합니다.
파이썬에서는 데코레이터를 사용하여 싱글톤 패턴을 구현할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Singleton : __instance = None def __new__ (cls) : if cls.__instance is None : cls.__instance = super().__new__(cls) return cls.__instance a = Singleton() b = Singleton() print(a is b)
3. 어댑터 패턴 (Adapter Pattern)
어댑터 패턴은 구조 패턴으로, 클래스의 인터페이스를 클라이언트가 원하는 다른 인터페이스로 변환합니다.
파이썬에서는 어댑터 패턴을 사용하여 호환되지 않는 인터페이스 간의 호환성을 구현할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Target : def request (self) : pass class Adaptee : def specific_request (self) : pass class Adapter (Target) : def __init__ (self, adaptee) : self.adaptee = adaptee def request (self) : self.adaptee.specific_request() adaptee = Adaptee() adapter = Adapter(adaptee) adapter.request()
4. 데코레이터 패턴 (Decorator Pattern)
데코레이터 패턴은 구조 패턴으로, 객체에 새로운 동작을 동적으로 추가할 수 있습니다.
파이썬에서는 데코레이터 함수를 사용하여 함수나 클래스의 동작을 수정할 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 def logging (func) : def wrapper (*args, **kwargs) : print("call function:" , func.__name__) return func(*args, **kwargs) return wrapper @logging def foo () : print("hello world" ) foo()
5. 옵저버 패턴 (Observer Pattern)
옵저버 패턴은 행동 패턴으로, 객체 간 일대다 관계를 정의하여, 객체의 상태가 변경될 때 이를 의존하는 모든 객체가 자동으로 업데이트됩니다.
파이썬에서는 옵저버 패턴을 사용하여 이벤트 중심의 프로그래밍을 구현할 수 있습니다.
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 class Subject : def __init__ (self) : self.observers = [] def attach (self, observer) : self.observers.append(observer) def detach (self, observer) : self.observers.remove(observer) def notify (self) : for observer in self.observers: observer.update(self) class Observer : def update (self, subject) : pass class ConcreteSubject (Subject) : def __init__ (self) : super().__init__() self.state = 0 def get_state (self) : return self.state def set_state (self, state) : self.state = state self.notify() class ConcreteObserver (Observer) : def update (self, subject) : print("state changed to:" , subject.get_state()) subject = ConcreteSubject() observer = ConcreteObserver() subject.attach(observer) subject.set_state(1 )
6. 빌더 패턴 (Builder Pattern)
빌더 패턴은 복잡한 객체의 생성 과정을 그 표현과 분리하여 동일한 생성 과정으로 다양한 표현을 만들 수 있게 합니다.
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 class Director : def construct_car (self, builder) : builder.create_new_car() builder.add_model() builder.add_engine() class Builder : def __init__ (self) : self.car = None def create_new_car (self) : self.car = Car() def get_car (self) : return self.car class CarBuilder (Builder) : def add_model (self) : self.car.model = "Sports Car" def add_engine (self) : self.car.engine = "V8" class Car : def __init__ (self) : self.model = None self.engine = None director = Director() car_builder = CarBuilder() director.construct_car(car_builder) car = car_builder.get_car() print(f"Car Model: {car.model} , Engine: {car.engine} " )
7. 전략 패턴 (Strategy Pattern)
전략 패턴은 알고리즘 군을 정의하고, 이를 캡슐화하며, 이 알고리즘을 상호 교체 가능하게 만드는 패턴입니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class PaymentContext : def __init__ (self, payment_strategy) : self.payment_strategy = payment_strategy def execute_payment (self, amount) : return self.payment_strategy.pay(amount) class CreditCardPayment : def pay (self, amount) : return f"Paying ${amount} using Credit Card." class PayPalPayment : def pay (self, amount) : return f"Paying ${amount} using PayPal." credit_card_payment = CreditCardPayment() paypal_payment = PayPalPayment() context_credit_card = PaymentContext(credit_card_payment) context_paypal = PaymentContext(paypal_payment) print(context_credit_card.execute_payment(100 )) print(context_paypal.execute_payment(50 ))
8. 책임 연쇄 패턴 (Chain of Responsibility Pattern)
책임 연쇄 패턴은 요청을 처리할 수 있는 여러 핸들러가 체인으로 연결되어 각 핸들러가 요청을 처리할지 또는 다음 핸들러로 전달할지 결정하는 패턴입니다.
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 class Handler : def __init__ (self, successor=None) : self.successor = successor def handle_request (self, request) : if self.successor: self.successor.handle_request(request) class ConcreteHandlerA (Handler) : def handle_request (self, request) : if request == "A" : print("Handler A processing request A." ) else : super().handle_request(request) class ConcreteHandlerB (Handler) : def handle_request (self, request) : if request == "B" : print("Handler B processing request B." ) else : super().handle_request(request) class Client : def __init__ (self) : self.handler_chain = ConcreteHandlerA(ConcreteHandlerB()) def make_request (self, request) : self.handler_chain.handle_request(request) client = Client() client.make_request("A" ) client.make_request("B" )
요약
Python은 절차 지향 언어일 뿐만 아니라 객체 지향 프로그래밍 스타일도 지원합니다. 디자인 패턴을 사용하면 Python의 객체 지향 기능을 더 잘 활용하여 코드를 더 유연하고, 읽기 쉽고, 유지보수가 용이하게 만들 수 있습니다.
파이썬에서의 디자인 패턴에는 팩토리 패턴, 싱글톤 패턴, 어댑터 패턴, 데코레이터 패턴, 옵저버 패턴 등이 포함됩니다. 이러한 패턴들은 다양한 프로그래밍 시나리오에 적용할 수 있으며, 소프트웨어 개발의 공통적인 문제를 해결하는 데 도움을 줍니다.