
이는 뭐.. 라이브러리나 함수처럼 붙여넣기는 불가능해요!
하지만 전형적인 패턴 방식을 사용함으로서 문제 해결을 쉽게 하기 위함이라 말할 수 있다!
뭐 알고리즘이랑 비슷한가 했지만 그건 아니다!
오히려 더 넓은 청사진? 느낌이랄까
AI가 만들어준 코드를 같이 봐보자
객체 생성을 서브클래스에 위임하는 패턴
클라이언트 코드가 구체적인 클래스 대신 인터페이스와 상호작용
예: 다양한 타입의 문서를 생성하는 문서 작성 애플리케이션
from abc import ABC, abstractmethod
class Product(ABC):
@abstractmethod
def operation(self):
pass
class ConcreteProductA(Product):
def operation(self):
return "Product A 작동"
class ConcreteProductB(Product):
def operation(self):
return "Product B 작동"
class Creator(ABC):
@abstractmethod
def factory_method(self):
pass
def some_operation(self):
product = self.factory_method()
return f"Creator: {product.operation()}"
class ConcreteCreatorA(Creator):
def factory_method(self):
return ConcreteProductA()
class ConcreteCreatorB(Creator):
def factory_method(self):
return ConcreteProductB()
관련된 객체들의 집합을 생성하기 위한 인터페이스 제공
구체적인 클래스를 지정하지 않고 객체들의 집합을 생성
예: GUI 테마 생성 (윈도우, 버튼, 체크박스 등)
class AbstractFactory(ABC):
@abstractmethod
def create_product_a(self):
pass
@abstractmethod
def create_product_b(self):
pass
class ConcreteFactory1(AbstractFactory):
def create_product_a(self):
return ConcreteProductA1()
def create_product_b(self):
return ConcreteProductB1()
class ConcreteFactory2(AbstractFactory):
def create_product_a(self):
return ConcreteProductA2()
def create_product_b(self):
return ConcreteProductB2()
복잡한 객체의 생성 과정을 단계별로 분리
같은 생성 과정으로 다양한 표현의 객체 생성 가능
예: 복잡한 문서나 XML 생성
class Product:
def __init__(self):
self.parts = []
def add(self, part):
self.parts.append(part)
def list_parts(self):
return f"제품 부품: {', '.join(self.parts)}"
class Builder(ABC):
@abstractmethod
def build_part_a(self):
pass
@abstractmethod
def build_part_b(self):
pass
@abstractmethod
def get_result(self):
pass
class ConcreteBuilder(Builder):
def __init__(self):
self.product = Product()
def build_part_a(self):
self.product.add("부품 A")
def build_part_b(self):
self.product.add("부품 B")
def get_result(self):
return self.product
class Director:
def __init__(self, builder):
self._builder = builder
def build_minimal_product(self):
self._builder.build_part_a()
def build_full_product(self):
self._builder.build_part_a()
self._builder.build_part_b()
기존 객체를 복제하여 새로운 객체 생성
객체 생성 비용이 높을 때 유용
깊은 복사(deep copy)와 얕은 복사(shallow copy) 지원
import copy
class Prototype(ABC):
@abstractmethod
def clone(self):
pass
class ConcretePrototype(Prototype):
def __init__(self, value):
self.value = value
self.complex_obj = [1, 2, 3]
def clone(self):
# 깊은 복사를 위해 copy.deepcopy() 사용
return copy.deepcopy(self)
클래스의 인스턴스를 하나만 생성하도록 보장
전역 상태 관리, 리소스 공유에 사용
스레드 세이프한 구현 필요
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
호환되지 않는 인터페이스를 클라이언트가 기대하는 인터페이스로 변환
기존 코드의 수정 없이 새로운 인터페이스 적용 가능
예: 서드파티 라이브러리 통합
class Target:
def request(self):
return "표준 요청"
class Adaptee:
def specific_request(self):
return "특정 요청"
class Adapter(Target):
def __init__(self, adaptee):
self._adaptee = adaptee
def request(self):
return f"어댑터: {self._adaptee.specific_request()}"
추상화와 구현을 분리하여 독립적으로 변경 가능하게 함
복잡한 계층 구조 간소화
예: 다양한 플랫폼에서 작동하는 그래픽 드로잉 라이브러리
class Implementor(ABC):
@abstractmethod
def operation_implementation(self):
pass
class ConcreteImplementorA(Implementor):
def operation_implementation(self):
return "구현체 A"
class ConcreteImplementorB(Implementor):
def operation_implementation(self):
return "구현체 B"
class Abstraction:
def __init__(self, implementor):
self._implementor = implementor
def operation(self):
return f"추상화: {self._implementor.operation_implementation()}"
객체들을 트리 구조로 구성
개별 객체와 복합 객체를 동일하게 다룰 수 있음
예: 파일 시스템, UI 컴포넌트 트리
class Component(ABC):
@abstractmethod
def operation(self):
pass
def add(self, component):
pass
def remove(self, component):
pass
class Leaf(Component):
def operation(self):
return "리프"
class Composite(Component):
def __init__(self):
self._children = []
def add(self, component):
self._children.append(component)
def remove(self, component):
self._children.remove(component)
def operation(self):
return f"복합체: {[child.operation() for child in self._children]}"
객체에 동적으로 새로운 기능 추가
상속 대신 래퍼(wrapper) 사용
예: 입출력 스트림 확장, UI 컴포넌트 스타일링
class BaseComponent:
def operation(self):
return "기본 컴포넌트"
class Decorator(BaseComponent):
def __init__(self, component):
self._component = component
def operation(self):
return self._component.operation()
class ConcreteDecoratorA(Decorator):
def operation(self):
return f"데코레이터 A({self._component.operation()})"
class ConcreteDecoratorB(Decorator):
def operation(self):
return f"데코레이터 B({self._component.operation()})"
복잡한 서브시스템에 대한 단순화된 인터페이스 제공
클라이언트와 서브시스템 간 결합도 감소
예: 복잡한 라이브러리 사용 단순화
class SubsystemA:
def operation_a(self):
return "서브시스템 A 작동"
class SubsystemB:
def operation_b(self):
return "서브시스템 B 작동"
class Facade:
def __init__(self):
self._subsystem_a = SubsystemA()
self._subsystem_b = SubsystemB()
def operation(self):
return f"{self._subsystem_a.operation_a()} + {self._subsystem_b.operation_b()}"
메모리 사용을 최적화하기 위해 객체를 공유
대량의 유사 객체 생성 시 메모리 절약
예: 그래픽 에디터의 문자 렌더링
class Flyweight:
def __init__(self, shared_state):
self._shared_state = shared_state
def operation(self, unique_state):
return f"공유 상태: {self._shared_state}, 고유 상태: {unique_state}"
class FlyweightFactory:
def __init__(self):
self._flyweights = {}
def get_flyweight(self, key):
if key not in self._flyweights:
self._flyweights[key] = Flyweight(key)
return self._flyweights[key]
다른 객체에 대한 접근을 제어하는 대리 객체
지연 로딩, 접근 제어, 로깅 등에 사용
예: 원격 리소스 접근, 대용량 이미지 로딩
class RealSubject:
def request(self):
return "실제 주체 요청"
class Proxy:
def __init__(self, real_subject):
self._real_subject = real_subject
def request(self):
print("프록시: 접근 제어")
return self._real_subject.request()
요청을 처리할 수 있는 객체를 동적으로 찾아 전달
요청 처리 객체 간 느슨한 결합
예: 로깅, 이벤트 핸들링 시스템
class Handler(ABC):
def __init__(self, next_handler=None):
self._next_handler = next_handler
@abstractmethod
def handle(self, request):
pass
class ConcreteHandlerA(Handler):
def handle(self, request):
if request == "A":
return "핸들러 A 처리"
elif self._next_handler:
return self._next_handler.handle(request)
return "요청 처리 불가"
class ConcreteHandlerB(Handler):
def handle(self, request):
if request == "B":
return "핸들러 B 처리"
elif self._next_handler:
return self._next_handler.handle(request)
return "요청 처리 불가"
요청을 객체로 캡슐화
요청 실행, 취소, 재실행 지원
예: 메뉴 항목, 매크로, 실행 취소/다시 실행
class Command(ABC):
@abstractmethod
def execute(self):
pass
class Receiver:
def action_a(self):
return "액션 A 수행"
def action_b(self):
return "액션 B 수행"
class ConcreteCommand(Command):
def __init__(self, receiver):
self._receiver = receiver
def execute(self):
return self._receiver.action_a()
컬렉션 내부 표현을 노출하지 않고 순회 기능 제공
다양한 컬렉션 타입에 대해 일관된 순회 방식
예: 리스트, 트리, 그래프 순회
class Iterator(ABC):
@abstractmethod
def has_next(self):
pass
@abstractmethod
def next(self):
pass
class Collection(ABC):
@abstractmethod
def create_iterator(self):
pass
class ConcreteCollection(Collection):
def __init__(self):
self._items = []
def add_item(self, item):
self._items.append(item)
def create_iterator(self):
return ConcreteIterator(self)
class ConcreteIterator(Iterator):
def __init__(self, collection):
self._collection = collection
self._index = 0
def has_next(self):
return self._index < len(self._collection._items)
def next(self):
item = self._collection._items[self._index]
self._index += 1
return item
객체 간 직접 통신 대신 중앙 중재자를 통해 통신
객체 간 결합도 감소
예: 대화상자, 채팅 애플리케이션
class Mediator(ABC):
@abstractmethod
def send(self, message, colleague):
pass
class ConcreteMediator(Mediator):
def __init__(self):
self._colleague1 = None
self._colleague2 = None
def set_colleague1(self, colleague):
self._colleague1 = colleague
def set_colleague2(self, colleague):
self._colleague2 = colleague
def send(self, message, colleague):
if colleague == self._colleague1:
self._colleague2.notify(message)
else:
self._colleague1.notify(message)
class Colleague:
def __init__(self, mediator):
self._mediator = mediator
class ConcreteColleague1(Colleague):
def send(self, message):
self._mediator.send(message, self)
def notify(self, message):
print(f"동료 1 수신: {message}")
class ConcreteColleague2(Colleague):
def send(self, message):
self._mediator.send(message, self)
def notify(self, message):
print(f"동료 2 수신: {message}")
객체의 상태를 저장하고 복원
되돌리기(Undo) 기능 구현
예: 문서 편집기의 실행 취소 기능
class Memento:
def __init__(self, state):
self._state = state
def get_state(self):
return self._state
class Originator:
def __init__(self):
self._state = None
def set_state(self, state):
self._state = state
def save_to_memento(self):
return Memento(self._state)
def restore_from_memento(self, memento):
self._state = memento.get_state()
class Caretaker:
def __init__(self):
self._mementos = []
def add_memento(self, memento):
self._mementos.append(memento)
def get_memento(self, index):
return self._mementos[index]
객체 상태 변경 시 의존 객체들에 자동으로 알림
발행-구독(Publish-Subscribe) 모델
예: 이벤트 처리, UI 업데이트
class Subject:
def __init__(self):
self._observers = []
self._state = None
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._state)
def set_state(self, state):
self._state = state
self.notify()
class Observer(ABC):
@abstractmethod
def update(self, state):
pass
class ConcreteObserver(Observer):
def update(self, state):
print(f"옵저버 상태 업데이트: {state}")
객체의 내부 상태에 따라 행동을 동적으로 변경
상태 전환 로직을 객체로 캡슐화
예: 상태 머신, 게임 캐릭터 상태
class State(ABC):
@abstractmethod
def handle(self):
pass
class ConcreteStateA(State):
def handle(self):
return "상태 A 처리"
class ConcreteStateB(State):
def handle(self):
return "상태 B 처리"
class Context:
def __init__(self, state):
self._state = state
def set_state(self, state):
self._state = state
def request(self):
return self._state.handle()
런타임에 알고리즘 선택 가능
알고리즘 계열을 정의하고 상호 교환 가능하게 함
예: 정렬 알고리즘, 결제 방식
class Strategy(ABC):
@abstractmethod
def do_algorithm(self):
pass
class ConcreteStrategyA(Strategy):
def do_algorithm(self):
return "전략 A 알고리즘"
class ConcreteStrategyB(Strategy):
def do_algorithm(self):
return "전략 B 알고리즘"
class Context:
def __init__(self, strategy):
self._strategy = strategy
def set_strategy(self, strategy):
self._strategy = strategy
def execute_strategy(self):
return self._strategy.do_algorithm()
알고리즘의 골격을 정의하고 일부 단계를 서브클래스에 위임
코드 재사용성 증가
예: 문서 생성 프레임워크
class AbstractClass(ABC):
def template_method(self):
self.base_operation1()
self.required_operation1()
self.base_operation2()
self.hook1()
self.required_operation2()
self.base_operation3()
self.hook2()
def base_operation1(self):
print("기본 작업 1")
def base_operation2(self):
print("기본 작업 2")
def base_operation3(self):
print("기본 작업 3")
def hook1(self):
pass
def hook2(self):
pass
@abstractmethod
def required_operation1(self):
pass
@abstractmethod
def required_operation2(self):
pass
class ConcreteClass(AbstractClass):
def required_operation1(self):
print("구체적인 작업 1")
def required_operation2(self):
print("구체적인 작업 2")
def hook1(self):
print("구체적인 후크 1")
알고리즘을 객체 구조에서 분리
새로운 연산을 기존 객체 구조 변경 없이 추가
예: 컴파일러의 AST(Abstract Syntax Tree) 방문
# 10. 비지터 (Visitor)
class Element(ABC):
@abstractmethod
이번에 프로젝트 코드리뷰를 하며 배운 점은 디자인 패턴을 잘 쓰는 것도 중요하다이다
중복을 제거하되 코드를 보는 사람이 명확하고 쉽게 알 수 있어야 하더라
그렇게 abstract method로 class를 구상하고 그걸 상속받게 하니 자녀클래스에서 기능을 만들 때
일관성을 유지할 수 있었고 각 기능에 대한 설명이 들어가있으니 사용자도 쉽게 알 수 있었다.
그 후 싱글톤을 통해 DB 연결을 1회만 인스턴스화로 연결하여 사용하기도 했으며,
커맨드 패턴으로 요청을 클래스로 따로 빼고 캡슐화하여 내부 정보에 대해 알 수 없게 하였다!
게다가 load든 sgen이든 다양한 알고리즘을 2~3개씩 써야했던 터라 전략적으로 선택 가능하게끔 수정하였다
이런 이유로 패턴을 쓰는구나를 알게 되었고.. 아무래도 프로젝트하다가 주먹구구식으로 길게 쓴 내 코드들은 문제가 많았구나 생각하게 된..?