| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | 7 |
| 8 | 9 | 10 | 11 | 12 | 13 | 14 |
| 15 | 16 | 17 | 18 | 19 | 20 | 21 |
| 22 | 23 | 24 | 25 | 26 | 27 | 28 |
| 29 | 30 | 31 |
Tags
- PROJECT
- Github
- R 그래프
- 그래프 탐색
- Express.js
- Lock-free Stack
- SCC
- 이분 탐색
- 트라이
- ccw 알고리즘
- Delete
- reference counting
- localstorage
- 자바스크립트
- trie
- 게임 서버 아키텍처
- Strongly Connected Component
- JavaScript
- 2-SAT
- 벨만-포드
- Binary Lifting
- 최소 공통 조상
- 비트필드를 이용한 dp
- 비트마스킹
- Spin Lock
- 강한 연결 요소
- Prisma
- Behavior Design Pattern
- map
- DP
Archives
- Today
- Total
dh_0e
[Design Pattern] Structural Design Patterns I (Adapter Pattern, Bridge Pattern) 본문
Software Analysis & Design/Design Patterns
[Design Pattern] Structural Design Patterns I (Adapter Pattern, Bridge Pattern)
dh_0e 2025. 11. 5. 23:21구조 디자인 패턴(Structural Design Patterns)
- 클래스들과 객체들을 더 큰 구조로 조립하여 구조를 유연하고 효율적으로 유지하도록 돕는 패턴

Adapater Pattern
- 호환성이 없는 인터페이스를 가진 클래스들을 더 큰 구조로 조립하여 구조를 유연하고 효율적으로 유지하도록 돕는 패턴
- 이미 구축되어 있는 것을 새로운 어떤 것에 사용하려고 할 때 양 쪽 간의 호환성을 유지해주기 위해 사용

- ex) 주식 시장 모니터링 앱을 만드는데 여러 소스에서 주식 데이터를 XML 형식으로 다운로드 해서 보여준다고 가정
어느 시점에 타사의 스마트 분석 라이브러리를 통합하여 개선하려고 했는데 이 라이브러리가 JSON 형식으로 입력 요구

- 특징
- 코드 유지: 기존 시스템의 코드를 수정하지 않고 재사용성을 높임
- 호환성: 두 개의 호환되지 않는 인터페이스를 연결
- 유연성과 확장성을 제공
- 2가지 어댑터 구조
- 객체 어댑터 구조(Composition/포함): 어댑터가 서비스 객체(adaptee)를 포함(wrapping)하여 기능을 위임
- 클래스 어댑터 구조(Inheritance/상속): 어댑터가 서비스 클래스를 상속하고 클라이언트 인터페이스를 구현
- Java의 단일 상속 제약 때문에 활용도가 제한적

ex) Object Adapter

ex) Class Adapter

- 클래스 다중 상속은 Java에서 지원하지 않음
- 다중 구현(extends & implements)로 우회하여 사용할 수 있음
- C++ 같이 다중 상속을 지원하는 프로그래밍 언어에서 사용할 수 있음


- 사용 시기
- 호환 불가: 새로운 인터페이스와 Legacy가 호환이 되지 않을 때
- 재사용: 이미 만든 것을 재사용하고자 하나 수정은 하고 싶지 않을 때
- 구&신 조화: 소프트웨어의 구 버전과 신 버전을 공존시키고 싶을 때

// 1. Adaptee (기존 서비스: 숫자 배열만 받음)
class LegacyChartLibrary {
public void drawChart(int[] data) {
System.out.print("차트 그리는 중... 데이터: [ ");
for (int i : data) {
System.out.print(i + " ");
}
System.out.println("]");
}
}
// 2. Target (클라이언트가 원하는 인터페이스: 문자열을 주고 싶음)
interface CsvTarget {
void drawCsvData(String csv);
}
// 3. Adapter (변환기)
// 상속(extends)을 받아서 LegacyChartLibrary의 기능을 가져오고
// 인터페이스(implements)를 맞춰서 클라이언트를 속임
class CsvAdapter extends LegacyChartLibrary implements CsvTarget {
@Override
public void drawCsvData(String csv) {
// --- [핵심] 자료형 변환 로직 (String -> int[]) ---
// 1. 콤마로 쪼개기 ("10,20" -> "10", "20")
String[] parts = csv.split(",");
// 2. int 배열 만들기
int[] numbers = new int[parts.length];
// 3. 하나씩 숫자로 변환해서 담기
for (int i = 0; i < parts.length; i++) {
numbers[i] = Integer.parseInt(parts[i].trim()); // "10" -> 10 변환
}
// 4. 부모(Legacy) 메서드 호출! (이제 자료형이 맞음)
super.drawChart(numbers);
}
}
// 4. Client (사용)
public class Main {
public static void main(String[] args) {
// 클라이언트는 "10,20,30" 문자열을 넣지만
Target adapter = new CsvAdapter();
// 내부적으로 int[]로 변환되어 차트가 그려짐
adapter.drawCsvData("10,20,30,55,99");
}
}
- 다음과 같이 자료형이 안 맞는 Client와 Service를 어댑터로 연결 해주기도 함
Adapter Pattern 장단점
- 장점
- SRP(단일 책임 원칙) 준수: 프로그램의 기본 비즈니스 로직(클라이언트)에서 구현(인터페이스) 분리 가능
- Adapter 클래스는 “변환”이라는 하나의 책임만 맡음 → SRP 준수
- 인터페이스(Target) 를 클라이언트가 따로 두면 → 클라이언트가 실제 구현(Service)에 직접 의존하지 않음 → 의존성 분리 효과
- OCP(개방 폐쇄 원칙) 준수: 기존 클래스 코드를 건들지 않고 클라이언트 인터페이스를 통해 어댑터와 작동
- 추가로 필요한 메소드가 있다면 어댑터로 빠르게 구현 가능
- 버그가 발생해도 기존의 클래스에는 버그가 없으므로 어댑터만 중점적으로 조사 (SRP가 지켜지기 때문)
- 버그가 발생해도 기존의 클래스에는 버그가 없으므로 어댑터만 중점적으로 조사 (SRP가 지켜지기 때문)
- SRP(단일 책임 원칙) 준수: 프로그램의 기본 비즈니스 로직(클라이언트)에서 구현(인터페이스) 분리 가능
- 단점
- 코드 복잡성 증가: 새로운 인터페이스와 어댑터 클래스 세트를 도입해야 함
- 서비스(adaptee) 클래스를 변경하는 것이 더 간단할 수도 있음
Bridge Pattern
- 큰 클래스 또는 밀접하게 관련된 클래스들의 집합을 두 개의 개별 계층 구조로 나눈 후 각각 독립적으로 개발할 수 있도록 하는 구조 패턴

- 포함 관계로 전환하여 차원 중 하나를 별도의 클래스 계층 구조로 추출
- Shape 클래스는 색상 객체를 가리키는 reference 필드를 가짐 (색상 객체와 연결하여 표현가능 - 이 연결이 브릿지)

- 구조 관점에서 Abstraction과 Implementation의 역할이 있음
- Abstraction: 일부 개체에 대한 상위 수준의 제어/기능 레이어
- 자체적으로 실제 작업을 수행하는 것은 아니고, 작업들은 구현 레이어에 위임해야 함
- Implementation: 각 기능에 대한 구현부를 담당하는 레이어
- Abstraction: 일부 개체에 대한 상위 수준의 제어/기능 레이어
- Abstraction과 Implemntation을 분리하고 브릿지로 연결하여 변화 대응에 독립적으로 확장될 수 있음

- ex) 앱의 그래픽 사용자 인터페이스 레이어(Abstraction)과 OS의 API(Implementation)


ex) 가전 기기와 리모콘




Bridge Pattern 장단점
- 장점
- OCP(개방 폐쇄 원칙) 준수: 새로운 추상화들과 구현들을 상호 독립적으로 둘 수 있음
- 추상적인 코드를 구체적인 코드 변경 없이도 독립적으로 확장할 수 있음
- SRP(단일 책임 원칙) 준수: 추상화의 상위 수준 논리와 구현 플랫폼 세부 정보에 집중할 수 있음
- OCP(개방 폐쇄 원칙) 준수: 새로운 추상화들과 구현들을 상호 독립적으로 둘 수 있음
- 단점
- 계층 구조가 늘어나 코드 복잡성이 증가할 수 있음
- 단 하나의 클래스만 만들고 확장 가능성이 없다면 불필요한 작업
- 설계 구조 파악이 안 되면 코드를 파악하는데 어려움이 있을 수 있음 (Abstraction & Implementation 구분)
- 계층 구조가 늘어나 코드 복잡성이 증가할 수 있음
Bridge Pattern vs Adapter Pattern
- 코드 생김새(구조)만 보면 둘은 거의 똑같음
- 다른 객체를 내 품에 안고(Composition), 대신 일을 시키는(Delegation) 구조
- 패턴의 사용 시기 및 목적이 완전 다름
| 구분 | 어댑터 (Adapter) | 브릿지 (Bridge) |
| 사용 시기 | 다 만들고 나서 (After) | 설계 단계에서 미리 (Before) |
| 목적 | "수습(땜질)" | "설계(확장)" |
| 상황 | 이미 있는 A랑 B가 안 맞네? → "중간에 껴서 맞추자." |
앞으로 A랑 B가 제각각 엄청 늘어날 것 같네? |
| 비유 | 110v → 220v 변환 플러그 | 리모컨(추상) + TV(구현) |
- 브릿지는 일반적으로 사전에 설계되어서 다양한 부분을 독립적으로 개발
- 어댑터는 기존 앱과 사용되어 원래 호환되지 않던 일부 클래스들이 서로 잘 작동하도록 함
- ※ 화살표 주의해서 봐보기 (의존성)
