Skip to content

Item 1. 생성자 대신 정적 팩터리 메서드를 고려하라. #7

@byunghyunkim0

Description

@byunghyunkim0

Chapter : 2. 객체 생성과 파괴

Item : 1. 생성자 대신 정적 팩터리 메서드를 고려하라.

Assignee : byunghyunkim0


🍑 서론

클래스는 클라이언트에 public 생성자 대신 정적 팩토리 메서드를 제공할 수 있다.

  • 간단한 정적 팩토리 메서드의 예
public static Boolean valueOf(boolean b) {
    return b ? Boolean.TRUE : Boolean.FALSE;
}

🍑 본론

정적 팩토리 메서드(static factory method)

Static Method를 이용하여 인스턴스를 생성하는 방식

class Book {
    private String title;
    
    // 생성자를 private화 하여 외부에서 생성자 호출 차단
    private Book(String title) { this.title = title; }
    
    // 정적 팩토리 메서드
    public static Book titleOf(String title) {
        return new Book(title); // 메서드에서 생성자를 호출하고 리턴함
    }
}
public static void main(String[] args) {
	// 정적 메서드 호출을 통해 인스턴스화된 객체를 얻음
    Book book1 = Book.titleOf("어린왕자"); 
}

정적 팩토리 메서드의 장점 (5가지)

1. 이름을 가질 수 있다.

  • 생성자에 넘기는 매개변수만으로 반환되는 객체의 특성을 알기 어렵다.
  • 적적한 이름의 정적 메서드를 사용하면 반환될 객체의 특성을 묘사할 수 있다.
class Car {
    private String brand;
    private String color = "black";

    public Car(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    public Car(String brand) {
        this.brand = brand;
    }
}

public static void main(String[] args) {
    // 검정색 테슬라 자동차 
    Car teslaCar = new Car("Tesla");

    // 빨간색 BMW 자동차
    Car bmwRedCar = new Car("BMW", "Red");
}
class Car {
    private String brand;
    private String color;

    // private 생성자
    private Car(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }

    // 정적 팩토리 메서드 (매개변수 하나는 from 네이밍)
    public static Car brandBlackFrom(String brand) {
        return new Car(brand, "black");
    }

    // 정적 팩토리 메서드 (매개변수 여러개는 of 네이밍)
    public static Car brandColorOf(String brand, String color) {
        return new Car(brand, color);
    }
}
public static void main(String[] args) {
    // 검정색 테슬라 자동차 
    Car teslaCar = Car.brandBlackFrom("Tesla");

    // 빨간색 BMW 자동차
    Car bmwRedCar = Car.brandColorOf("BMW", "Red");
}

2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.

메서드를 통해 간접적으로 객체를 생성하기 때문에, 전반적인 객체 생성 및 통제 관리를 할 수 있다. 필요에 따라서 새로운 객체를 생성할수도 있고, 객체 하나만 만들어두고 이를 공유하여 재사용하게 하여 불필요한 객체 생성을 방지 할 수 있다.

import java.util.HashMap;
import java.util.Map;

public class Main {
    public static void main(String[] args) {
        // 이미 등록된 요일 가져오기
        Day day = DayFactory.from("Monday");
        System.out.println(day.getDay());

        // 등록되지 않은 요일 가져오기
        day = DayFactory.from("Friday");
        System.out.println(day.getDay());
    }
}
class Day {
    private String day;

    public Day(String day) { this.day = day; }

    public String getDay() { return day; }
}

// Day 객체를 생성하고 관리하는 Flyweight 팩토리 클래스
class DayFactory {

    // Day 객체를 저장하는 캐싱 저장소 역할
    private static final Map<String, Day> cache = new HashMap<>();

    // 자주 사용될것 같은 Day 객체 몇가지를 미리 등록한다
    static {
        cache.put("Monday", new Day("Monday"));
        cache.put("Tuesday", new Day("Tuesday"));
        cache.put("Wednesday", new Day("Wednesday"));
    }

    // 정적 팩토리 메서드 (인스턴스에 대해 철저한 관리)
    public static Day from(String day) {
        return cache.get(day);
    }
}

3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.

4. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

class Main{
    public static void main(String[] args) {
        SmarPhone s = SmarPhone.getPhone(600000);
        System.out.println(s instanceof IPhone);
    }
}
class Galaxy implements SmarPhone {}
class IPhone implements SmarPhone {}
class Huawei implements SmarPhone {}
interface SmarPhone {
    public static SmarPhone getPhone(int price) {
        if(price > 100000) {
            return new IPhone();
        }

        if(price > 50000) {
            return new Galaxy();
        }

        return new Huawei();
    }
}

5. 정적 팩토리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.

  • 어떤 타입의 객체를 반환할지 결정하는 로직이 팩토리 메서드 내부에 있기 때문에 클라이언트는 팩토리 메서드를 호출하기만 하면 되고, 클라이언트는 어떤 클래스의 객체가 생성되는지 알 필요가 없다.
  • 새로운 타입의 객체가 필요해진다면 팩토리 메서드만 수정하면 된다.
  • 유연한 확장성을 가질 수 있고, 내부 구현을 드러내지 않아 캡슐화할 수 있다는 장점도 있다.
public class CarFactory {
 
    private CarFactory() {
    }
 
    public static Car createCar(String type) {
        if (type.equals("sports")) {
            return new SportsCar();
        }
        if (type.equals("suv")) {
            return new SuvCar();
        } 
        throw new IllegalArgumentException("해당 타입의 자동차가 존재하지 않습니다 : " + type);
    }
}

정적 팩토리 메서드의 단점 (2가지)

1. 상속을 하려면 public이나 protected 생성자가 필요하니 정적 팩토리 메서드만 제공하면 하위 클래스를 만들 수 없다.

  • 생성자가 private이기 때문에 상속이 불가능하다.
  • "상속 보단 합성" 원칙에 따라서 상속보다는 합성을 사용하는 것을 권한다.

2. 정적 팩토리 메서드는 프로그래머가 찾기 어렵다.

🍑 결론


Referenced by

Metadata

Metadata

Assignees

Labels

🐳 02 Objects2장 객체 생성과 파괴🙇🏻 help wanted맡은 주제를 잘 모르겠어요. 도움이 필요해요.

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions