1. 생성 패턴 (Creational Patterns)
객체 생성 프로세스를 추상화하여 시스템이 객체 생성, 구성, 표현 방식에 독립적일 수 있도록 돕습니다.
1.1 싱글톤 패턴 (Singleton)
클래스의 인스턴스가 오직 하나만 생성되도록 보장하고 이에 대한 전역적인 접근점을 제공합니다.
// Bill Pugh Singleton (권장되는 방식)
public class AppConfig {
private AppConfig() {}
private static class Holder {
private static final AppConfig INSTANCE = new AppConfig();
}
public static AppConfig getInstance() {
return Holder.INSTANCE;
}
}
1.2 팩토리 메서드 패턴 (Factory Method)
객체 생성 인터페이스를 정의하되, 실제 어떤 클래스의 인스턴스를 생성할지는 서브클래스에서 결정하게 합니다.
interface Vehicle { void drive(); }
class Truck implements Vehicle {
public void drive() { System.out.println("트럭이 달립니다."); }
}
abstract class VehicleFactory {
abstract Vehicle createVehicle();
}
class TruckFactory extends VehicleFactory {
Vehicle createVehicle() { return new Truck(); }
}
1.3 추상 팩토리 패턴 (Abstract Factory)
관련성이 있는 여러 객체 군을 생성하기 위한 인터페이스를 제공하며, 구체적인 클래스를 명시하지 않고 제품군을 생성합니다.
interface Button { void render(); }
interface ScrollBar { void scroll(); }
interface GuiFactory {
Button createButton();
ScrollBar createScrollBar();
}
class DarkThemeFactory implements GuiFactory {
public Button createButton() { return () -> System.out.println("Dark Button"); }
public ScrollBar createScrollBar() { return () -> System.out.println("Dark Scroll"); }
}
1.4 빌더 패턴 (Builder)
복잡한 객체의 생성 과정과 표현 방법을 분리하여 동일한 생성 절차에서 서로 다른 표현을 만들 수 있게 합니다.
public class User {
private final String name;
private final int age;
public static class Builder {
private String name;
private int age;
public Builder name(String name) { this.name = name; return this; }
public Builder age(int age) { this.age = age; return this; }
public User build() { return new User(this); }
}
private User(Builder builder) {
this.name = builder.name;
this.age = builder.age;
}
}
1.5 프로토타입 패턴 (Prototype)
원형이 되는 인스턴스를 사용하여 객체를 복사함으로써 새로운 객체를 생성합니다.
class Document implements Cloneable {
String content;
public Document clone() throws CloneNotSupportedException {
return (Document) super.clone();
}
}
2. 구조 패턴 (Structural Patterns)
클래스나 객체를 조합하여 더 큰 구조를 만드는 방법론입니다.
2.1 어댑터 패턴 (Adapter)
호환되지 않는 인터페이스를 가진 클래스들이 함께 작동할 수 있도록 중간에서 변환해줍니다.
interface PowerOutlet { void supply5V(); }
class WallSocket { void supply220V() { /* ... */ } }
class VoltageAdapter implements PowerOutlet {
private WallSocket socket = new WallSocket();
public void supply5V() {
socket.supply220V();
System.out.println("5V로 변환 중...");
}
}
2.2 브릿지 패턴 (Bridge)
추상화된 부분과 구현 부분을 독립적으로 확장할 수 있도록 연결합니다.
interface Color { void apply(); }
abstract class Shape {
protected Color color;
protected Shape(Color color) { this.color = color; }
abstract void draw();
}
class Square extends Shape {
public Square(Color color) { super(color); }
void draw() { System.out.print("정사각형 "); color.apply(); }
}
2.3 컴포지트 패턴 (Composite)
객체들을 트리 구조로 구성하여 개별 객체와 복합 객체를 동일하게 다룰 수 있게 합니다.
interface FileSystemNode { void printName(); }
class File implements FileSystemNode {
private String name;
public File(String name) { this.name = name; }
public void printName() { System.out.println("File: " + name); }
}
class Directory implements FileSystemNode {
private List<FileSystemNode> nodes = new ArrayList<>();
public void add(FileSystemNode node) { nodes.add(node); }
public void printName() { nodes.forEach(FileSystemNode::printName); }
}
2.4 데코레이터 패턴 (Decorator)
객체에 동적으로 새로운 책임을 추가하며, 서브클래스를 만드는 것보다 유연한 확장성을 제공합니다.
interface Beverage { String getDescription(); }
class BasicCoffee implements Beverage {
public String getDescription() { return "커피"; }
}
class MilkDecorator implements Beverage {
private Beverage beverage;
public MilkDecorator(Beverage b) { this.beverage = b; }
public String getDescription() { return beverage.getDescription() + " + 우유"; }
}
2.5 퍼사드 패턴 (Facade)
복잡한 서브시스템에 대해 단순화된 통합 인터페이스를 제공합니다.
class AudioSystem { void on() {} }
class VideoSystem { void on() {} }
class HomeTheaterFacade {
private AudioSystem audio = new AudioSystem();
private VideoSystem video = new VideoSystem();
public void watchMovie() {
video.on();
audio.on();
}
}
2.6 플라이웨이트 패턴 (Flyweight)
다수의 유사한 객체를 생성할 때 가능한 많은 데이터를 공유하여 메모리 사용량을 줄입니다.
class TreeType {
private String name, color; // 공유 데이터
public TreeType(String n, String c) { this.name = n; this.color = c; }
}
class TreeFactory {
private static Map<String, TreeType> types = new HashMap<>();
public static TreeType getTreeType(String name, String color) {
return types.computeIfAbsent(name, k -> new TreeType(name, color));
}
}
2.7 프록시 패턴 (Proxy)
특정 객체에 대한 접근을 제어하기 위해 대리자 객체를 둡니다.
interface SensitiveData { void access(); }
class RealData implements SensitiveData {
public void access() { System.out.println("데이터 접근 완료"); }
}
class DataProxy implements SensitiveData {
private RealData realData;
public void access() {
if (realData == null) realData = new RealData();
System.out.println("보안 체크 중...");
realData.access();
}
}
3. 행위 패턴 (Behavioral Patterns)
객체 간의 알고리즘이나 책임 분배에 관련된 패턴입니다.
3.1 책임 연쇄 패턴 (Chain of Responsibility)
요청을 처리할 수 있는 기회를 하나 이상의 객체에게 부여하여 요청자와 처리자를 분리합니다.
abstract class Logger {
protected Logger next;
public void setNext(Logger next) { this.next = next; }
abstract void log(String msg, int level);
}
class ErrorLogger extends Logger {
void log(String msg, int level) {
if (level == 3) System.out.println("Error: " + msg);
else if (next != null) next.log(msg, level);
}
}
3.2 커맨드 패턴 (Command)
요청을 객체의 형태로 캡슐화하여 요청을 매개변수화하거나 로그에 남길 수 있게 합니다.
interface Command { void execute(); }
class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light l) { this.light = l; }
public void execute() { light.turnOn(); }
}
3.3 인터프리터 패턴 (Interpreter)
특정 언어의 문법 표현과 이를 해석하는 인터프리터를 정의합니다.
interface Expression { boolean interpret(String context); }
class ContainsExpression implements Expression {
private String data;
public ContainsExpression(String d) { this.data = d; }
public boolean interpret(String context) { return context.contains(data); }
}
3.4 반복자 패턴 (Iterator)
내부 구조를 노출하지 않고 컬렉션의 요소들에 순차적으로 접근할 수 있는 방법을 제공합니다.
interface MyIterator { boolean hasNext(); Object next(); }
class NameList {
String[] names = {"Lee", "Kim", "Park"};
public MyIterator getIterator() {
return new MyIterator() {
int i = 0;
public boolean hasNext() { return i < names.length; }
public Object next() { return names[i++]; }
};
}
}
3.5 중재자 패턴 (Mediator)
객체들 간의 복잡한 상호작용을 중재자 객체에 캡슐화하여 객체 간의 결합도를 낮춥니다.
interface ChatRoom { void send(String msg, User user); }
class ChatRoomImpl implements ChatRoom {
public void send(String msg, User user) {
System.out.println(user.getName() + ": " + msg);
}
}
3.6 메멘토 패턴 (Memento)
캡슐화를 위배하지 않고 객체의 상태를 저장하고 나중에 복구할 수 있게 합니다.
class GameState {
private String level;
public Memento save() { return new Memento(level); }
public void restore(Memento m) { level = m.getState(); }
}
class Memento {
private final String state;
public Memento(String s) { this.state = s; }
public String getState() { return state; }
}
3.7 옵저버 패턴 (Observer)
객체의 상태가 변할 때 의존 관계에 있는 모든 객체들에게 자동으로 통지합니다.
interface Subscriber { void update(String news); }
class NewsAgency {
private List<Subscriber> subs = new ArrayList<>();
public void broadcast(String news) {
subs.forEach(s -> s.update(news));
}
}
3.8 상태 패턴 (State)
객체의 내부 상태에 따라 스스로 행동을 변경할 수 있게 합니다.
interface State { void handle(); }
class PlayState implements State {
public void handle() { System.out.println("재생 중..."); }
}
class PlayerContext {
private State state;
public void setState(State s) { this.state = s; }
public void request() { state.handle(); }
}
3.9 전략 패턴 (Strategy)
동일 계열의 알고리즘들을 정의하고 캡슐화하여 실행 중에 알고리즘을 선택할 수 있게 합니다.
interface PaymentStrategy { void pay(int amount); }
class CreditCardStrategy implements PaymentStrategy {
public void pay(int amount) { System.out.println(amount + " 카드 결제"); }
}
class ShoppingCart {
public void checkout(int amount, PaymentStrategy strategy) {
strategy.pay(amount);
}
}
3.10 템플릿 메서드 패턴 (Template Method)
알고리즘의 구조를 정의하고 구체적인 단계는 서브클래스에서 구현하도록 위임합니다.
abstract class DataProcessor {
public final void process() {
read();
transform();
write();
}
abstract void read();
abstract void transform();
void write() { System.out.println("데이터 저장 완료"); }
}
3.11 방문자 패턴 (Visitor)
객체 구조를 변경하지 않고 새로운 작업을 객체에 추가할 수 있게 합니다.
interface Element { void accept(Visitor v); }
interface Visitor { void visit(Book b); }
class Book implements Element {
public void accept(Visitor v) { v.visit(this); }
}