[CS] 1.2. 디자인 패턴의 종류 2편

ofohj·2023년 5월 25일
0

CS

목록 보기
2/14
post-thumbnail

🤓 지난 시간에 공부한 주제 >> 디자인 패턴

📍 디자인 패턴의 종류 >> 싱글톤, 팩토리, 이터레이터 패턴, 의존성주입 및 원칙

🔥 오늘 공부할 내용 >>

  1. 전략 패턴
  2. 옵저버 패턴
  3. 프록시 패턴
  4. MVC, MVP, MVVM 패턴
  5. flux 패턴

1. 전략 패턴

개념

  • 알고리즘을 캡슐화하고 / 이를 동적으로 교환하여 / 동일한 문제를 다른 방식으로 해결하는 패턴

  • '행위'에 관한 클래스들 간의 관계를 정의하는 패턴

추가설명: 전략패턴과 의존성주입

전략패턴의 첫 번째 개념의 '동일한 문제를 다른 방식으로 해결' 부분에서 무언가 떠오를 수 있다.

바로 지난 시간에 공부한 의존성주입!
의존성주입의 이유인 '모듈 간 의존성을 느슨하게 만들어 쉽게 교체가능하게 만드는 점'이 전략패턴의 개념과 비슷하다.

하지만 둘은 다르다!!!

⚖️ 비교

둘 다 디자인 패턴이지만, 그 목적에서 차이가 있다.

📍 목적 중심 차이점

  • 의존성주입: 주입을 통해 객체 간 '결합도 약화' 및 '유연성 확보'
  • 전략패턴: 알고리즘 캡슐화를 통해 동적으로 교환 가능한 '구조 제공' 및 이를 통한 '문제 해결'

사용 예시

전략패턴은 주로 행위나 알고리즘을 동적으로 교환하거나 다양한 방식으로 문제를 해결하기 위해 사용된다.

는 말이 너무 구구절절이고 어렵다면 아래 예시를 보면 더 잘 이해할 수 있을 것이다.

  • 파일 압축 방식을 선택하는 경우
  • 로그인 및 회원가입 방법을 선택하는 경우
  • 결제 시스템에서 결제 방법을 선택하는 경우

예시를 통해 알 수 있듯이, 전략패턴은 여러 방식(행위) 중 하나를 선택할 때 사용된다.

코드: 카드결제

코드를 너무 잘 짰다면 그건 바로 지피티가 알려줬기 때문이다.
코드 하나하나 외우기보다는 흐름을 알기 위해서 넣어보았다. 결국 다 코딩해야하는 거니까!

주석을 따라 흐름을 파악할 수 있다.

# 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'는 가독성과 명확성에 도움을 준다고 한다. 따라서 이를 활용하면 전략 변경이 더 명시적으로 이루어진다!!


2. 옵저버 패턴

개념

  • 말 그대로 관찰자 입장에서 생각
  • 주체가 어떠한 객체의 상태 변화를 관찰하다가 / 변화가 있을 때마다 / 옵저버(해당 객체에 의존하는 다른 객체들)에게 자동으로 변경 사항을 알려주고 업데이트 수행
    👉 객체들 간 느슨한 결합과 유연성 제공

사용 예시

  • 이벤트 핸들링
    : 버튼 클릭, 사용자 입력 등의 경우에 옵저버에게 이벤트 알람 전달

  • 구독 모델
    : 뉴스레터 구독, 푸시 알림 등에서 출판사, 서비스(주체)가 변경되면 구독자(옵저버)에게 알림 전달

코드: 뉴스레터

# 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. 상태 변경 알림 문제를 해결할 수 있습니다. 어떤 객체에 의존하는 다른 객체들에게 자동으로 알림을 전달해 동작 수행 또는 상태를 업데이트합니다.


3. 프록시 패턴

개념

  • 실제 객체 사이에 대리자(proxy) 객체를 제공하여, 실제 객체에 대한 접근을 제어하고 보완

🔻 구체적인 원리
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. 둘 다 객체 간 중개자 역할을 수행한다는 공통점이 있지만, 각자의 목적에 차이가 있습니다. 프록시 패턴은 원본 객체에 대한 접근을 제어하고 보안, 로깅 등 부가적인 기능을 제공합니다. 데코레이터 패턴은 객체의 기능을 동적으로 확장하고 변경을 목적으로 사용됩니다.


4. MVC, MVP, MVVM 패턴

이번 패턴은 위와 다르게 각자 비교 정리를 하려한다!

1) MVC 패턴

구조

  • 모델: 애플리케이션의 데이터와 비즈니스 로직 담당
  • 뷰: 모델의 데이터를 시각적으로 표현, 사용자와 상호작용
  • 컨트롤러: 사용자의 입력 처리, 모델과 뷰 사이 상호작용 관리

등장 이유

애플리케이션의 데이터, 비즈니스 로직, 사용자 인터페이스를 분리해 유지보수 및 확장성 향상

무엇을 보완하기 위해 생겼는가?

기존의 훼손 가능성이 높은 코드 구조를 개선하기 위해 등장

2) MVP 패턴

구조

  • 모델: 애플리케이션의 데이터와 비즈니스 로직 담당(동일)
  • 뷰: 모델의 데이터를 시각적으로 표현, 사용자와 상호작용(동일)
  • 프리젠터: 모델과 뷰 사이 상호작용 관리, UI 로직 처리

목적

뷰와 모델 사이의 의존성을 제거하여 테스트 용이성과 유지보수성 향상

등장 이유

MVC 패턴에서 발생할 수 있는 뷰와 모델 간의 강한 결합을 완화

3) MVVM 패턴

구조

  • 모델: 애플리케이션의 데이터와 비즈니스 로직 담당(동일)
  • 뷰: 모델의 데이터를 시각적으로 표현, 사용자와 상호작용(동일)
  • 프리젠터: 뷰에 표시할 데이터 준비, 뷰와 모델 간의 통신 관리

목적

  • 뷰와 모델 사이의 의존성 제거
  • 데이터 바인딩을 통해 뷰를 자동으로 업데이트 👉 개발 생산성, 유지보수성 향상

등장 이유

사용자 인터페이스와 비즈니스 로직 사이의 강한 의존성과 코드 중복을 해결하기 위해 등장

예상 면접 질문

💁 Q. MVC 패턴에서 양방향 통신으로 인해 발생할 수 있는 문제점과 해결 방법에 대해 이야기해보세요.

🙋‍♀️ A. 각 구성요소 간 양방향 통신으로 인해 변경이 한 곳에서 다른 곳으로 전파되어 유지 및 확장, 흐름 추적이 어려워질 수 있습니다. 이를 해결하기 위해 뷰와 컨드롤러 간 중개자 객체를 도입해 상호작용 관리 및 복잡성을 줄일 수 있습니다.

위 질문에 대한 해결방법으로 '단방향 데이터 바인딩'이라는 또다른 해결방법도 존재한다. 그것이 다음 공부할 flux 패턴이다1!!


아휴 패턴 너무많아서 힘들다.

Flux 패턴

개념

  • 사용자 인터페이스의 상태 관리를 위한 애플리케이션 아키텍처 패턴
  • MVC 패턴의 문제를 해결할 수 있는 단방향 데이터 흐름을 가짐
  • 상태 변화에 대한 업데이트를 예측 가능하고 일관성있게 처리

구조

  • Action: 애플리케이션에서 발생하는 이벤트 또는 사용자의 상호작용을 나타냄
  • Dispatcher: Action을 전달
  • Store: 애플리케이션의 상태와 비즈니스 로직 관리
  • View: 사용자 인터페이스를 나타내고 Store의 상태에 따라 업데이트

예상 면접 질문

💁‍♂️ Q. Flux 패턴을 사용한 상태 관리의 장점은 무엇인가요?

🙋 A. 상태 변화가 예측 가능하고 일관성 있게 처리됩니다. Action → Dispatcher → Store → View의 단방향 흐름을 따르므로 상태 변화가 추적 가능하고 디버깅이 용이합니다.

🔻나는 답못하지만 이런게 나올수도있을듯~ 한 질문

💁‍♂️ Q. Flux 패턴과 관련되어 사용해본 라이브러리 또는 프레임워크가 있나요?
🙋 A. 사용해본적은 없지만 Redux를 알고있습니다.(사실방금앎) javascript 애플리케이션의 상태관리를 위해 사용되며, 컴포넌트 기반 UI 개발에 많이 활용됩니다.

끝!!!!!

0개의 댓글