JAVA

[Java] 싱글톤 패턴(Singleton), 빌더 패턴(Builder)

바켠주 2024. 6. 8. 19:45
728x90

싱글톤 패턴

특정 클래스의 인스턴스를 하나만 생성해 사용하기 위한 생성 패턴

생성자를 여러번 호출해도 하나의 인스턴스만 존재하도록 한다.

✅싱글톤 패턴 구현

조건

1. 해당 클래스 타입의 static 변수 

2. private 접근지정자를 가진 기본 생성자

3. 인스턴스를 하나만 생성하는 코드를 가진 static 메서드

public class StudentService {
    private static StudentService instance;
    
    private StudentService() {}

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

 

생성자를 private으로 설정한 이유는 public의 경우 new를 사용해 생성자를 호출할 때마다 새로운 객체가 생성되기 때문에 외부에서 호출을 막고 하나의 객체를 하나만 생성해 공유하기 위함이다.

public class StudentRepository { 
    private String[] names;

    private static StudentRepository instance; //1. 인스턴스 변수

    private StudentRepository() { //2. 생성자 private
        names = new String[5];
    }

    public static StudentRepository getInstance(){ //3. static 메서드
        if(instance == null) {
            instance = new StudentRepository();
        }
        return instance;
    }

    public void add(String name) {
        for(int i = 0; i < names.length; i++) {
            if(names[i] == null) {
                names[i] = name;
                break;
            }
        }
    }

    public String[] getNames() {
        return names;
    }

}

⭕ 싱글톤 패턴 사용

StudentService studentService1 = StudentService.getInstance();
StudentService studentService2 = StudentService.getInstance();

 

studentService1, 2 변수를 두 개 선언하고 생성해도 하나의 인스턴스를 공유한다.

studentService, studentService2 모두 동일한 메모리 주소를 담고있다.

public void addStudent() {
	StudentRepository.getInstance().add("test");
}

 

❌싱글톤 패턴 사용x

StudentService studentService1 = new StudentService();
StudentService studentService2 = new StudentService();

 

studentService1, 2 변수를 두 개 선언하면 각각의 변수에 서로 다른 메모리 주소가 할당된다. 

public void addStudent(StudentRepository studentRepository) {
      StudentRepository studentRepository = new StudentRepository();
      studentRepository.add("test");
}

 


빌더 패턴

하나의 생성자로 여러가지 타입의 객체를 생성할 수 있게한다.

➡️사용하는 이유

Data 클래스를 생성할 때 초기값을 지정하고 싶은 경우 생성자를 만든다.

생성자 오버로딩을 사용해 다양한 타입을 가지는 생성자를 만들 수 있지만 변수가 많으면 모든 타입을 다 만들기 힘들다.

 public Data(String data1, int data2, double data3, String data4) {
        this.data1 = data1;
        this.data2 = data2;
        this.data3 = data3;
        this.data4 = data4;
}

public Data(int data2, double data3) {
        this.data2 = data2;
        this.data3 = data3;
}

 

생성자를 위의 코드처럼 두개만 만들었는데 data1만 넣어야하거나 또 다른 타입만 넣어야하는 경우가 있다.

이때 모든 경우의 생성자를 만드는 것 보다 기본생성자와 setter를 사용하면 다양한 타입의 객체를 생성할 수 있다.

Data data2 = new Data();
data2.setData1("a");
data2.setData3(3.14);

 

위 코드처럼 원하는 데이터만 setter를 사용해 대입할 수 있게된다.

이렇게 기본 생성자와 setter를 사용하는 것을 패턴으로 정의한게 빌더패턴이다.

 

➡️빌더 패턴 구현

빌더 패턴 구조

- Student 클래스 :  멤버변수로 studentCode, name, age를 가진다. 전체 생성자 가진다.

- StudentBuilder 클래스 : static으로 생성, 내부 클래스로 Student의 객체 생성을 담당한다.

 

작성 순서

1. Student 클래스 내부에 StudentBuilder 클래스를 작성

2. Student 클래스에 Builder 메서드를 static으로 작성

4. StudentBuilder 클래스에 build 메서드 작성

5. StudentBuilder 클래스에 변수명과 동일한 setter 메소드 작성

 

1. Student 클래스 내부에 StudentBuilder 클래스를 작성하고 동일한 멤버변수를 가지도록한다.

public class Student {
    private int studentCode;
    private String name;
    private int age;

    public Student(int studentCode, String name, int age) {
        this.studentCode = studentCode;
        this.name = name;
        this.age = age;
    }

    public static class StudentBuilder {
        private int studentCode;
        private String name;
        private int age;
	}
}

 

2. Student 클래스에 Builder 메서드 작성

static으로 선언하고 반환타입은 StudentBuilder이다.

StudentBuilder 객체를 생성하는 역할을 한다.

public static StudentEntityBuilder builder() {
       return new StudentBuilder();
}

 

3. StudentBuilder 클래스에 build 메서드 작성 

멤버변수들로 Student 객체를 생성한다.

public Student build() {
	return new Student(studentCode, name, age);
}

 

4. Setter메서드 작성

멤버변수와 동일하게 작성한다. (개수, 이름)

this를 리턴한다는 것은 자신의 주소를 리턴하는 것이다. 

Student 객체를 생성하는 역할을 한다.

public StudentEntityBuilder studentCode(int studentCode) {
	this.studentCode = studentCode;
	return this;
}

public StudentEntityBuilder name(String name) {
	this.name = name;
	return this;
}

public StudentEntityBuilder age(int age) {
	this.age = age;
	return this;
}

 

➡️빌더 패턴 사용

Main클래스에서 사용한다.

Student student1 = Student.builder()
	.studentCode(1)
	.name("학생1")
	.age(19)
	.build();
Student student2 = Student.builder()
	.studentCode(1)
	.name("학생2")
	.build();

 

student1에서는 모든 값을 지정했고 student에는 코드와 이름만 값을 지정했다.

 

 

builder()은 static메서드이기 때문에 객체를 생성하지 않고 바로 사용이 가능하다.

동일한 주소를 반환하도록 되어있기 때문에 그 주소를 계속해서 참조할 수 있고 모두 같은 객체에 값을 대입할 수 있다.

만약 student2 처럼 age를 대입하지 않으면 age의 타입 int의 기본값인 0이 대입된다.

name이 없다면 String의 기본값 null이 대입된다.

그리고 마지막 build()에서 Student 객체를 생성하면서 앞의 값들이 대입된다.