디자인 패턴(2)

조정훈·2024년 3월 22일

옵저버 패턴

주체가 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 디자인 패턴
주체 : 객체의 상태 변화를 보고있는 관찰자
옵저버 : 이 객체의 상태 변화에 따라 전달되는 메서드 등을 기반으로 추가 변화사항이 생기는 객체

# 옵저버(Observer) 클래스
class Observer:
    def update(self, message):
        pass

# 구독자(Subscriber) 클래스
class Subscriber(Observer):
    def __init__(self, name):
        self.name = name

    def update(self, message):
        print(f"{self.name} received message: {message}")

# 발행자(Publisher) 클래스
class Publisher:
    def __init__(self):
        self.observers = []

    def add_observer(self, observer):
        self.observers.append(observer)

    def remove_observer(self, observer):
        self.observers.remove(observer)

    def notify_observers(self, message):
        for observer in self.observers:
            observer.update(message)

# 메인 함수
if __name__ == "__main__":
    # 발행자 객체 생성
    publisher = Publisher()

    # 구독자 객체들 생성 및 발행자에 등록
    subscriber1 = Subscriber("Subscriber 1")
    subscriber2 = Subscriber("Subscriber 2")
    publisher.add_observer(subscriber1)
    publisher.add_observer(subscriber2)

    # 발행자가 메시지를 발행하면 구독자들에게 알림
    publisher.notify_observers("Hello, World!")

subscriber1과 subscriber2 생성되고 이게 observers 라는 리스트에 각각 들어 간 다음 마지막 코드 publisher.notify_observers("Hello, World!")를 통해서 Publisher 클래스의 notify 함수 쪽에서 for문을 돌면서 각각Subcriber.update 를 실행하게 된다.

Subscriber 1 received message: Hello, World!
Subscriber 2 received message: Hello, World!

로 실행됨. 옵저버 패턴을 사용하여 발행자(publisher)와 구독자(subscriber) 간의 상호작용을 구현 할 수 있다.



프록시 패턴과 프록시 서버


프록시 패턴

은 객체의 대리자(Proxy) 또는 대변자 역할을 하는 객체를 사용하여 객체에 대한 접근을 제어하거나 중간에 추가적인 동작을 수행하는 디자인 패턴이다.
이를 통해 객체의 접근을 제한하거나 생성 및 초기화에 대한 부가적인 작업을 할 수 있다.
-> 프록시를 거쳐서 대신 뭔가를 수행시킨다고 생각하면 됨.
이렇게 대리자를 이용하는 이유는 클래스가 민감한정보를 갖고잇거나, 인스턴스화 하기에 무겁거나 추가기능을 넣고싶은데 원본 객체를 수정할수가 없는 상황일 때를 극복하기 위해서이다.

# 파일 읽기 인터페이스
class FileReader:
    def read_file(self, filename):
        pass

# 실제 파일 읽기 기능을 제공하는 클래스
class RealFileReader(FileReader):
    def read_file(self, filename):
        print(f"Reading file: {filename}")
        with open(filename, "r") as file:
            content = file.read()
        return content

# 파일 읽기 기능을 제어하는 프록시 클래스
class FileReaderProxy(FileReader):
    def __init__(self):
        self.real_reader = RealFileReader()

    def read_file(self, filename):
        # 파일 확장자가 .txt인 경우에만 실제 파일 읽기 기능을 호출
        if filename.endswith(".txt"):
            return self.real_reader.read_file(filename)
        else:
            return "Access denied: Only .txt files are allowed to read."

# 클라이언트 코드
if __name__ == "__main__":
    proxy = FileReaderProxy()

    # .txt 파일 읽기
    print(proxy.read_file("example.txt"))

    # .csv 파일 읽기 시도
    print(proxy.read_file("example.csv"))

FileReaderProxy 클래스에서 조건문을 달아서 txt파일 일 경우에만 읽을 수 있도록 제어할 수 있다.


프록시 서버

서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용프로그램

사용하는 이유
1. 보안강화
2. 접근제어
3. 캐시
4. 로깅

단점
1. 성능저하 - 중개역할을 하기 때문에 네트워크 지연이 발생할 수 있다.
2. 단일 장애점 - 프록시서버에 장애생기면 전체 네트워크에 영향을 줌
3. 보안 위협 - 서버와 클라이언트 중간자공격같은 보안공격 당할 수 있음



이터레이터 패턴

이터레이터를 사용하여 컬렉션의 요소들에 순차적으로 접근하는 디자인 패턴.
이터레이터는 컬렉션의 내부 구조를 노출하지 않고도 컬렉션의 각 요소에 접근할 수 있도록 해준다.

# 이터레이터 클래스 정의
class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index >= len(self.data):
            raise StopIteration
        value = self.data[self.index]
        self.index += 1
        return value

# 컬렉션 클래스 정의
class MyCollection:
    def __init__(self):
        self.data = []

    def add_item(self, item):
        self.data.append(item)

    def __iter__(self):
        return MyIterator(self.data)

# 클라이언트 코드
if __name__ == "__main__":
    collection = MyCollection()
    collection.add_item(1)
    collection.add_item(2)
    collection.add_item(3)

    # 이터레이터를 사용하여 컬렉션의 요소에 접근
    iterator = iter(collection)
    for item in iterator:
        print(item)

0개의 댓글