싱글톤 패턴이란?

  • 싱글톤 패턴이란 객체의 인스턴스가 1개만 생성되는 것을 보장하는 디자인 패턴입니다. -> 객체 인스턴스를 2개 이상 생성하지 못하도록 막아야 하고 private를 사용해서 외부에서 new 키워드를 사용해서 객체를 인스턴스 생성하는 것을 못하도록 막아야 합니다.

싱글톤 패턴 만드는 방법

public class Singleton {
    private static final Sigleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }

위에 있는 코드처럼 하면 객체의 인스턴스는 1개만 생성할 수 있습니다.
그런데 이렇게 작성할 경우 문제점이 있습니다.
문제점은 아래에서 확인해 보겠습니다.

싱글톤 패턴의 문제점

  • 싱글톤 패턴을 구현하는 코드 자체가 많이 들어갑니다.
  • 의존 관계 클라이언트가 추상화 클래스에 의존하는 게 아니라 구체 클래스에 의존하게 되면서 객체지향 설계 5원칙인 SOLID 중 DLP를 위반하고 OCP원칙을 위반할 가능성이 높습니다.
  • 내부 속성을 변경하고 초기화하는 게 어렵고 private 생성자로 자식 클래스를 만들기가 어렵습니다.

이러한 문제점을 해결하기 위해서는 스프링 컨테이너를 사용합니다.

스프링 컨테이너

  • 스프링 컨테이너는 싱글톤 패턴의 문제를 해결하면서 객체의 인스턴스를 싱글톤으로 관리합니다.
  • 스프링 빈이 싱글톤으로 관리되는 빈입니다다.
  • 싱글톤 패턴을 적용하지 않아도 객체 인스턴스를 싱글톤으로 관리합니다다.
  • 스프링 컨테이너는 싱글톤 컨테이너 역할을 하고 이렇게 싱글톤 객체를 생성하고 관리하는 기능을 싱글톤 레지스트리라 합니다.

-> 스프링 컨테이너의 이런 기능 덕분에 싱글톤 패턴의 모든 단점을 해결하면서 객체를 싱글톤으로 유지할 수 있습니다.

  • 스프링의 기본 빈 등록은 싱글톤입니다다.

싱글톤 방식의 주의점

  • 싱글톤 패턴은 객체의 상태를 유지하게 설계를 하면 안 됩니다.
  • 무상태로 설계를 해야 됩니다.

상태 유지 설계 코드

public class Stateful {
    private String name;

    String studentAddress(String name, String address) {
        System.out.println(name + "학생의 주소는 " + address);
        this.name = name;
        return name;
    }

    public String getName() {
        return name;
    }

    public static void main(String[] args) {

        //스프링 컨테이너에 설정 정보에 등록된 빈을 등록
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Test.class);

        // ac.getBean을 하여 스프링 컨테이너에 등록된 빈을 반환
        Stateful stateful1 = ac.getBean(Stateful.class);

        Stateful stateful2 = ac.getBean(Stateful.class);

        stateful1.studentAddress("hoestory","대한민국");

        stateful2.studentAddress("hoe", "미국");

        System.out.println("stateful1.getName = " + stateful1.getName());
        // stateful1.getName = hoe



    }

    static class Test {

        @Bean
        public Stateful    stateful() {
            return new Stateful();
        }

    }
}

결과값은 "hoestory"가 나와야 되는데 "hoe"가 나올 것입니다.

이유는 상태가 유지된 상태 stateful2.studentAddress("hoe", "미국"); 을 하여 name 부분에 "hoestory"에서 "hoe"로 변경되었기 때문입니다. 이렇게 되는 이유는 스프링 컨테이너에 빈으로 등록된 stateful 빈이 객체의 인스턴스를 한 번만 생성하기 때문에 stateful1과 stateful2는 같은 인스턴스입니다.

무상태 설계

  • 특정 클라이언트에 의존적인 필드가 있으면 안 됩니다.
  • 특정 클라이언트가 값을 변경할 수 있는 필드가 있으면 안 됩니다.
  • 필드 대신 자바에서 공유되지 않는 지역변수 파라미터 등을 사용해야 됩니다.
public class Stateful {
   

    String studentAddress(String name, String address) {
        System.out.println(name + "학생의 주소는 " + address);
       
        return name;
    }

  

    public static void main(String[] args) {

        //스프링 컨테이너에 설정 정보에 등록된 빈을 등록
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(Test.class);

        // ac.getBean을 하여 스프링 컨테이너에 등록된 빈을 반환
        Stateful stateful1 = ac.getBean(Stateful.class);

        Stateful stateful2 = ac.getBean(Stateful.class);

        String student1 = stateful1.studentAddress("hoestory","대한민국");

        String student2 = stateful2.studentAddress("hoe", "미국");

        System.out.println("statefu1.getName() = " + student1);
        // stateful1.getName() = hoestory



    }

    static class Test {

        @Bean
        public Stateful    stateful() {
            return new Stateful();
        }

    }
}

 

결과값은 상태 유지 코드의 결과값과 달리 상태가 유지 안되어서 "hoestory"가 나올 것입니다.