디자인 패턴

2025. 1. 17. 12:55
728x90

1. 싱글톤 패턴

더보기

싱글톤 패턴 

특정 클래스의 인스턴스가 하나만 존재하도록 보장하고, 이 인스턴스를 전역적으로 접근할 수 있게 하는 디자인 패턴. 이를 통해 시스템 리소스를 효율적으로 사용하고, 동일한 상태를 유지해야 하는 객체를 쉽게 관리할 수 있다. 

- 프로그램 내에서 하나의 객체만 존재해야 한다.

- 프로그램 내에서 여러 부분에서 해당 객체를 공유하며 사용해야 한다. 

ex. 데이터베이스 연결 모듈

 

장점

- 메모리 절약 : 하나의 인스턴스만 사용하므로 불필요한 객체 생성을 방지

- 전역 상태 공유 : 애플리케이션 전역에서 동일한 상태를 유지하며 접근 가능

- 객체 관리 용이 : 인스턴스를 중앙에서 관리하여 효율적인 자원 활요

 

단점

- 테스트 어려움 : 전역 상태를 공유하기 때문에 테스트 환경에서 각 테스트마다 독립적인 인스턴스를 만들기가 어려움. 

- 높은 결합도 : 싱글톤 객체를 전역적으로 참조하면 코드 간 결합도가 증가할 수 있음.

- 멀티스레드 이슈 : 적절히 구현하지 않으면 멀티스레드 환경에서 안전하지 않을 수 있음. 

 

구현 방법

1) 기본 구현 (Lazy Initialization) : 객체를 처음 요청할 때만 인스턴스를 생성

public class Singleton {
	private static Singleton instance;
    
    private Singleton() {} // private 생성자
    
    public static Singleton getInstance() {
    	if (instance == null) {
        	instance = new Singleton();
        }
        return instance;
    }
}

2) 스레드 안전 구현 : 멀티스레드 환경에서도 안전하도록 동기화 사용

public class Singleton {
	private static Singleton instance;
    
    private Singleton() {} // private 생성자
    
    public static synchronized Singleton getInstance() {
    	if (instance == null) {
        	instance = new Singleton();
        }
        return instance;
    }
}

3) 이른 초기화 (Eager Initialization) : 클래스 로딩 시점에 인스턴스를 생성

public class Singleton {
	private static final Singleton instance = new Singleton();
    
    private Singleton() {} // private 생성자
    
    public static Singleton getInstance() {
        return instance;
    }
}

4) Bill Pugh 방식 (권장) : 내부 정적 클래스를 사용하여 Lazy Initialization과 스레드 안전성을 동시에 보장

public class Singleton {
    private Singleton() {} // private 생성자
    
    private static class Holder {
    	private static final Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance() {
    	return Holder.instance;
    }
}

싱글톤 패턴은 클래스의 인스턴스를 단 하나만 생성하도록 보장하는 디자인 패턴입니다. 이는 메모리 사용을 효율화하고, 애플리케이션 전역에서 동일한 인스턴스를 공유하도록 합니다. 생성된 인스턴스는 어디에서든 getInstance 메서드로 접근 가능합니다. 

 

2. 빌더 패턴

더보기

빌더 패턴

복잡한 객체를 생성하는 과정을 단계별로 분리하여, 객체의 생성과정을 캡슐화하는 패턴. 이를 통해 생성 코드의 가독성을 높이고, 객체 생성 과정에서의 실수를 줄일 수 있다. 

 

사례

- 매개변수가 많은 객체 생성

- 불변 객체 생성

- 복잡한 객체 생성

 

장점

- 가독성 향상 : 메서드 체인을 사용하여 직관적으로 객체 생성 가능. 매개변수 순서를 헷갈릴 염려가 없음.

- 객체 생성의 유연성 : 필수 값과 선택 값을 명확히 구분 가능, 다양한 조합의 객체 생성 가능.

- 불변성 보장 : 객체를 생성 후 변경 불가능하게 설계 가능.

- 코드 유지보수성 증가 : 다양한 생성 옵션을 관리하기 쉬움

 

단점

- 구현 복잡성 증가 : 간단한 객체 생성의 경우 불필요하게 복잡한 코드를 유발할 수 있음.

- 객체 생성 비용 증가 : 추가적인 Builder 클래스와 메서드 호출로 인해 약간의 성능 저하가 있을 수 있음. 

 

구현

class StudentBuilder {
    private int id;
    private String name;
    private String grade;
    private String phoneNumber;

    public StudentBuilder id(int id) { ... }

    public StudentBuilder name(String name) { ... }

    public StudentBuilder grade(String grade) { ... }

    public StudentBuilder phoneNumber(String phoneNumber) { ... }

    public Student build() {
        return new Student(id, name, grade, phoneNumber); // Student 생성자 호출
    }
}

빌더 패턴은 객체 생성 과정이 복잡할 때, 객체를 단계적으로 구성하여 생성할 수 있도록 도와주는 생성 패턴 중 하나입니다. 생성자의 매개변수가 많거나 다양한 조합의 인스턴스를 생성해야 할 때 유용합니다. 주요 특징으로는 가독성 향상, 불변성 보장, 유연성이 있습니다. Java의 StringBuilder나 스프링에서의 BeanDefinitionBuilder 등이 빌더 패턴의 예시입니다. 

 

 

3. 팩토리 메서드 패턴

더보기

팩토리 메서드 패턴

객체 생성을 위한 인터페이스를 정의하고, 실제 객체 생성은 서브클래스에서 담당하게 하는 패턴. 

클래스의 인스턴스화를 서브클래스에 위임하여 객체 생성 로직을 캡슐화하고, 클라이언트와 객체 생성 로직 간의 결합도를 낮춘다. 

 

구조

- Creator : 객체를 생성하는 팩토리 메서드를 선언하는 상위 클래스 

- ConcreteCreator : Creator를 확장하여 팩토리 메서드를 구현하는 클래스

- Product : 팩토리 메서드가 생성하는 객체의 상위 인터페이스 또는 추상 클래스

- ConcreteProduct : Product 인터페이스를 구현한 실제 객체 

 

장점

- 객체 생성 코드 캡슐화 : 객체 생성 로직이 한 곳에 모여 관리되므로, 코드 깔끔, 유지보수성

- 확장성 : 새로운 객체 추가시 기존 코드를 수정하지 않고도 확장 가능

- 결합도 감소 : 클라이언트는 구체적인 클래스에 의존하지 않고, 상위 클래스나 인터페이스에 의존하므로 결합도 낮음. 

 

단점

- 구현 복잡성 증가

- 추상화의 오버헤드 : 간단한 객체 생성에는 불필요하게 느껴질 수 있음. 

 

구현

// Product 인터페이스
public interface Shape {
    void draw();
}

// ConcreteProduct 클래스
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Circle");
    }
}

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing a Rectangle");
    }
}

// Creator 추상 클래스
public abstract class ShapeFactory {
    public abstract Shape createShape();
}

// ConcreteCreator 클래스
public class CircleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Circle();
    }
}

public class RectangleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Rectangle();
    }
}

// 사용 예시
public class Main {
    public static void main(String[] args) {
        ShapeFactory factory = new CircleFactory();
        Shape shape = factory.createShape();
        shape.draw();  // Output: Drawing a Circle

        factory = new RectangleFactory();
        shape = factory.createShape();
        shape.draw();  // Output: Drawing a Rectangle
    }
}

팩토리 메서드 패턴은 객체를 사용하는 코드에서 객체 생성 부분을 떼어내 추상화한 패턴이자 상속 관계에 있는 두 클래스에서 상위 클래스가 중요한 뼈대를 결정하고, 하위 클래스에서 객체 생성에 관한 구체적인 내용을 결정하는 패턴입니다. 주요 특징은 객체 생성 로직을 한 곳에 모아 관리하고, 새로운 객체 유형을 추가할 때 클라이언트 코드를 수정하지 않아도 된다는 특징이 있습니다. 

728x90

'Programming > CS' 카테고리의 다른 글

네트워크2  (1) 2025.01.20
스프링  (0) 2025.01.14
데이터베이스  (0) 2025.01.11
운영체제  (0) 2025.01.10
네트워크  (0) 2025.01.09

BELATED ARTICLES

more