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