백엔드(Java)/Effective_Java

1. 생성자 대신 왜 정적 팩토리 메서드를 사용하는가?

*티몬* 2021. 10. 6. 23:00

해당 Item1의 제목은 생성자 대신 정적 팩토리 메서드를 이용하라 라는 제목이었는데, 제목이 너무 딱딱하고 질문이 아니다보니 수동적인 느낌이 강한 것 같아서 나는 "왜 정적 팩토리 메서드를 사용하는가?" 라고 제목을 붙여봤다

그리고 책을 읽는데 책에서 설명하는 부분이 그렇게 설명이 친절하지 않아서 ChatGPT에게

java에서 static factory method 패턴에 대해서 설명해주고 어떤 경우에 사용하는지와 예제 코드를 보여줘

라고 질문했다

그래서 ChatGPT가 해준 답변은 다음과 같았다

자바에서 정적 팩토리 메서드(Static Factory Method) 패턴은 클래스의 인스턴스를 생성하는 대신, 정적 메서드를 사용하여 인스턴스를 반환하는 디자인 패턴입니다. 이 패턴은 다양한 이유로 사용되며, 일반적인 생성자 대신에 몇 가지 장점을 제공합니다.
정적 팩토리 메서드 패턴의 장점:

1. 메서드의 이름을 통해 메서드의 목적을 명확하게 전달할 수 있습니다.
2. 호출할 때마다 새로운 객체를 생성할 필요가 없어, 인스턴스를 캐싱하거나 재사용할 수 있습니다.
3. 반환되는 객체의 클래스를 자유롭게 변경할 수 있으므로, 리턴 타입의 하위 클래스를 반환할 수 있습니다.
4. 반환 타입의 인터페이스를 구현한 객체를 반환함으로써, 메서드의 반환 타입을 더 유연하게 만들 수 있습니다.
정적 팩토리 메서드 패턴은 다음과 같은 경우에 사용할 수 있습니다:

1. 명확한 이름으로 인스턴스 생성을 설명하고 싶을 때
2. 인스턴스 생성에 대한 추가적인 로직이 필요한 경우
3. 인스턴스 재사용이 중요한 경우
4. 인터페이스를 구현하는 여러 클래스들 중에서 적절한 클래스를 반환해야 하는 경우

그리고 예제 코드도 받아왔다

import java.util.HashMap;
import java.util.Objects;

public class Car {

    private String model;
    private int year;
    private String type; // 차량 종류를 저장하는 속성

    // 이미 생성된 인스턴스를 저장하고 관리하기 위한 HashMap
    private static final HashMap<String, Car> instances = new HashMap<>();

    // private 생성자
    private Car(String model, int year, String type) {
        this.model = model;
        this.year = year;
        this.type = type; // 차량 종류를 설정
    }

    // 정적 팩토리 메서드: 전기차 인스턴스 생성
    public static Car createElectricCar(String model, int year) {
        String key = "electric_" + model + "_" + year; // 고유한 키 생성
        if (!instances.containsKey(key)) { // 인스턴스가 존재하지 않으면 새 인스턴스 생성 및 저장
            Car car = new Car(model, year, "electric");
            instances.put(key, car);
        }
        return instances.get(key); // 기존 인스턴스 반환
    }

    // 정적 팩토리 메서드: 가솔린차 인스턴스 생성
    public static Car createGasolineCar(String model, int year) {
        String key = "gasoline_" + model + "_" + year; // 고유한 키 생성
        if (!instances.containsKey(key)) { // 인스턴스가 존재하지 않으면 새 인스턴스 생성 및 저장
            Car car = new Car(model, year, "gasoline");
            instances.put(key, car);
        }
        return instances.get(key); // 기존 인스턴스 반환
    }

    // getter 메서드
    public String getModel() {
        return model;
    }

    public int getYear() {
        return year;
    }

    public String getType() {
        return type;
    }

    // equals 메서드: 두 객체의 동일성 판단
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Car car = (Car) o;
        return year == car.year &&
                Objects.equals(model, car.model) &&
                Objects.equals(type, car.type);
    }

    // hashCode 메서드: 객체의 고유한 해시코드 생성
    @Override
    public int hashCode() {
        return Objects.hash(model, year, type);
    }
}

예제코드도 훌륭하다. 위 나온 예제코드가 한번에 나온건 아니고 몇 개의 오류가 있어서 Chat에게 몇번의 지적을 통한 수정과정을 걸쳐서 나온 코드다. 

그런데 위 코드를 인터페이스로 적용하면 아래와 같이 나올 수 있다

import java.util.HashMap;

// Car 인터페이스 정의
interface Car {
    String getModel();
    int getYear();
    String getType();
}

// 전기차 클래스 구현
public class ElectricCar implements Car {

    private String model;
    private int year;
    private String type;

    public ElectricCar(String model, int year) {
        this.model = model;
        this.year = year;
        this.type = "electric";
    }

    @Override
    public String getModel() {
        return model;
    }

    @Override
    public int getYear() {
        return year;
    }

    @Override
    public String getType() {
        return type;
    }
}

// 가솔린차 클래스 구현
public class GasolineCar implements Car {

    private String model;
    private int year;
    private String type;

    public GasolineCar(String model, int year) {
        this.model = model;
        this.year = year;
        this.type = "gasoline";
    }

    @Override
    public String getModel() {
        return model;
    }

    @Override
    public int getYear() {
        return year;
    }

    @Override
    public String getType() {
        return type;
    }
}

// 정적 팩토리 메서드를 제공하는 CarFactory 클래스
public class CarFactory {

    // 이미 생성된 인스턴스를 저장하고 관리하기 위한 HashMap
    private static final HashMap<String, Car> instances = new HashMap<>();

    // 전기차 인스턴스 생성 메서드
    public static Car createElectricCar(String model, int year) {
        String key = "electric_" + model + "_" + year; // 고유한 키 생성
        if (!instances.containsKey(key)) { // 인스턴스가 존재하지 않으면 새 인스턴스 생성 및 저장
            Car car = new ElectricCar(model, year);
            instances.put(key, car);
        }
        return instances.get(key); // 기존 인스턴스 반환
    }

    // 가솔린차 인스턴스 생성 메서드
    public static Car createGasolineCar(String model, int year) {
        String key = "gasoline_" + model + "_" + year; // 고유한 키 생성
        if (!instances.containsKey(key)) { // 인스턴스가 존재하지 않으면 새 인스턴스 생성 및 저장
            Car car = new GasolineCar(model, year);
            instances.put(key, car);
        }
        return instances.get(key); // 기존 인스턴스 반환
    }
}

예제코드

https://github.com/timonPark/EFFECTIVE_JAVA_STUDY/tree/item01