Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
317 changes: 317 additions & 0 deletions CS/17-20weeks_DesignPatterns/10주차_디자인패턴개념.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,317 @@
# Design Pattern (디자인 패턴) 정리

# 1. 디자인 패턴의 개념
디자인 패턴은 소프트웨어 설계 과정에서 자주 발생하는 문제에 대한 전형적인 해결 방식(설계 템플릿)

디자인 패턴은 코드 자체가 아니라 문제를 해결하는 구조와 접근 방법에 대한 정형화된 해법

개발자는 패턴의 개념을 이해하고, 자신의 프로그램에 맞게 구체적으로 구현해야 함

## 1.1 패턴 vs. 알고리즘
- 패턴: 해결 구조를 설명하는 상위 수준의 설계 개념
- 알고리즘: 목표 달성을 위한 구체적이고 순차적인 절차


# 2. 디자인 패턴의 필요성
- 검증된 설계 해법
- 유지보수성 및 확장성 확보
- 의사소통 비용 감소 (팀 내에서 패턴은 일종의 설계 메타 언어로 기능)
- 객체 지향적 설계 가능 (패턴을 통해 추상화, 캡슐화, 다형성 등 적용 가능)


# 3. 디자인 패턴의 분류

## 3.1 생성 패턴 (Creational Patterns)
기존 코드의 재활용과 유연성을 증가시키는 객체 생성 메커니즘들을 제공
- **Singleton**
- **Factory**
- Abstract Factory
- Builder
- Prototype

## 3.2 구조 패턴 (Structural Patterns)
구조를 유연하고 효율적으로 유지하면서 객체와 클래스를 더 큰 구조로 조합하는 방법을 설명
- Adapter
- Decorator
- Proxy
- Composite
- Facade

## 3.3 행위 패턴 (Behavioral Patterns)
객체 간의 효과적인 의사소통과 책임 할당을 처리
- **Strategy**
- Observer
- Template Method
- Command
- State


# 4. Singleton Pattern

``` mermaid
classDiagram
class Singleton {
- static Singleton instance
- Singleton()
+ static getInstance()
}
```

## 4.1 개념
클래스의 인스턴스를 하나만 생성하도록 보장하고, 전역적으로 접근 가능하게 하는 패턴

## 4.2 사용 목적
- 설정 관리 객체
- 로깅 시스템
- 캐시 관리자
- 공통 리소스 관리자

## 4.3 구현 방식
### 1) 단순 구현
인스턴스 생성 여부 확인 후 없으면 생성
``` java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
```
장점
- 간단한 구현

단점
- Multi-thread 환경에서 여러 인스턴스가 생성될 수 있음

### 2) 동기화 사용
Synchronized를 통해 하나의 thread만 메서드 호출이 가능하도록 격리 보장
``` java
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
```
장점
- Multi-thread 환경에서도 하나의 인스턴스 생성 보장

단점
- 메서드 호출마다 lock으로 인해 성능 저하 → 인스턴스 생성 여부를 synchronized 전후로 체크하여 개선 가능

### 3) Eager Initialization
클래스 초기화 과정에서 정적 멤버로 인스턴스 생성
``` java
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
```
장점
- 구현 단순
- Thread-Safe
- 동기화 비용 없음

단점
- 메서드 사용 여부와 관계없이 클래스 load 시 인스턴스 생성 → 불필요한 메모리 점유

### 4) Inner Static Holder
인스턴스 홀더 역할의 내부 클래스를 두어 메서드 호출 때만 인스턴스를 생성
``` java
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return Holder.instance;
}
}
```
장점
- Lazy initialization
- Thread-Safe
- 불필요한 메모리 점유 없음

단점
- Java에서는 private 생성자라도 리플렉션으로 강제 호출 가능 → 여러 인스턴스 생성

### 5) Single-Element Enum
enum 인스턴스의 Thread-Safe한 특성을 활용

장점
- 간단한 구현
- 구현 단계에서 thread-safe를 직접 고려할 필요 없음

단점
- Enum은 클래스 로딩 시점에 초기화됨 → 완전한 lazy는 아님


# 5. Factory Pattern
``` mermaid
classDiagram
class Client
class Product {
<<interface>>
+ use()
}
class ConcreteProductA
class ConcreteProductB
class ProductFactory {
+ createProduct(type)
}

Client --> ProductFactory
ProductFactory --> Product
Product <|.. ConcreteProductA
Product <|.. ConcreteProductB
```

## 5.1 개념
객체 생성 로직을 팩토리 클래스(또는 메서드)에 위임하여 클라이언트 코드가 구체 클래스(concrete class)에 의존하지 않도록 하는 패턴

즉, 객체 생성 책임을 별도의 계층으로 분리하여 결합도(coupling)를 낮추고 확장성을 확보하는 것이 목적

## 5.2 사용 목적
- 객체 생성 로직이 복잡한 경우 분리
- 클라이언트 코드에서 new 키워드 제거
- OCP(Open-Closed Principle) 준수
- 구체 클래스 의존성 제거
- 객체 생성 정책 변경 시 클라이언트 수정 최소화

## 5.3 예시 코드
``` java
interface Product {
void use();
}

class ConcreteProductA implements Product {
public void use() {
System.out.println("Product A 실행");
}
}

class ConcreteProductB implements Product {
public void use() {
System.out.println("Product B 실행");
}
}

class ProductFactory {
public static Product createProduct(String type) {
if (type.equals("A")) {
return new ConcreteProductA();
} else if (type.equals("B")) {
return new ConcreteProductB();
}
throw new IllegalArgumentException("Unknown type");
}
}
```
장점
- 객체 생성 책임을 분리 → 클라이언트는 구체 클래스에 의존하지 않음
- OCP (객체 지향) 준수 → 기존 코드 수정 없이 확장 가능
- 클라이언트는 인터페이스에만 의존도가 있음

단점
- Product마다 클래스 존재 → 많은 클래스로 인한 구조 복잡도 상승
- 단순히 new를 사용하면 되는 상황에서 불필요한 추상화가 될 수 있음 (over-engineering)

# 6. Strategy Pattern (전략 패턴)

``` mermaid
classDiagram
class Context {
- Strategy strategy
+ setStrategy(Strategy)
+ run()
}

class Strategy {
<<interface>>
+ execute()
}

class ConcreteStrategyA
class ConcreteStrategyB

Context --> Strategy
Strategy <|.. ConcreteStrategyA
Strategy <|.. ConcreteStrategyB
```

## 6.1 개념
객체가 할 수 있는 행위들 각각을 전략 (캡슐화한 알고리즘)으로 만들어 놓고, 동적으로 행위의 수정이 필요한 경우 전략을 바꾸는 것만으로 행위의 수정이 가능하도록 만든 패턴

즉, 실행 중에 알고리즘을 선택할 수 있음

## 6.2 전략 패턴의 구조
### 6.2.1 전략 인터페이스(Strategy Interface)
알고리즘을 캡슐화하는 인터페이스

다양한 알고리즘 클래스가 이 인터페이스를 구현하여 특정 작업을 수행

### 6.2.2 구체적인 전략 클래스(Concrete Strategy Classes)
전략 인터페이스를 구현하여 실제 알고리즘을 정의하는 클래스

서로 다른 알고리즘을 구현하며, 요청에 따라 교체 가능

### 6.2.3 컨텍스트 클래스(Context Class)
전략 인터페이스를 통해 알고리즘을 사용하는 클래스

클라이언트가 선택한 전략을 사용하여 작업을 수행

## 6.2 예시 코드
``` java
interface Strategy {
void execute();
}

class ConcreteStrategyA implements Strategy {
public void execute() {
System.out.println("전략 A 실행");
}
}

class ConcreteStrategyB implements Strategy {
public void execute() {
System.out.println("전략 B 실행");
}
}

class Context {
private Strategy strategy;
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void run() {
strategy.execute();
}
}
```
장점
- 알고리즘을 쉽게 변경 및 대체할 수 있으므로 유연함
- 알고리즘을 캡슐화로 인해 코드 재사용성이 좋음 → 높은 확장성
- 각각 알고리즘을 독립적으로 테스트 가능

단점
- 전략이 많아질수록 클래스 폭증 → 관리 비용 증가
- 단순한 if나 switch로 끝날 로직도 인터페이스 + 구현체 + Context 구조로 분리됨 (over-engineering)
- 전략이 자주 교체되면 객체 생성 비용이 발생

# 정리
- Singleton: 단일 인스턴스 보장
- Factory: 객체 생성 책임 분리
- Strategy: 알고리즘 교체 가능 구조