객체지향의 꽃, 디자인 패턴(Design Pattern) 핵심 요약
GoF(Gang of Four)가 정리한 23가지 패턴
2026.03.30
디자인패턴 종류
- 디자인 패턴(Design Pattern)은 소프트웨어 개발 중 반복적으로 발생하는 문제들에 대한 검증된 해결책 모음
- GoF(Gang of Four)가 정리한 23가지 패턴이 가장 널리 알려져 있습니다.
- 이 패턴들은 크게 생성, 구조, 행위 3가지 범주로 분류됩니다.
생성 패턴 (Creational) — 5가지
객체의 생성 방식을 결정하는 패턴입니다.
| # | 패턴 | 설명 |
|---|---|---|
| 1 | Singleton (싱글톤) | 클래스 인스턴스를 딱 하나만 생성하고 전역 접근점 제공 |
| 2 | Factory Method (팩토리 메서드) | 상위 클래스에서 인터페이스 정의, 하위 클래스에서 인스턴스 생성 |
| 3 | Abstract Factory (추상 팩토리) | 관련된 객체들의 집합을 생성하는 인터페이스 제공 |
| 4 | Builder (빌더) | 복잡한 객체의 생성 과정을 단계별로 분리해 단순화 |
| 5 | Prototype (프로토타입) | 기존 객체를 복사해 새로운 객체를 생성 |
구조 패턴 (Structural) — 7가지
클래스나 객체를 조합해 더 큰 구조를 만드는 패턴입니다.
| # | 패턴 | 설명 |
|---|---|---|
| 6 | Adapter (어댑터) | 호환되지 않는 인터페이스를 변환해 함께 동작하도록 연결 |
| 7 | Bridge (브리지) | 구현부와 추상층을 분리해 독립적으로 확장 가능하게 함 |
| 8 | Composite (컴포지트) | 개별 객체와 복합 객체를 동일하게 다루어 트리 구조 구성 |
| 9 | Decorator (데코레이터) | 객체에 동적으로 새로운 기능을 추가해 확장 |
| 10 | Facade (퍼사드) | 복잡한 서브시스템을 단순한 인터페이스로 감싸 쉽게 사용 |
| 11 | Flyweight (플라이웨이트) | 공유 가능한 객체를 통해 메모리 사용 최적화 |
| 12 | Proxy (프록시) | 다른 객체의 대리자를 제공해 접근 제어 및 지연 로딩 구현 |
행위 패턴 (Behavioral) — 11가지
객체나 클래스 사이의 알고리즘 및 책임 분배에 관련된 패턴입니다.
| # | 패턴 | 설명 |
|---|---|---|
| 13 | Observer (옵저버) | 한 객체의 상태 변화 시 의존 객체들에게 자동으로 알림 전달 |
| 14 | Strategy (전략) | 알고리즘을 캡슐화해 동적으로 교체 가능하게 함 |
| 15 | Template Method (템플릿 메서드) | 알고리즘의 뼈대를 정의하고, 세부 구현은 하위 클래스에 위임 |
| 16 | Command (커맨드) | 요청을 객체로 캡슐화해 실행 취소/재실행 등을 지원 |
| 17 | State (상태) | 객체 내부 상태에 따라 행위를 자동으로 변경 |
| 18 | Chain of Responsibility | 요청을 처리할 수 있는 객체를 체인으로 연결해 순차 전달 |
| 19 | Iterator (이터레이터) | 내부 구현 노출 없이 컬렉션 요소에 순차적으로 접근 |
| 20 | Mediator (미디에이터) | 객체 간 통신을 중재자를 통해 처리해 결합도 낮춤 |
| 21 | Memento (메멘토) | 객체의 이전 상태를 저장해 복원(undo) 기능 구현 |
| 22 | Interpreter (인터프리터) | 특정 언어의 문법을 클래스로 표현하고 해석 |
| 23 | Visitor (비지터) | 객체 구조를 변경하지 않고 새로운 연산을 추가 |
각 패턴의 예시
🏗️ 생성 패턴 (Creational)
1. Singleton — 단 하나의 인스턴스
javapublic class DatabaseConnection {
private static DatabaseConnection instance;
private DatabaseConnection() {}
public static DatabaseConnection getInstance() {
if (instance == null) {
instance = new DatabaseConnection();
}
return instance;
}
}
// 사용: 어디서 호출해도 동일한 객체
DatabaseConnection db1 = DatabaseConnection.getInstance();
DatabaseConnection db2 = DatabaseConnection.getInstance();
System.out.println(db1 == db2); // true
getInstance()를 통해 항상 동일한 객체를 반환합니다.
2. Factory Method — 하위 클래스가 객체 생성 결정
javainterface Animal {
void speak();
}
class Dog implements Animal {
public void speak() { System.out.println("멍멍!"); }
}
class Cat implements Animal {
public void speak() { System.out.println("야옹!"); }
}
class AnimalFactory {
public static Animal create(String type) {
if (type.equals("dog")) return new Dog();
else return new Cat();
}
}
// 사용
Animal a = AnimalFactory.create("dog");
a.speak(); // 멍멍!
클라이언트는 구체 클래스를 몰라도 객체를 생성할 수 있습니다.
3. Abstract Factory — 관련 객체 집합 생성
javainterface Button { void click(); }
interface Checkbox { void check(); }
class WindowsButton implements Button { public void click() { System.out.println("Windows 버튼"); } }
class MacButton implements Button { public void click() { System.out.println("Mac 버튼"); } }
interface GUIFactory {
Button createButton();
}
class WindowsFactory implements GUIFactory {
public Button createButton() { return new WindowsButton(); }
}
class MacFactory implements GUIFactory {
public Button createButton() { return new MacButton(); }
}
OS에 맞는 UI 컴포넌트 세트를 한 번에 생성할 수 있습니다.
4. Builder — 단계별 객체 생성
javaclass Pizza {
String size, crust, topping;
static class Builder {
String size, crust, topping;
Builder size(String s) { this.size = s; return this; }
Builder crust(String c) { this.crust = c; return this; }
Builder topping(String t) { this.topping = t; return this; }
Pizza build() {
Pizza p = new Pizza();
p.size = size; p.crust = crust; p.topping = topping;
return p;
}
}
}
// 사용
Pizza pizza = new Pizza.Builder()
.size("Large").crust("씬").topping("페퍼로니").build();
복잡한 객체를 메서드 체이닝으로 유연하게 생성합니다.
5. Prototype — 기존 객체 복사
javaclass Character implements Cloneable {
String name;
int level;
public Character clone() throws CloneNotSupportedException {
return (Character) super.clone();
}
}
// 사용
Character hero = new Character();
hero.name = "전사"; hero.level = 50;
Character clone = hero.clone(); // 복사
clone.name = "복제 전사";
객체를 처음부터 새로 만드는 대신 복사해 성능을 절약합니다.
🔩 구조 패턴 (Structural)
6. Adapter — 인터페이스 변환
java// 기존 클래스 (220V)
class KoreanSocket {
public void connect220V() { System.out.println("220V 연결"); }
}
// 목표 인터페이스 (110V)
interface USASocket {
void connect110V();
}
// 어댑터
class SocketAdapter implements USASocket {
KoreanSocket korean = new KoreanSocket();
public void connect110V() {
korean.connect220V(); // 내부에서 변환
}
}
JDBC가 대표적인 어댑터 패턴 사례로, 다양한 DB를 공통 인터페이스로 연결합니다.
7. Bridge — 구현과 추상 분리
javainterface Color { String fill(); }
class Red implements Color { public String fill() { return "빨간색"; } }
class Blue implements Color { public String fill() { return "파란색"; } }
abstract class Shape {
Color color;
Shape(Color c) { this.color = c; }
abstract void draw();
}
class Circle extends Shape {
Circle(Color c) { super(c); }
public void draw() { System.out.println(color.fill() + " 원"); }
}
// 사용
new Circle(new Red()).draw(); // 빨간색 원
new Circle(new Blue()).draw(); // 파란색 원
도형과 색상을 독립적으로 확장할 수 있습니다.
8. Composite — 트리 구조 (개별·복합 동일 취급)
javainterface FileSystem {
void show(String indent);
}
class File implements FileSystem {
String name;
File(String n) { name = n; }
public void show(String indent) { System.out.println(indent + name); }
}
class Folder implements FileSystem {
String name;
List<FileSystem> children = new ArrayList<>();
Folder(String n) { name = n; }
void add(FileSystem f) { children.add(f); }
public void show(String indent) {
System.out.println(indent + "[" + name + "]");
for (FileSystem f : children) f.show(indent + " ");
}
}
파일 시스템처럼 폴더 안에 파일/폴더를 재귀적으로 구성합니다.
9. Decorator — 동적으로 기능 추가
javainterface Coffee { int cost(); }
class BasicCoffee implements Coffee { public int cost() { return 1000; } }
class MilkDecorator implements Coffee {
Coffee coffee;
MilkDecorator(Coffee c) { this.coffee = c; }
public int cost() { return coffee.cost() + 500; }
}
class ShotDecorator implements Coffee {
Coffee coffee;
ShotDecorator(Coffee c) { this.coffee = c; }
public int cost() { return coffee.cost() + 300; }
}
// 사용
Coffee order = new ShotDecorator(new MilkDecorator(new BasicCoffee()));
System.out.println(order.cost()); // 1800원
기존 코드 수정 없이 기능을 겹겹이 추가합니다.
10. Facade — 복잡한 시스템을 단순 인터페이스로
javaclass CPU { void start() { System.out.println("CPU 시작"); } }
class Memory { void load() { System.out.println("메모리 로드"); } }
class Disk { void read() { System.out.println("디스크 읽기"); } }
// 퍼사드
class Computer {
CPU cpu = new CPU();
Memory mem = new Memory();
Disk disk = new Disk();
void turnOn() { // 복잡한 내부를 하나로 감춤
cpu.start(); mem.load(); disk.read();
}
}
new Computer().turnOn(); // 한 줄로 컴퓨터 시작
사용자는 내부 동작을 몰라도 됩니다.
11. Flyweight — 공유로 메모리 최적화
javaclass TreeType { // 공유 객체 (나무 종류)
String name, color;
TreeType(String n, String c) { name = n; color = c; }
}
class TreeFactory {
static Map<String, TreeType> cache = new HashMap<>();
static TreeType get(String name, String color) {
return cache.computeIfAbsent(name, k -> new TreeType(name, color));
}
}
// 1000그루 나무도 TreeType은 몇 개만 공유
대량의 유사 객체를 공유해 메모리를 아낍니다.
12. Proxy — 대리자 제공
javainterface Image { void display(); }
class RealImage implements Image {
String file;
RealImage(String f) { file = f; System.out.println(f + " 로딩..."); }
public void display() { System.out.println(file + " 표시"); }
}
class ProxyImage implements Image {
String file;
RealImage real;
ProxyImage(String f) { file = f; }
public void display() {
if (real == null) real = new RealImage(file); // 지연 로딩
real.display();
}
}
실제 객체는 필요할 때만 생성합니다(지연 로딩).
🔄 행위 패턴 (Behavioral)
13. Observer — 상태 변화 알림
javainterface Observer { void update(float temp); }
class WeatherStation {
List<Observer> observers = new ArrayList<>();
float temperature;
void addObserver(Observer o) { observers.add(o); }
void setTemperature(float t) {
temperature = t;
observers.forEach(o -> o.update(t)); // 전체 알림
}
}
class Display implements Observer {
public void update(float temp) { System.out.println("현재 온도: " + temp); }
}
유튜브 구독 알림처럼, 변화를 등록된 모든 구독자에게 자동 전파합니다.
14. Strategy — 알고리즘 교체
javainterface SortStrategy { void sort(int[] arr); }
class BubbleSort implements SortStrategy {
public void sort(int[] arr) { System.out.println("버블 정렬"); }
}
class QuickSort implements SortStrategy {
public void sort(int[] arr) { System.out.println("퀵 정렬"); }
}
class Sorter {
SortStrategy strategy;
Sorter(SortStrategy s) { strategy = s; }
void sort(int[] arr) { strategy.sort(arr); }
}
// 사용
Sorter sorter = new Sorter(new QuickSort());
sorter.sort(new int[]{3,1,2}); // 퀵 정렬
런타임에 알고리즘을 자유롭게 교체합니다.
15. Template Method — 알고리즘 뼈대 고정
javaabstract class DataProcessor {
final void process() { // 순서 고정
readData(); processData(); writeData();
}
abstract void readData();
abstract void processData();
void writeData() { System.out.println("파일에 저장"); } // 기본 구현
}
class CSVProcessor extends DataProcessor {
void readData() { System.out.println("CSV 읽기"); }
void processData() { System.out.println("CSV 파싱"); }
}
전체 흐름은 부모가 제어하고, 세부 구현만 자식이 담당합니다.
16. Command — 요청을 객체로 캡슐화
javainterface Command { void execute(); }
class Light {
void on() { System.out.println("불 켜짐"); }
void off() { System.out.println("불 꺼짐"); }
}
class LightOnCommand implements Command {
Light light;
LightOnCommand(Light l) { light = l; }
public void execute() { light.on(); }
}
class RemoteControl {
Command command;
void setCommand(Command c) { command = c; }
void pressButton() { command.execute(); }
}
실행 취소(Undo), 로깅, 큐 처리 등에 활용됩니다.
17. State — 상태에 따라 행동 변경
javainterface State { void handle(); }
class GreenLight implements State { public void handle() { System.out.println("직진"); } }
class RedLight implements State { public void handle() { System.out.println("정지"); } }
class TrafficLight {
State state = new GreenLight();
void setState(State s) { state = s; }
void action() { state.handle(); }
}
조건문 없이 상태 객체 교체만으로 행동이 바뀝니다.
18. Chain of Responsibility — 요청을 체인으로 전달
javaabstract class Approver {
Approver next;
void setNext(Approver a) { next = a; }
abstract void approve(int amount);
}
class Manager extends Approver {
public void approve(int amount) {
if (amount <= 100000) System.out.println("팀장 승인");
else if (next != null) next.approve(amount);
}
}
class CEO extends Approver {
public void approve(int amount) { System.out.println("대표 승인"); }
}
// manager.setNext(ceo) → 결재 라인 구성
결재 라인처럼 처리 가능한 객체가 나올 때까지 요청을 전달합니다.
19. Iterator — 내부 구조 노출 없이 순회
javaclass NumberList implements Iterable<Integer> {
int[] numbers = {1, 2, 3, 4, 5};
public Iterator<Integer> iterator() {
return new Iterator<>() {
int index = 0;
public boolean hasNext() { return index < numbers.length; }
public Integer next() { return numbers[index++]; }
};
}
}
// 사용 (향상된 for문도 동일)
for (int n : new NumberList()) System.out.println(n);
Java의 for-each가 바로 이터레이터 패턴을 사용합니다.
20. Mediator — 중재자를 통한 통신
javaclass ChatRoom { // 중재자
static void sendMessage(String user, String msg) {
System.out.println("[" + user + "]: " + msg);
}
}
class User {
String name;
User(String n) { name = n; }
void send(String msg) { ChatRoom.sendMessage(name, msg); }
}
// 사용자들이 직접 연결되지 않고 채팅방을 통해 소통
객체들이 직접 참조하지 않고 중재자를 통해 통신해 결합도를 낮춥니다.
21. Memento — 상태 저장 및 복원 (Undo)
javaclass Editor {
String text = "";
String save() { return text; } // 저장
void restore(String saved) { text = saved; } // 복원
void write(String t) { text += t; }
}
// 사용
Editor e = new Editor();
e.write("안녕");
String saved = e.save(); // 저장
e.write("하세요");
System.out.println(e.text); // 안녕하세요
e.restore(saved);
System.out.println(e.text); // 안녕 (Undo)
텍스트 에디터의 Ctrl+Z 기능과 동일한 원리입니다.
22. Interpreter — 문법 해석
javainterface Expression { boolean interpret(String context); }
class TerminalExpression implements Expression {
String data;
TerminalExpression(String d) { data = d; }
public boolean interpret(String context) { return context.contains(data); }
}
class OrExpression implements Expression {
Expression e1, e2;
OrExpression(Expression a, Expression b) { e1 = a; e2 = b; }
public boolean interpret(String context) { return e1.interpret(context) || e2.interpret(context); }
}
// "자바" OR "파이썬" 포함 여부 판별
SQL 파서, 수식 계산기 등에 활용되는 패턴입니다.
23. Visitor — 구조 변경 없이 새 연산 추가
javainterface Visitor { void visit(Circle c); void visit(Rectangle r); }
interface Shape { void accept(Visitor v); }
class Circle implements Shape {
double radius;
Circle(double r) { radius = r; }
public void accept(Visitor v) { v.visit(this); }
}
class AreaCalculator implements Visitor {
public void visit(Circle c) { System.out.println("원 넓이: " + Math.PI * c.radius * c.radius); }
public void visit(Rectangle r) { /* ... */ }
}
도형 클래스를 건드리지 않고, Visitor만 추가해 새로운 연산을 구현합니다.