본문 바로가기
Programming

JAVA 디자인 패턴

by 나무수피아는 지식의 가지를 뻗어가는 공간입니다. 2025. 11. 26.
반응형

⚙️ 디자인 패턴

디자인 패턴은 소프트웨어 개발에서 자주 발생하는 문제를 해결하기 위한 재사용 가능한 설계 템플릿입니다. 코드의 유지보수성과 확장성을 높이고, 협업 시 의사소통을 원활하게 하는 중요한 개념입니다. 이번 강의에서는 대표적인 생성 패턴, 구조 패턴, 행동 패턴을 깊이 있게 다룹니다. 각 패턴은 서로 다른 문제를 해결하며, 적절한 상황에 맞게 적용할 수 있도록 설계되었습니다.


📌 목차

  1. 생성 패턴 (Singleton, Factory)
  2. 구조 패턴 (Adapter, Composite)
  3. 행동 패턴 (Strategy, Observer)

🔹 1. 생성 패턴

생성 패턴은 객체 생성 방식을 체계적으로 관리하여 시스템의 복잡성을 줄이고 유연성을 높이는 설계 패턴입니다. 대규모 애플리케이션에서는 객체 생성을 일관성 있게 제어하는 것이 매우 중요합니다.

🖥️ Singleton 패턴

Singleton 패턴은 애플리케이션에서 클래스의 인스턴스를 하나만 생성하고, 어디서든 이 인스턴스에 접근할 수 있도록 보장합니다. 주로 설정 정보, 로깅, 리소스 풀 관리 등에 활용됩니다. 멀티스레드 환경에서 안전하게 인스턴스를 생성하도록 getInstance() 메서드에 동기화를 적용합니다.

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

위 코드에서 생성자가 private로 선언되어 외부에서 직접 객체를 생성할 수 없으며, getInstance() 메서드를 통해서만 단 하나의 인스턴스를 얻을 수 있습니다.

💻 Factory 패턴

Factory 패턴은 객체 생성 로직을 별도의 팩토리 클래스에 캡슐화하여 클라이언트 코드가 구체적인 클래스에 의존하지 않도록 합니다. 이를 통해 객체 생성 과정을 분리하고, 향후 객체 종류가 추가되거나 변경될 때 코드 수정 범위를 최소화할 수 있습니다.

interface Product {
    void use();
}

class ConcreteProduct implements Product {
    public void use() {
        System.out.println("ConcreteProduct 사용");
    }
}

class ProductFactory {
    public Product createProduct() {
        return new ConcreteProduct();
    }
}

팩토리 메서드는 반환 타입을 인터페이스나 상위 클래스로 지정하여 클라이언트가 구체적인 구현에 의존하지 않도록 합니다. 이는 유연한 확장성을 지원하는 중요한 디자인 원칙입니다.


🔹 2. 구조 패턴

구조 패턴은 클래스나 객체 간의 관계를 다루어 더 큰 구조를 구성할 때 유용한 패턴입니다. 객체 간 인터페이스를 변환하거나, 복합 객체를 단일 객체처럼 다룰 수 있게 하여 시스템을 모듈화 하고 재사용성을 높입니다.

🖥️ Adapter 패턴

Adapter 패턴은 호환되지 않는 인터페이스를 가진 클래스들이 함께 작동하도록 중간에서 인터페이스를 변환해 주는 역할을 합니다. 주로 레거시 코드와 신기능을 통합할 때 많이 사용됩니다.

interface Target {
    void request();
}

class Adaptee {
    public void specificRequest() {
        System.out.println("특정 요청");
    }
}

class Adapter implements Target {
    private Adaptee adaptee;

    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void request() {
        adaptee.specificRequest();
    }
}

Adapter 클래스는 기존의 Adaptee 객체를 감싸 Target 인터페이스에 맞게 변환하여 클라이언트가 일관된 방식으로 호출할 수 있도록 합니다.

💻 Composite 패턴

Composite 패턴은 객체를 트리 구조로 구성하여 단일 객체(Leaf)와 복합 객체(Composite)를 동일하게 다룰 수 있게 합니다. 복잡한 계층 구조를 간단하게 표현하고 처리할 수 있어 GUI, 파일 시스템, 문서 편집기 등에서 자주 쓰입니다.

import java.util.ArrayList;
import java.util.List;

interface Component {
    void operation();
}

class Leaf implements Component {
    public void operation() {
        System.out.println("Leaf operation");
    }
}

class Composite implements Component {
    private List children = new ArrayList<>();

    public void add(Component child) {
        children.add(child);
    }

    public void operation() {
        for (Component child : children) {
            child.operation();
        }
    }
}

Composite 객체는 자식 객체를 관리하며, 자식들이 모두 동일한 인터페이스를 구현하므로 클라이언트는 단일 객체든 복합 객체든 동일하게 처리할 수 있습니다. 이를 통해 복잡한 객체 구조를 단순화합니다.


🔹 3. 행동 패턴

행동 패턴은 객체 간의 상호작용과 책임 분배에 중점을 둡니다. 객체들이 효과적으로 소통하고 협력할 수 있도록 구조를 정의하며, 유연한 변경과 확장이 가능합니다.

🖥️ Strategy 패턴

Strategy 패턴은 알고리즘 군을 캡슐화하여 각각을 독립적으로 정의하고, 필요에 따라 동적으로 알고리즘을 변경할 수 있게 합니다. 이는 if-else나 switch문으로 여러 조건을 처리하는 대신 객체 지향적으로 문제를 해결합니다.

interface Strategy {
    void execute();
}

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

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

class Context {
    private Strategy strategy;

    public Context(Strategy strategy) {
        this.strategy = strategy;
    }

    public void setStrategy(Strategy strategy) {
        this.strategy = strategy;
    }

    public void executeStrategy() {
        strategy.execute();
    }
}

Context 클래스는 Strategy 인터페이스 타입의 객체를 보유하며, 클라이언트는 실행 중에 setStrategy() 메서드를 통해 원하는 전략을 바꿀 수 있습니다. 이는 코드의 유연성과 확장성을 극대화합니다.

💻 Observer 패턴

Observer 패턴은 한 객체의 상태 변화가 있을 때 그 객체에 의존하는 여러 객체들에게 자동으로 알림을 전달하는 패턴입니다. 이를 통해 객체 간 느슨한 결합을 유지하면서 상태 변화를 효율적으로 처리할 수 있습니다.

import java.util.ArrayList;
import java.util.List;

interface Observer {
    void update(String message);
}

class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        this.name = name;
    }

    public void update(String message) {
        System.out.println(name + " received: " + message);
    }
}

class Subject {
    private List observers = new ArrayList<>();

    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(String message) {
        for (Observer observer : observers) {
            observer.update(message);
        }
    }
}

Subject 객체는 상태 변화를 감지하면 등록된 모든 Observer에게 알림을 전달합니다. Observer는 독립적으로 동작하며, 필요한 경우 추가하거나 제거할 수 있어 유연한 이벤트 처리 구조를 만듭니다.


이번 강의에서 살펴본 디자인 패턴들은 개발자가 코드의 품질을 높이고, 유지보수와 확장을 쉽게 할 수 있도록 돕는 핵심 도구입니다. 패턴을 정확히 이해하고 적절히 활용하면 프로젝트의 성공 가능성을 크게 높일 수 있습니다. 앞으로 다양한 상황에서 이들 패턴을 응용해 보시길 권장합니다.

반응형

'Programming' 카테고리의 다른 글

JAVA Stream & 람다식  (46) 2025.11.28
JAVA 리플렉션과 어노테이션  (63) 2025.11.27
JAVA 네트워크 프로그래밍  (43) 2025.11.25
JAVA 쓰레드와 동시성  (62) 2025.11.24
JAVA 클래스 고급  (40) 2025.11.23