🤓 지난 시간에 공부한 주제 >> 디자인 패턴
📍 디자인 패턴의 종류 >> 싱글톤, 팩토리, 이터레이터 패턴, 의존성주입 및 원칙
🔥 오늘 공부할 내용 >>
알고리즘을 캡슐화하고 / 이를 동적으로 교환하여 / 동일한 문제를 다른 방식으로 해결하는 패턴
'행위'에 관한 클래스들 간의 관계를 정의하는 패턴
전략패턴의 첫 번째 개념의 '동일한 문제를 다른 방식으로 해결' 부분에서 무언가 떠오를 수 있다.
바로 지난 시간에 공부한 의존성주입!
의존성주입의 이유인 '모듈 간 의존성을 느슨하게 만들어 쉽게 교체가능하게 만드는 점'이 전략패턴의 개념과 비슷하다.
하지만 둘은 다르다!!!
둘 다 디자인 패턴이지만, 그 목적에서 차이가 있다.
📍 목적 중심 차이점
전략패턴은 주로 행위나 알고리즘을 동적으로 교환하거나 다양한 방식으로 문제를 해결하기 위해 사용된다.
는 말이 너무 구구절절이고 어렵다면 아래 예시를 보면 더 잘 이해할 수 있을 것이다.
예시를 통해 알 수 있듯이, 전략패턴은 여러 방식(행위) 중 하나를 선택할 때 사용된다.
코드를 너무 잘 짰다면 그건 바로 지피티가 알려줬기 때문이다.
코드 하나하나 외우기보다는 흐름을 알기 위해서 넣어보았다. 결국 다 코딩해야하는 거니까!
주석을 따라 흐름을 파악할 수 있다.
# 1. 전략 인터페이스
class PaymentStrategy:
def pay(self, amount):
pass
# 2-1. 신용카드 결제 전략
class CreditCardPaymentStrategy(PaymentStrategy):
def __init__(self, card_number, cvv):
self.card_number = card_number
self.cvv = cvv
def pay(self, amount):
print(f"Paid ${amount} with credit card {self.card_number}")
# 2-2. 페이팔 결제 전략
class PayPalPaymentStrategy(PaymentStrategy):
def __init__(self, email, password):
self.email = email
self.password = password
def pay(self, amount):
print(f"Paid ${amount} with PayPal account {self.email}")
# 3. 주문 클래스(주문 금액(amount), 결제 방식 포함)
class Order:
def __init__(self, amount, payment_strategy):
self.amount = amount
self.payment_strategy = payment_strategy
def process_payment(self):
self.payment_strategy.pay(self.amount)
def set_strategy(self, payment_strategy):
self.payment_strategy = payment_strategy
# 4-1. 사용 예시
credit_card_payment = CreditCardPaymentStrategy("123456789", "123")
paypal_payment = PayPalPaymentStrategy("example@gmail.com", "password")
order1 = Order(100, credit_card_payment)
order1.process_payment()
order2 = Order(200, paypal_payment)
order2.process_payment()
# 4-2.동적으로 전략 변경
new_payment_strategy = PayPalPaymentStrategy("new_email@gmail.com", "new_password")
order2.set_strategy(new_payment_strategy)
order2.process_payment()
우리의 지피티에게 예상 면접 질문을 뽑아주라고 부탁했습니다.
다섯개나 알려줬지만 욕심부리지말고 딱! 한개만! 완전히 알아버리기!!
💁♂️ Q. 전략 패턴을 사용하는 프로그램에서 동적으로 전략을 변경하는 방법은 무엇인가요?
🙋 A. 클라이언트가 실행 중에 새로운 전략 객체를 생성하여 주입하거나, 런타임에 전략을 변경할 수 있는 메서드를 제공하는 것 입니다. 예를 들어 'set_strategy'를 사용할 수 있습니다.
💡 set_strategy가 낯선 나를 위한 설명~
이게 뭐야 했는데 알고보니 위에서 사용했다. 3번에 마지막 함수 부분!
이 메서드가 없어도 충분히 작동하지만, 'set_strategy'는 가독성과 명확성에 도움을 준다고 한다. 따라서 이를 활용하면 전략 변경이 더 명시적으로 이루어진다!!
이벤트 핸들링
: 버튼 클릭, 사용자 입력 등의 경우에 옵저버에게 이벤트 알람 전달
구독 모델
: 뉴스레터 구독, 푸시 알림 등에서 출판사, 서비스(주체)가 변경되면 구독자(옵저버)에게 알림 전달
# 1. 옵저버 인터페이스
class Observer:
def update(self, newsletter):
pass
# 2. 뉴스레터 주제 클래스
class NewsletterSubject:
def __init__(self):
self.observers = []
self.newsletter = None
# 2-1. 구독자(옵저버) 등록
def attach(self, observer):
self.observers.append(observer)
# 2-2. 구독자 해지
def detach(self, observer):
self.observers.remove(observer)
# 2-3. 뉴스레터의 상태 변화 알림
def notify(self):
for observer in self.observers:
observer.update(self.newsletter)
# 2-4. 뉴스레터(주체)의 상태: 2-3의 notify 메서드 호출
def set_newsletter(self, newsletter):
self.newsletter = newsletter
self.notify()
# 3. 뉴스레터 구독자 클래스
class Subscriber(Observer):
def __init__(self, name):
self.name = name
def update(self, newsletter):
print(f"{self.name} received the latest newsletter: {newsletter}")
# 4. 사용 예시
newsletter_subject = NewsletterSubject()
subscriber1 = Subscriber("John")
subscriber2 = Subscriber("Alice")
newsletter_subject.attach(subscriber1) # 구독자 추가
newsletter_subject.attach(subscriber2) # 구독자 추가
newsletter_subject.set_newsletter("May Newsletter") # 뉴스레터 업데이트
newsletter_subject.detach(subscriber2) # 구독자 제거
newsletter_subject.set_newsletter("June Newsletter") # 뉴스레터 업데이트
💁 Q. 옵저버 패턴은 어떤 문제를 해결하기 위해 사용되나요?
🙋♀️ A. 상태 변경 알림 문제를 해결할 수 있습니다. 어떤 객체에 의존하는 다른 객체들에게 자동으로 알림을 전달해 동작 수행 또는 상태를 업데이트합니다.
🔻 구체적인 원리
1. 클라이언트와 객체 사이에 프록시 제공
2. 클라이언트의 요청을 프록시가 가로채고 필요한 추가 동작 수행
3. 실제 객체에 전달
웹 프록시: 사용자와 웹 서버 사이에서 중개자 역할(사용자의 요청, 서버의 응답 전달)
보안 프록시: 네트워크 트래픽 감시, 악성코드 및 해킹등의 위협 탐지와 차단
게임 내 AI 캐릭터 행동 프록시(💡신기해서 좀 더 알아봄!)
: 복잡한 로직과 알고리즘을 필요로 하는 AI 캐릭터의 행동을 추상화 👉 복잡성 관리
: 실제 로직을 구현한 객체에 대한 접근 제어 👉 보안 문제 강화
: 플레이어와 상호작용에 사용
# 1. AI 행동 인터페이스
class AIAction:
def perform_action(self):
pass
# 2. 실제 AI 행동 구현 클래스
class AIActionImpl(AIAction):
def perform_action(self):
print("Performing AI action...")
# 3. AI 행동 프록시
class AIActionProxy(AIAction):
def __init__(self, ai_action):
self.ai_action = ai_action
def perform_action(self):
# 프록시에서 추가적인 동작을 수행할 수 있음
print("Proxy: Pre-action steps...")
self.ai_action.perform_action()
print("Proxy: Post-action steps...")
# 4. 게임 로직
ai_action = AIActionImpl()
ai_action_proxy = AIActionProxy(ai_action)
# 5. AI 행동 실행
ai_action_proxy.perform_action()
💁♂️ Q. 프록시 패턴과 데코레이터 패턴의 차이를 아시나요?
🙋 A. 둘 다 객체 간 중개자 역할을 수행한다는 공통점이 있지만, 각자의 목적에 차이가 있습니다. 프록시 패턴은 원본 객체에 대한 접근을 제어하고 보안, 로깅 등 부가적인 기능을 제공합니다. 데코레이터 패턴은 객체의 기능을 동적으로 확장하고 변경을 목적으로 사용됩니다.
이번 패턴은 위와 다르게 각자 비교 정리를 하려한다!
애플리케이션의 데이터, 비즈니스 로직, 사용자 인터페이스를 분리해 유지보수 및 확장성 향상
기존의 훼손 가능성이 높은 코드 구조를 개선하기 위해 등장
뷰와 모델 사이의 의존성을 제거하여 테스트 용이성과 유지보수성 향상
MVC 패턴에서 발생할 수 있는 뷰와 모델 간의 강한 결합을 완화
사용자 인터페이스와 비즈니스 로직 사이의 강한 의존성과 코드 중복을 해결하기 위해 등장
💁 Q. MVC 패턴에서 양방향 통신으로 인해 발생할 수 있는 문제점과 해결 방법에 대해 이야기해보세요.
🙋♀️ A. 각 구성요소 간 양방향 통신으로 인해 변경이 한 곳에서 다른 곳으로 전파되어 유지 및 확장, 흐름 추적이 어려워질 수 있습니다. 이를 해결하기 위해 뷰와 컨드롤러 간 중개자 객체를 도입해 상호작용 관리 및 복잡성을 줄일 수 있습니다.
위 질문에 대한 해결방법으로 '단방향 데이터 바인딩'이라는 또다른 해결방법도 존재한다. 그것이 다음 공부할 flux 패턴이다1!!
아휴 패턴 너무많아서 힘들다.
💁♂️ Q. Flux 패턴을 사용한 상태 관리의 장점은 무엇인가요?
🙋 A. 상태 변화가 예측 가능하고 일관성 있게 처리됩니다. Action → Dispatcher → Store → View의 단방향 흐름을 따르므로 상태 변화가 추적 가능하고 디버깅이 용이합니다.
🔻나는 답못하지만 이런게 나올수도있을듯~ 한 질문
💁♂️ Q. Flux 패턴과 관련되어 사용해본 라이브러리 또는 프레임워크가 있나요?
🙋 A. 사용해본적은 없지만 Redux를 알고있습니다.(사실방금앎) javascript 애플리케이션의 상태관리를 위해 사용되며, 컴포넌트 기반 UI 개발에 많이 활용됩니다.
끝!!!!!