✅ Adapter (어댑터) 패턴
🎯 의도 (Intent)
- 서로 호환되지 않는 인터페이스를 가진 클래스들이
함께 동작할 수 있도록 연결(호환)시켜주는 패턴입니다.
- 기존 코드를 수정하지 않고도 새로운 코드(클라이언트)가 기존 라이브러리나 클래스와 통합될 수 있게 합니다.
- 주로 레거시 코드 재사용, 타사 라이브러리 연동 등에 사용됩니다.
👉 즉, 인터페이스가 맞지 않아 함께 사용할 수 없는 두 클래스를 연결해
호환성을 제공하는 변환기 역할을 합니다.
🧩 구성 요소 (Participants)
-
Target (타겟 인터페이스)
- 클라이언트가 기대하는 인터페이스입니다.
- 예:
request() 메서드를 정의.
-
Client (클라이언트)
Target 인터페이스를 통해 서비스를 사용합니다.
Adaptee의 구체적인 구현은 알 필요가 없습니다.
-
Adaptee (적응 대상 클래스)
- 기존에 존재하지만, 클라이언트가 기대하는 인터페이스와 호환되지 않는 클래스입니다.
- 자체적인 메서드(예:
specificRequest())를 가집니다.
-
Adapter (어댑터 클래스)
Target 인터페이스를 구현하고,
내부적으로 Adaptee의 인스턴스를 참조하여 메서드를 변환(호출)합니다.
- Target → Adapter → Adaptee 형태로 요청이 전달됩니다.
📌 작동 방식
- Client는
Target 인터페이스를 통해 요청을 보냅니다.
- Adapter는 요청을 받아
Adaptee가 이해할 수 있는 형태로 변환하여 전달합니다.
Adaptee는 실제 작업을 수행하고 결과를 반환합니다.
- 클라이언트는
Adaptee의 존재나 내부 구조를 알 필요 없이 서비스를 사용할 수 있습니다.
✅ Bridge (브리지) 패턴
🎯 의도 (Intent)
- 추상화(Abstraction)와 구현(Implementation)을 분리하여
서로 독립적으로 확장할 수 있도록 하는 패턴입니다.
- 클래스 계층이 추상화 부분과 구현 부분으로 나뉘며,
이 둘을 브리지(Bridge)로 연결하여 결합도를 낮춥니다.
- 상속 대신 구성(Composition)을 사용해 유연한 구조를 제공합니다.
👉 즉, 기능 계층(추상화)과 구현 계층을 분리하여
각각 독립적으로 확장 가능하게 만드는 패턴입니다.
🧩 구성 요소 (Participants)
-
Abstraction (추상화 클래스)
- 상위 수준의 인터페이스를 정의합니다.
- 구현 부분(
Implementor)에 대한 참조를 포함하고,
실제 작업을 구현 객체에 위임합니다.
-
RefinedAbstraction (확장된 추상화 클래스)
Abstraction을 확장하여
추가적인 기능을 정의할 수 있습니다.
-
Implementor (구현자 인터페이스)
- 구현 계층의 인터페이스를 정의합니다.
- 추상화 클래스에서 구현 부분을 호출할 때 사용됩니다.
-
ConcreteImplementor (구체 구현자)
Implementor를 구현하여 구체적인 기능을 제공합니다.
-
Client (클라이언트)
Abstraction을 통해 기능을 사용합니다.
- 구체 구현에는 직접 접근하지 않으며, 구현 변경에 영향을 받지 않습니다.
📌 작동 방식
- Client는
Abstraction 객체를 생성하고,
어떤 Implementor를 사용할지 설정합니다.
Abstraction은 내부적으로 Implementor의 메서드를 호출하여 실제 작업을 수행합니다.
- 추상화(기능 계층)와 구현(플랫폼 계층)을 독립적으로 확장할 수 있어
새로운 기능이나 구현체 추가 시 기존 코드에 영향을 최소화할 수 있습니다.
✅ Composite (컴포지트) 패턴
🎯 의도 (Intent)
- 객체들을 트리 구조로 구성하여,
부분-전체(Part-Whole) 계층 구조를 표현하는 패턴입니다.
- 개별 객체(Leaf)와 복합 객체(Composite)를 동일하게 다룰 수 있도록 하여,
클라이언트가 객체의 세부 구조를 신경 쓰지 않고 일관된 방식으로 작업할 수 있게 합니다.
👉 즉, 단일 객체와 복합 객체를 동일한 인터페이스로 처리하여
클라이언트가 트리 구조를 쉽게 다룰 수 있도록 합니다.
🧩 구성 요소 (Participants)
-
Component (컴포넌트 인터페이스)
- 트리 구조의 모든 객체(Leaf, Composite)가 공통으로 구현해야 할 인터페이스를 정의합니다.
- 예:
operation() 메서드, 트리 구조 관리 메서드(add(), remove(), getChild()).
-
Leaf (리프, 잎 노드)
- 트리의 말단 객체(자식 없음)이며, 실제 동작을 수행합니다.
add(), remove() 같은 메서드는 보통 구현되지 않거나 예외를 던집니다.
-
Composite (복합 객체, 노드)
- 자식 노드들을 관리하고, Component 인터페이스를 구현합니다.
- 자신의 자식들에게 작업을 위임하여 전체 트리 구조에서 동작을 수행합니다.
-
Client (클라이언트)
Component 인터페이스만 알고 있으며,
Leaf와 Composite를 동일한 방식으로 처리할 수 있습니다.
📌 작동 방식
- Client는
Component 인터페이스를 사용하여 객체에 접근합니다.
- 단일 객체(
Leaf)든, 복합 객체(Composite)든 동일한 메서드 호출로 작업이 수행됩니다.
Composite는 요청을 자식들에게 재귀적으로 전달하여 트리 전체에서 작업을 처리합니다.
- 새로운 Leaf나 Composite 클래스를 추가하더라도 클라이언트 코드는 변경할 필요가 없습니다.
✅ Decorator (데코레이터) 패턴
🎯 의도 (Intent)
- 객체에 동적으로 새로운 기능을 추가할 수 있도록 하는 패턴입니다.
- 상속(Inheritance)을 사용하지 않고, 구성(Composition)을 통해 기능 확장을 제공합니다.
- 여러 개의 데코레이터를 중첩하면 다양한 기능 조합을 쉽게 만들 수 있습니다.
👉 즉, 기존 객체를 감싸(wrap)서 기능을 확장하고,
클래스 수 증가 없이 유연하게 기능을 추가할 수 있도록 합니다.
🧩 구성 요소 (Participants)
-
Component (컴포넌트 인터페이스)
- 기본 객체와 데코레이터가 공통으로 가져야 할 인터페이스를 정의합니다.
- 예:
operation() 메서드.
-
ConcreteComponent (구체 컴포넌트)
- 기본 기능을 구현하는 실제 객체입니다.
- 데코레이터가 감싸는 대상이 됩니다.
-
Decorator (데코레이터 추상 클래스)
Component 인터페이스를 구현하고,
내부에 Component 참조를 보관합니다.
- 요청을 위임하면서, 추가 기능을 덧붙일 수 있는 기반을 제공합니다.
-
ConcreteDecorator (구체 데코레이터)
Decorator를 상속하여,
기존 기능에 새로운 기능을 덧붙이는 구체 클래스입니다.
-
Client (클라이언트)
Component 인터페이스를 사용하여 객체를 다루며,
객체가 데코레이터로 감싸져 있는지 여부를 신경 쓰지 않습니다.
📌 작동 방식
- Client는
ConcreteComponent를 생성한 후,
필요에 따라 여러 ConcreteDecorator로 감쌉니다.
- 각 데코레이터는 내부적으로 원본
Component의 메서드를 호출하면서
추가 기능을 수행합니다.
- 데코레이터를 중첩하면 기능이 누적적으로 확장됩니다.
✅ Facade (퍼사드) 패턴
🎯 의도 (Intent)
- 복잡한 서브시스템(Subsystem)의 인터페이스 집합에 대해
단순하고 통합된 인터페이스(Entry Point)를 제공하는 패턴입니다.
- 클라이언트가 서브시스템의 내부 세부 사항을 알 필요 없이
Facade를 통해 간단하게 기능을 사용할 수 있도록 합니다.
- 의존성을 줄이고(Loosely Coupled), 사용 편의성을 높입니다.
👉 즉, 복잡한 시스템을 단일 진입점으로 감싸서
클라이언트가 쉽게 접근할 수 있도록 단순화합니다.
🧩 구성 요소 (Participants)
-
Facade (퍼사드 클래스)
- 클라이언트가 사용하는 간단한 인터페이스를 제공합니다.
- 내부적으로 여러 서브시스템의 객체를 생성하고,
적절한 순서로 메서드를 호출하여 요청을 처리합니다.
-
Subsystem Classes (서브시스템 클래스들)
- 실제 기능을 수행하는 복잡한 클래스들의 집합입니다.
- 퍼사드 없이도 직접 사용할 수 있지만,
Facade를 통해 사용하면 더 단순하고 일관된 접근이 가능합니다.
-
Client (클라이언트)
Facade를 통해 서브시스템을 사용합니다.
- 서브시스템의 복잡한 세부 사항에는 의존하지 않습니다.
📌 작동 방식
- Client는 복잡한 서브시스템 대신
Facade에 요청을 보냅니다.
Facade는 내부적으로 필요한 서브시스템 객체들을 생성하고,
요청을 처리하기 위해 적절한 메서드를 호출합니다.
- 서브시스템이 변경되더라도
Facade만 수정하면 되며,
클라이언트는 변경의 영향을 받지 않습니다.
✅ Flyweight (플라이웨이트) 패턴
🎯 의도 (Intent)
- 많은 수의 유사한 객체를 효율적으로 관리하기 위해
공유 가능한 상태(내부 상태)를 분리하여 메모리 사용을 최소화하는 패턴입니다.
- 동일한 데이터를 여러 객체가 공유함으로써 객체 생성 비용과 메모리 사용량을 줄입니다.
- 자주 사용되는 객체를 캐싱(pooling)하여 성능을 최적화할 수 있습니다.
👉 즉, 공유 가능한 상태를 재사용하여
대량의 객체를 효율적으로 관리하는 구조를 제공합니다.
🧩 구성 요소 (Participants)
-
Flyweight (플라이웨이트 인터페이스)
- 공유 가능한 객체가 구현해야 할 공통 인터페이스를 정의합니다.
- 예:
operation(extrinsicState) 메서드를 통해 외부 상태를 인자로 전달받아 처리.
-
ConcreteFlyweight (구체 플라이웨이트)
Flyweight 인터페이스를 구현하고,
내부 상태(Intrinsic State)를 저장합니다.
- 이 상태는 공유되며 변경되지 않습니다.
-
UnsharedConcreteFlyweight (비공유 플라이웨이트, 선택적)
- 공유되지 않는 객체를 정의합니다.
- 일부 객체는 공유되지 않고 개별적으로 존재할 수 있습니다.
-
FlyweightFactory (플라이웨이트 팩토리)
Flyweight 객체를 캐싱하고 재사용하는 역할을 합니다.
- 요청받은 객체가 이미 존재하면 기존 인스턴스를 반환하고,
없으면 새로 생성하여 저장합니다.
-
Client (클라이언트)
FlyweightFactory를 통해 Flyweight 객체를 요청하고 사용합니다.
- 외부 상태(Extrinsic State)를 유지하고 필요할 때
Flyweight에 전달합니다.
📌 작동 방식
- Client가
FlyweightFactory를 통해 필요한 객체를 요청합니다.
FlyweightFactory는 이미 존재하는 객체를 재사용하거나 새로 생성합니다.
ConcreteFlyweight는 공유 가능한 내부 상태를 보관하고,
외부 상태는 클라이언트가 제공하여 작업을 수행합니다.
- 이를 통해 대량의 객체를 효율적으로 관리할 수 있습니다.
✅ Proxy (프록시) 패턴
🎯 의도 (Intent)
- 다른 객체(RealSubject)에 대한 대리자(Proxy)를 제공하여
접근 제어, 지연 로딩, 로깅, 캐싱 등의 추가 기능을 수행하는 패턴입니다.
- 클라이언트는 실제 객체와 동일한 방식으로 프록시를 사용하므로,
RealSubject의 존재를 몰라도 동작할 수 있습니다.
- 객체에 대한 접근을 제어하거나 부가적인 작업을 삽입할 때 유용합니다.
👉 즉, 실제 객체에 대한 대리 역할을 수행하는 객체를 사용하여
접근 제어나 부가 기능을 투명하게 추가합니다.
🧩 구성 요소 (Participants)
-
Subject (주제 인터페이스)
- RealSubject와 Proxy가 공통으로 구현해야 할 인터페이스를 정의합니다.
- 클라이언트는 Subject 인터페이스만 알면 됩니다.
-
RealSubject (실제 객체)
- Proxy가 대리할 실제 객체입니다.
- 본래의 핵심 기능을 구현합니다.
-
Proxy (프록시 클래스)
Subject 인터페이스를 구현하고, RealSubject를 참조합니다.
- 요청을 가로채어 접근 제어, 캐싱, 로깅, 지연 초기화 등의 부가 작업을 수행한 후
RealSubject에 요청을 위임합니다.
-
Client (클라이언트)
Subject 인터페이스를 사용하여 객체에 접근합니다.
- 실제 객체인지 프록시인지 구분할 필요가 없습니다.
📌 작동 방식
- Client는
Proxy를 통해 요청을 보냅니다.
Proxy는 필요 시 부가 기능(로그, 권한 체크, 캐싱 등)을 수행하고
RealSubject에 요청을 위임합니다.
RealSubject는 본래 기능을 수행하고 결과를 반환합니다.
- 클라이언트는
Proxy와 RealSubject의 차이를 인지하지 못합니다.