Item 1. Static Factory Method

μƒμ„±μž λŒ€μ‹  정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό κ³ λ €ν•˜λΌ

보톡 Javaλ₯Ό μ‚¬μš©ν•  λ•Œ 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό 생성할 λ•ŒλŠ” public μƒμ„±μžλ₯Ό μ‚¬μš©ν•œλ‹€. ν•˜μ§€λ§Œ 이 방법 말고도 μ•„λž˜μ™€ 같이 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œ(static factory method)λ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό 생성할 수 μžˆλ‹€.

public class Example {

    private Example() {
    }

    public static Example of() {
        return new Example();
    }
}

public class Main {

    public static void main(String[] args) {
        Example example = Example.of();
    }
}

정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œμ˜ μž₯점

μœ„ μ˜ˆμ‹œ μ½”λ“œμ™€ 같이 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό ν†΅ν•˜μ—¬ 객체λ₯Ό 생성을 ν•˜λ©΄ λ‹Ήμ—°νžˆ 단점듀도 μ‘΄μž¬ν•˜μ§€λ§Œ μž₯점듀도 μ‘΄μž¬ν•œλ‹€.

1. μƒμ„±μžμ— 이름(의미) λΆ€μ—¬

public μƒμ„±μžλŠ” 클래슀의 이름과 λ§€κ°œλ³€μˆ˜λ§ŒμœΌλ‘œ 객체λ₯Ό μƒμ„±ν•˜μ§€λ§Œ 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλŠ” λ©”μ„œλ“œ 자체의 이름을 지정할 수 μžˆμ–΄ 가독성이 쒋아진닀.

class UUIDExample {
    public static void main(String[] args) {
        // 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체 생성
        UUID randomUUID1 = UUID.randomUUID();
        UUID nameUUID1 = UUID.nameUUIDFromBytes("example".getBytes());

        // public μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체 생성(μ‹€μ œ μ‘΄μž¬ν•˜λŠ” μ½”λ“œx)
        UUID randomUUID2 = new UUID();
        UUID nameUUID2 = new UUID("example".getBytes());
    }
}

λ‹¨μˆœνžˆ public μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒμ„±ν–ˆλ‹€λ©΄ μ •ν™•νžˆ μ–΄λ–€ 객체λ₯Ό μƒμ„±ν•˜λŠ”μ§€ μ•ŒκΈ° 어렀웠을 것이닀. λ•Œλ¬Έμ— λ§Œμ•½ ν•˜λ‚˜μ˜ ν΄λž˜μŠ€κ°€ μ—¬λŸ¬ 개의 μƒμ„±μžλ₯Ό κ°€μ Έμ•Ό ν•œλ‹€λ©΄ pulbic μƒμ„±μžλ³΄λ‹€λŠ” 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 이름을 μ§€μ •ν•˜λŠ” 것이 μ’‹λ‹€.

2. 호좜될 λ•Œλ§ˆλ‹€ μΈμŠ€ν„΄μŠ€ 생성 ν•„μš” μ—†μŒ

public μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό 생성할 λ•Œλ§ˆλ‹€ μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€λ₯Ό μƒμ„±ν•˜μ§€λ§Œ 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ 미리 생성해둔 μΈμŠ€ν„΄μŠ€λ₯Ό μž¬μ‚¬μš©ν•  수 μžˆλ‹€.

class SingletonExample {

    // 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μΈμŠ€ν„΄μŠ€λ₯Ό 관리
    private static SingletonExample instance;

    // private μƒμ„±μžλ‘œ μ™ΈλΆ€μ—μ„œ μΈμŠ€ν„΄μŠ€ 생성 방지
    private SingletonExample() {
    }

    // 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œ
    public static SingletonExample getInstance() {
        if (instance == null) {
            instance = new SingletonExample();
        }
        return instance;
    }

    public void doSomething() {
        System.out.println("Singleton instance is here!");
    }
}

class Main {
    public static void main(String[] args) {
        // 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μΈμŠ€ν„΄μŠ€ 쑰회
        SingletonExample singleton = SingletonExample.getInstance();

        // μΈμŠ€ν„΄μŠ€λ₯Ό μ—¬λŸ¬ 번 가져와도 λ™μΌν•œ μΈμŠ€ν„΄μŠ€ λ°˜ν™˜
        SingletonExample anotherSingleton = SingletonExample.getInstance();

        // 두 κ°μ²΄λŠ” μ™„μ „ λ™μΌν•œ κ°μ²΄μ΄λ―€λ‘œ true
        System.out.println(singleton == anotherSingleton); // true

        // 객체 λ©”μ„œλ“œ 호좜
        singleton.doSomething();
    }
}

μœ„μ™€ 같이 μƒμ„±ν•˜λ©΄ μΈμŠ€ν„΄μŠ€λ₯Ό ν•˜λ‚˜λ‘œ μ œν•œν•  수 있으며, λ˜ν•œ μΈμŠ€ν„΄μŠ€λ₯Ό 미리 생성해두고 μž¬μ‚¬μš©ν•  수 μžˆμ–΄ λ©”λͺ¨λ¦¬ λ‚­λΉ„λ₯Ό 방지할 수 μžˆλ‹€. 이와 같이 μΈμŠ€ν„΄μŠ€λ₯Ό ν•˜λ‚˜λ‘œ μ œν•œν•˜λŠ” 것은 ν”ŒλΌμ΄μ›¨μ΄νŠΈ νŒ¨ν„΄μ˜ 근간이 λ˜λŠ” 기법이닀.

3. λ°˜ν™˜ νƒ€μž…μ˜ ν•˜μœ„ νƒ€μž… 객체 λ°˜ν™˜ κ°€λŠ₯

public μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό 생성할 λ•ŒλŠ” ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€λ§Œ λ°˜ν™˜ν•  수 μžˆμ§€λ§Œ 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ ν•΄λ‹Ή 클래슀의 ν•˜μœ„ νƒ€μž… 객체λ₯Ό λ°˜ν™˜ν•  수 μžˆλ‹€.

interface Shape {
    // ...

    // 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ Circle 객체 생성
    static Shape createCircle(double radius) {
        return new Circle(radius);
    }

    // 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ΄μš©ν•˜μ—¬ Rectangle 객체 생성
    static Shape createRectangle(double width, double height) {
        return new Rectangle(width, height);
    }
}

class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // ...
}

class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    // ...
}

class Main {
    public static void main(String[] args) {
        Shape circle = Shape.createCircle(10);
        Shape rectangle = Shape.createRectangle(10, 20);
    }
}

μœ„μ™€ 같이 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ Shape μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•œ Circleκ³Ό Rectangle 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•  수 μžˆλ‹€. μ‹€μ œλ‘œ Collection ν”„λ ˆμž„μ›Œν¬λŠ” 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό μƒμ„±ν•˜κ³  λ°˜ν™˜ν•˜λŠ”λ°, μ‚¬μš©μžλŠ” ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€μ˜ κ΅¬ν˜„μ²΄λ₯Ό μ•Œ ν•„μš” 없이 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό 톡해 객체λ₯Ό μƒμ„±ν•˜κ³  μ‚¬μš©ν•  수 있게 λœλ‹€.

4. μž…λ ₯ λ§€κ°œλ³€μˆ˜μ— 따라 맀번 λ‹€λ₯Έ 클래슀의 객체 λ°˜ν™˜ κ°€λŠ₯

public μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό 생성할 λ•ŒλŠ” ν•΄λ‹Ή 클래슀의 μΈμŠ€ν„΄μŠ€λ§Œ λ°˜ν™˜ν•  수 μžˆμ§€λ§Œ 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ μž…λ ₯ λ§€κ°œλ³€μˆ˜μ— 따라 λ‹€λ₯Έ 클래슀의 객체λ₯Ό λ°˜ν™˜ν•  수 μžˆλ‹€. μ‹€μ œλ‘œ EnumSet 클래슀의 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλŠ” μž…λ ₯ λ§€κ°œλ³€μˆ˜μ— 따라 RegularEnumSet(μ›μ†Œ 64개 μ΄ν•˜)κ³Ό JumboEnumSet(μ›μ†Œ 64개 초과)의 μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•œλ‹€. μ•„λž˜λŠ” λ§€κ°œλ³€μˆ˜ λ¬Έμžμ—΄μ„ μž…λ ₯λ°›μ•„ ν•΄λ‹Ή λ¬Έμžμ—΄μ— ν•΄λ‹Ήν•˜λŠ” Shape μΈν„°νŽ˜μ΄μŠ€μ˜ κ΅¬ν˜„μ²΄λ₯Ό λ°˜ν™˜ν•˜λŠ” μ˜ˆμ‹œμ΄λ‹€.

interface Shape {
    static Shape createShape(String shapeType) {
        if (shapeType == null) {
            return null;
        }
        if (shapeType.equalsIgnoreCase("CIRCLE")) {
            return new Circle();
        } else if (shapeType.equalsIgnoreCase("RECTANGLE")) {
            return new Rectangle();
        }
        return null;
    }

    void draw();
}

class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Circle");
    }
}

class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Rectangle");
    }
}

public class test {
    public static void main(String[] args) {
        Shape circle = Shape.createShape("circle");
        circle.draw();
        Shape rectangle = Shape.createShape("rectangle");
        rectangle.draw();
    }
}

3번과 λ§ˆμ°¬κ°€μ§€λ‘œ ν΄λΌμ΄μ–ΈνŠΈλŠ” ν•΄λ‹Ή μΈν„°νŽ˜μ΄μŠ€μ˜ κ΅¬ν˜„μ²΄λ₯Ό μ•Œ ν•„μš” 없이 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό μƒμ„±ν•˜κ³  μ‚¬μš©ν•  수 μžˆλŠ” μž₯점을 κ°€μ§€κ²Œ λœλ‹€.

5. 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μž‘μ„±ν•˜λŠ” μ‹œμ μ—λŠ” λ°˜ν™˜ν•  객체의 ν΄λž˜μŠ€κ°€ μ‘΄μž¬ν•˜μ§€ μ•Šμ•„λ„ 됨

public μƒμ„±μžλ₯Ό μ‚¬μš©ν•˜μ—¬ 객체λ₯Ό 생성할 λ•ŒλŠ” ν•΄λ‹Ή ν΄λž˜μŠ€κ°€ λ°˜λ“œμ‹œ μ‘΄μž¬ν•΄μ•Ό ν•˜μ§€λ§Œ 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λ©΄ ν•΄λ‹Ή ν΄λž˜μŠ€κ°€ μ‘΄μž¬ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€. 이런 νŠΉμ§•μ€ μ„œλΉ„μŠ€ 제곡자 ν”„λ ˆμž„μ›Œν¬(Service Provider Framework)λ₯Ό λ§Œλ“œλŠ” 근간이 λœλ‹€. λŒ€ν‘œμ μΈ μ„œλΉ„μŠ€ 제곡자 ν”„λ ˆμž„μ›Œν¬λ‘œλŠ” JDBCκ°€ μžˆλŠ”λ°, κ·Έ 쀑 DriverManager 클래슀의 μ‹€μ œ κ΅¬ν˜„ μ½”λ“œλ₯Ό 일뢀 가져와 보면 μ•„λž˜μ™€ κ°™λ‹€.

// Connection.java
public interface Connection extends Wrapper, AutoCloseable {
    // ...
}

// Driver.java
public interface Driver {
    // ...
}

// DriverManager.java
public class DriverManager {

    // List of registered JDBC drivers
    private static final CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();
    // ...

    /* Prevent the DriverManager class from being instantiated. */
    private DriverManager() {
    }

    public static void registerDriver(java.sql.Driver driver,
                                      DriverAction da)
            throws SQLException {

        /* Register the driver if it has not already been added to our list */
        if (driver != null) {
            registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
        } else {
            // This is for compatibility with the original DriverManager
            throw new NullPointerException();
        }

        println("registerDriver: " + driver);

    }

    @CallerSensitive
    public static Connection getConnection(String url)
            throws SQLException {

        java.util.Properties info = new java.util.Properties();
        return (getConnection(url, info, Reflection.getCallerClass()));
    }

    //  Worker method called by the public getConnection() methods.
    private static Connection getConnection(
            String url, java.util.Properties info, Class<?> caller) throws SQLException {

        // ...

        for (DriverInfo aDriver : registeredDrivers) {
            // If the caller does not have permission to load the driver then
            // skip it.
            if (isDriverAllowed(aDriver.driver, callerCL)) {
                try {
                    println("    trying " + aDriver.driver.getClass().getName());
                    Connection con = aDriver.driver.connect(url, info);
                    if (con != null) {
                        // Success!
                        println("getConnection returning " + aDriver.driver.getClass().getName());
                        return (con);
                    }
                } catch (SQLException ex) {
                    if (reason == null) {
                        reason = ex;
                    }
                }

            } else {
                println("    skipping: " + aDriver.driver.getClass().getName());
            }

        }

        // if we got here nobody could connect.
        if (reason != null) {
            println("getConnection failed: " + reason);
            throw reason;
        }

        println("getConnection: no suitable driver found for " + url);
        throw new SQLException("No suitable driver found for " + url, "08001");
    }
}

μœ„ μ½”λ“œμ—μ„œ 확인할 수 μžˆλŠ” 것을 μ •λ¦¬ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€.

  • private μƒμ„±μžλ‘œ μ™ΈλΆ€μ—μ„œ μΈμŠ€ν„΄μŠ€ 생성 방지

  • ArrayList둜 객체듀을 관리

  • registerDriver() λ©”μ„œλ“œ νŒŒλΌλ―Έν„°μ˜ Driver μΈν„°νŽ˜μ΄μŠ€(= μ„œλΉ„μŠ€ 제곡자 μΈν„°νŽ˜μ΄μŠ€)λ₯Ό 톡해 객체λ₯Ό 생성(= 제곡자 등둝 API)

  • getConnection() λ©”μ„œλ“œλ₯Ό 톡해 Connection μΈν„°νŽ˜μ΄μŠ€(= μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€)λ₯Ό 톡해 객체λ₯Ό λ°˜ν™˜(= μ„œλΉ„μŠ€ μ ‘κ·Ό API)

이λ₯Ό κ°„λž΅ν™” ν•˜λ©΄ μ•„λž˜μ™€ 같이 정리 ν•  수 μžˆλ‹€.

// Service μΈν„°νŽ˜μ΄μŠ€: κ΅¬ν˜„μ²΄μ˜ λ™μž‘μ„ μ •μ˜
interface Service {
    void execute();
}

// μ„œλΉ„μŠ€ 제곡자 클래슀 1
class ServiceProvider1 implements Service {
    @Override
    public void execute() {
        // κ΅¬ν˜„ λ‚΄μš©
        System.out.println("ServiceProvider1 μ‹€ν–‰");
    }
}

// μ„œλΉ„μŠ€ 제곡자 클래슀 2
class ServiceProvider2 implements Service {
    @Override
    public void execute() {
        // κ΅¬ν˜„ λ‚΄μš©
        System.out.println("ServiceProvider2 μ‹€ν–‰");
    }
}

class ServiceRegistry {
    private static Map<String, Service> services = new HashMap<>();

    // μ„œλΉ„μŠ€ 제곡자 등둝 API: μ œκ³΅μžκ°€ κ΅¬ν˜„μ²΄λ₯Ό 등둝할 λ•Œ μ‚¬μš©
    public static void registerService(String name, Service service) {
        services.put(name, service);
    }

    // μ„œλΉ„μŠ€ μ ‘κ·Ό API: ν΄λΌμ΄μ–ΈνŠΈκ°€ μ„œλΉ„μŠ€μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό 얻을 λ•Œ μ‚¬μš©
    public static Service getService(String name) {
        return services.get(name);
    }
}

정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œμ˜ 단점

1. private μƒμ„±μžλ§Œ μ‘΄μž¬ν•˜λ©΄ ν•˜μœ„ 클래슀λ₯Ό λ§Œλ“€ 수 μ—†μ–΄ 상속이 λΆˆκ°€λŠ₯

public class Parent {
    private Parent() {
    }

    public static Parent createInstance() {
        return new Parent();
    }
}

public class Child extends Parent { // 컴파일 였λ₯˜, 상속 λΆˆκ°€λŠ₯
}

μ΄λŠ” λ‹¨μ μ΄μ§€λ§Œ, μƒμ†λ³΄λ‹€λŠ” μ»΄ν¬μ§€μ…˜μ„ μ‚¬μš©ν•˜λ„λ‘ μœ λ„ν•˜κ³  λΆˆλ³€ νƒ€μž…μœΌλ‘œ λ§Œλ“€ 수 μžˆμ–΄ λΆˆλ³€μ„±μ„ 보μž₯ν•  수 μžˆλŠ” μž₯점이 μžˆλ‹€.

2. 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œμ˜ 이름을 μ•Œμ•„μ•Ό 함

μƒμ„±μžλŠ” 클래슀의 이름과 λ™μΌν•˜μ§€λ§Œ 정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλŠ” 이름을 지정할 수 있기 λ•Œλ¬Έμ— ν•΄λ‹Ή λ©”μ„œλ“œμ˜ 이름을 μ•Œμ•„μ•Ό ν•œλ‹€. μ΄λŸ¬ν•œ 단점 μžˆμ–΄ 널리 μ•Œλ €μ§„ 넀이밍 κ·œμ•…μ„ 톡해 문제λ₯Ό μ™„ν™”ν•˜κ³  μžˆλ‹€.

λ©”μ„œλ“œ 이름섀λͺ…μ˜ˆμ‹œ

from

λ§€κ°œλ³€μˆ˜λ₯Ό ν•˜λ‚˜ λ°›μ•„μ„œ ν•΄λ‹Ή νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜λŠ” ν˜•λ³€ν™˜ λ©”μ„œλ“œ

Date d = Date.from(instant);

of

μ—¬λŸ¬ λ§€κ°œλ³€μˆ˜λ₯Ό λ°›μ•„μ„œ μ ν•©ν•œ νƒ€μž…μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜λŠ” 집계 λ©”μ„œλ“œ

Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);

valueOf

fromκ³Ό of의 더 μžμ„Έν•œ 버전

BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);

instance | getInstance

λ§€κ°œλ³€μˆ˜λ‘œ λͺ…μ‹œν•œ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•˜μ§€λ§Œ, 같은 μΈμŠ€ν„΄μŠ€μž„μ„ 보μž₯ν•˜μ§€ μ•ŠμŒ

StackWalker luke = StackWalker.getInstance(options);

create | newInstance

맀번 μƒˆλ‘œμš΄ μΈμŠ€ν„΄μŠ€ 생성 보μž₯

Object newArray = Array.newInstance(classObject, arrayLen);

getType

getInstance와 κ°™μœΌλ‚˜, 생성할 ν΄λž˜μŠ€κ°€ μ•„λ‹Œ λ‹€λ₯Έ ν΄λž˜μŠ€μ— νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ •μ˜ν•  λ•Œ μ‚¬μš©

FileStore fs = Files.getFileStore(path);

newType

newInstance와 κ°™μœΌλ‚˜, 생성할 ν΄λž˜μŠ€κ°€ μ•„λ‹Œ λ‹€λ₯Έ ν΄λž˜μŠ€μ— νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ •μ˜ν•  λ•Œ μ‚¬μš©

BufferedReader br = Files.newBufferedReader(path);

type

getTypeκ³Ό newType의 κ°„κ²°ν•œ 버전

List<Complaint> litany = Collections.list(legacyLitany);

Last updated