본문 바로가기

BE/Spring

[Spring] 디자인 패턴 ( 싱글톤 패턴, 팩토리 메소드 패턴, 프록시 패턴 )

 

 

 

 

 

싱글톤 패턴 ( Singleton Pattern )

사용 이유 : 싱글톤 패턴은 전체 애플리케이션에서 단 하나의 인스턴스만을 생성하여 사용하고자 할 때 사용한다

이는 공유 리소스 관리나 크로스-커팅 서비스( 애플리케이션의 여러 부분에 걸쳐 공통적으로 적용되는 기능을 말한다 ) 제공에 유용하다

 

스프링에서는 기본적으로 모든 빈객체를 싱글톤으로 생성하여 관리한다

 

싱글톤 사용의 장점

1. 메모리 낭비 방지

2. 전역 인스턴스로 애플리케이션 어디서나 접근이 가능

3. 데이터 공유의 용이성

 

싱글톤 사용의 단점

1. 싱글톤 인스턴스가 너무 많은 역할을 담당하게 되면 변경과 테스트가 어려워진다

2. 멀티스레드 환경에서 동기화 처리가 필요하다 ( 생성된 빈 객체를 동시에 사용할 수 없기 때문 )

 

 

 

 

 

 

 

 

 

 

팩토리 메소드 패턴 ( Factory Method Pattern )

사용 이유 : 객체 생성 처리를 서브 클래스로 분리하여 객체 생성 로직의 변경이나 확장이 필요할 때 유연하게 대처하기 위해 사용

 

스프링에서 인스턴스 생성을 추상화하고, 이를 상속받는 서브 클래스에서 해당 인스턴스의 구체적인 타입을 결정하게 하게 한다

public interface PaymentProcessor {
    void processPayment();
}

public class CreditCardProcessor implements PaymentProcessor {
    @Override
    public void processPayment() {
        // 신용카드 결제 로직 처리
    }
}

public class PayPalProcessor implements PaymentProcessor {
    @Override
    public void processPayment() {
        // PayPal 결제 로직 처리
    }
}

@Component
public class PaymentProcessorFactoryImpl implements PaymentProcessorFactory {
    @Override
    public PaymentProcessor createPaymentProcessor() {
        // 설정 등에 따라 생성할 프로세서 결정 로직
        return new CreditCardProcessor();
    }
}

코드 예시로서 결제에 관련된 인터페이스를 생성한 뒤 해당 인터페이스를 상속받아 기능에 따라 다양하게 구현

 

팩토리 메소드 패턴 사용의 장점

1. 유연성 : 팩토리 메소드 패턴은 프로그램 실행 중에 어떤 객체를 생성할지 결정할 수 있게 해준다, 이는 프로그램이 더 유연하게 동작하도록 돕는다

2. 낮은 결합도 : 클라이언트 코드가 구체적인 클래스를 몰라도 되기 때문에 서로 간의 의존성이 낮아진다

3. 유지보수의 용이성 : 팩토리 메소드 패턴을 사용하면 새로운 객체 타입을 추가하거나 변경해도 기존 코드를 수정할 필요가 없어 유지보수가 쉬워진다

 

팩토리 메소드 패턴 사용의 단점

1. 복잡성 증가 : 팩토리 메소드 패턴을 사용하면 각 객체마다 팩토리 클래스가 필요하기 때문에 코드가 복잡해질 수 있다

2. 서브 클래스 증가 : 새로운 객체 타입을 추가할때마다 새로운 서브 클래스를 만들어야 하기 때문에 클래스의 갯수가 증가하며 프로젝트의 크기와 복잡성을 증가시킬 수 있다 ( 서브 클래스의 증가는 관리해야 할 코드의 양의 증가와 테스트와 디버깅을 더 복잡하게 만들 수 있다 )

 

 

 

 

 

 

 

 

 

 

프록시 패턴 ( Proxy Pattern )

사용 이유 : 원본 객체에 대한 접근을 제어하거나, 원본 객체의 생명주기를 관리하거나, 원본 객체의 작업을 가상화하기 위해 사용

 

// 파일 인터페이스
public interface File {
    byte[] load();
}

// 실제 파일을 로드하는 클래스
public class RealFile implements File {
    private String path;

    public RealFile(String path) {
        this.path = path;
        loadFromDisk(path);
    }

    private void loadFromDisk(String path) {
        System.out.println("디스크에서 파일 로드: " + path);
    }

    @Override
    public byte[] load() {
        System.out.println("파일 내용을 반환합니다.");
        return new byte[0]; // 실제 파일 내용을 반환
    }
}

// RealFile 객체의 생성을 지연시키는 프록시 클래스
public class ProxyFile implements File {
    private RealFile realFile;
    private String path;

    public ProxyFile(String path) {
        this.path = path;
    }

    @Override
    public byte[] load() {
        if (realFile == null) {
            realFile = new RealFile(path);
        }
        return realFile.load();
    }
}

// 클라이언트 코드
public class ProxyExample {
    public static void main(String[] args) {
        File file = new ProxyFile("test_file.txt");
        // 파일이 실제로 필요할 때만 로드됩니다.
        file.load();
    }
}

예시 코드 ( ProxyFile 클래스에서는 요청한 파일의 객체가 이미 생성된 상태라면 다시 파일을 불러와서 객체를 생성하는 과정을 거치지 않고 이미 생성되어 있는 객체를 재사용한다 )

 

프록시 패턴 사용의 장점

1. 원본 객체를 보호하며 접근을 제어할 수 있음

2. 원본 객체의 로딩 시간 최적화

3. 원본 객체와 클라이언트 사이의 결합도 감소

 

프록시 패턴 사용의 단점

1. 응답 시간이 느려질 수 있다

2. 구현이 복잡해질 수 있다

728x90