BOOK/JAVA

μ΄νŽ™ν‹°λΈŒ μžλ°” : Item1. μƒμ„±μž λŒ€μ‹  정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό κ³ λ €ν•˜λΌ

devJK93 2024. 12. 29.

λͺ©μ°¨ο„ƒ

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

Effective Java의 μ €μž μ‘°μŠˆμ•„ λΈ”λ‘œν¬(Joshua Bloch)λŠ” μƒμ„±μž λŒ€μ‹  정적 νŒ©ν„°λ¦¬ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 것을 ꢌμž₯ν•˜λ©°, κ·Έ 이유둜 μ—¬λŸ¬ 가지 μž₯점을 μ œμ‹œν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. μ•„λž˜μ—μ„œ 각 μž₯점과 단점을 μ½”λ“œ μ˜ˆμ‹œμ™€ ν•¨κ»˜ μžμ„Ένžˆ μ„€λͺ…ν•˜κ² μŠ΅λ‹ˆλ‹€.

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

1. 이름을 κ°€μ§ˆ 수 μžˆλ‹€

μƒμ„±μžλŠ” 클래슀 이름을 κ°€μ§€λ―€λ‘œ, μ—¬λŸ¬ μƒμ„±μžκ°€ μžˆμ„ 경우 ꡬ뢄이 μ–΄λ ΅μŠ΅λ‹ˆλ‹€. 반면, 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλŠ” 의미 μžˆλŠ” 이름을 κ°€μ§ˆ 수 μžˆμ–΄ μ½”λ“œ 가독성이 ν–₯μƒλ©λ‹ˆλ‹€.

μ˜ˆμ‹œ:

public class User {
    private String username;
    private String email;

    private User(String username, String email) {
        this.username = username;
        this.email = email;
    }

    // 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œ
    public static User fromUsername(String username) {
        return new User(username, username + "@example.com");
    }

    public static User fromEmail(String email) {
        String username = email.substring(0, email.indexOf("@"));
        return new User(username, email);
    }

    @Override
    public String toString() {
        return "User{username='" + username + "', email='" + email + "'}";
    }
}
    

μ‚¬μš© 예:

public class Main {
    public static void main(String[] args) {
        User user1 = User.fromUsername("john_doe");
        User user2 = User.fromEmail("jane@example.com");

        System.out.println(user1); // User{username='john_doe', email='john_doe@example.com'}
        System.out.println(user2); // User{username='jane', email='jane@example.com'}
    }
}
    

2. 호좜될 λ•Œλ§ˆλ‹€ μΈμŠ€ν„΄μŠ€λ₯Ό μƒˆλ‘œ μƒμ„±ν•˜μ§€λŠ” μ•Šμ•„λ„ λœλ‹€

정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλŠ” ν•„μš”μ— 따라 κΈ°μ‘΄ μΈμŠ€ν„΄μŠ€λ₯Ό μž¬μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” λ©”λͺ¨λ¦¬ μ ˆμ•½κ³Ό μ„±λŠ₯ ν–₯상에 도움이 λ©λ‹ˆλ‹€.

μ˜ˆμ‹œ:

public class BooleanWrapper {
    private final boolean value;

    private static final BooleanWrapper TRUE = new BooleanWrapper(true);
    private static final BooleanWrapper FALSE = new BooleanWrapper(false);

    private BooleanWrapper(boolean value) {
        this.value = value;
    }

    public static BooleanWrapper valueOf(boolean value) {
        return value ? TRUE : FALSE;
    }

    @Override
    public String toString() {
        return Boolean.toString(value);
    }
}
    

μ‚¬μš© 예:

public class Main {
    public static void main(String[] args) {
        BooleanWrapper bw1 = BooleanWrapper.valueOf(true);
        BooleanWrapper bw2 = BooleanWrapper.valueOf(true);
        BooleanWrapper bw3 = BooleanWrapper.valueOf(false);

        System.out.println(bw1 == bw2); // true
        System.out.println(bw1 == bw3); // false
    }
}
    

3. λ°˜ν™˜ νƒ€μž…μ˜ ν•˜μœ„ νƒ€μž… 객체λ₯Ό λ°˜ν™˜ν•  수 μžˆλŠ” λŠ₯λ ₯이 μžˆλ‹€

정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλŠ” λ°˜ν™˜ νƒ€μž…μ˜ ν•˜μœ„ νƒ€μž… 객체λ₯Ό λ°˜ν™˜ν•  수 μžˆμ–΄ μœ μ—°μ„±μ΄ λ†’μŠ΅λ‹ˆλ‹€.

μ˜ˆμ‹œ:

public interface Shape {
    void draw();
}

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

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

public class ShapeFactory {
    public static Shape createShape(String type) {
        switch(type.toLowerCase()) {
            case "circle":
                return new Circle();
            case "square":
                return new Square();
            default:
                throw new IllegalArgumentException("Unknown shape type");
        }
    }
}
    

μ‚¬μš© 예:

public class Main {
    public static void main(String[] args) {
        Shape shape1 = ShapeFactory.createShape("circle");
        Shape shape2 = ShapeFactory.createShape("square");

        shape1.draw(); // Drawing Circle
        shape2.draw(); // Drawing Square
    }
}
    

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

μž…λ ₯ λ§€κ°œλ³€μˆ˜μ— 따라 λ‹€μ–‘ν•œ 클래슀의 μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•  수 μžˆμ–΄ μœ μ—°ν•œ 객체 생성을 ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜ˆμ‹œ:

public interface Connection {
    void connect();
}

public class HttpConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("Connecting via HTTP");
    }
}

public class FtpConnection implements Connection {
    @Override
    public void connect() {
        System.out.println("Connecting via FTP");
    }
}

public class ConnectionFactory {
    public static Connection getConnection(String protocol) {
        switch(protocol.toLowerCase()) {
            case "http":
                return new HttpConnection();
            case "ftp":
                return new FtpConnection();
            default:
                throw new IllegalArgumentException("Unsupported protocol");
        }
    }
}
    

μ‚¬μš© 예:

public class Main {
    public static void main(String[] args) {
        Connection conn1 = ConnectionFactory.getConnection("http");
        Connection conn2 = ConnectionFactory.getConnection("ftp");

        conn1.connect(); // Connecting via HTTP
        conn2.connect(); // Connecting via FTP
    }
}
    

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

정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ°˜ν™˜ νƒ€μž…μœΌλ‘œ μ‚¬μš©ν•  수 μžˆμ–΄, λ°˜ν™˜ν•  ꡬ체적인 ν΄λž˜μŠ€κ°€ λ‚˜μ€‘μ— μΆ”κ°€λ˜λ”λΌλ„ μ½”λ“œ μˆ˜μ • 없이 μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜ˆμ‹œ:

public interface Logger {
    void log(String message);
}

public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("ConsoleLogger: " + message);
    }
}

public class FileLogger implements Logger {
    @Override
    public void log(String message) {
        // νŒŒμΌμ— 둜그λ₯Ό κΈ°λ‘ν•˜λŠ” μ½”λ“œ
        System.out.println("FileLogger: " + message);
    }
}

public class LoggerFactory {
    public static Logger getLogger(String type) {
        switch(type.toLowerCase()) {
            case "console":
                return new ConsoleLogger();
            case "file":
                return new FileLogger();
            default:
                throw new IllegalArgumentException("Unknown logger type");
        }
    }
}
    

μ‚¬μš© 예:

public class Main {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("console");
        logger.log("This is a log message."); // ConsoleLogger: This is a log message.
    }
}
    

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

1. 상속을 ν•˜λ €λ©΄ publicμ΄λ‚˜ protected μƒμ„±μžκ°€ ν•„μš”ν•˜λ‹ˆ 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ§Œ μ œκ³΅ν•˜λ©΄ ν•˜μœ„ 클래슀λ₯Ό λ§Œλ“€ 수 μ—†λ‹€

정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ§Œ μ œκ³΅ν•˜κ³  곡개 μƒμ„±μžκ°€ μ—†μœΌλ©΄, μ™ΈλΆ€μ—μ„œ 클래슀λ₯Ό 상속받아 ν™•μž₯ν•  수 μ—†μŠ΅λ‹ˆλ‹€. μ΄λŠ” 클래슀의 μœ μ—°μ„±μ„ μ œν•œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜ˆμ‹œ:

public class ImmutableClass {
    private final int value;

    private ImmutableClass(int value) {
        this.value = value;
    }

    public static ImmutableClass of(int value) {
        return new ImmutableClass(value);
    }

    public int getValue() {
        return value;
    }
}

// 상속 μ‹œλ„
public class ExtendedClass extends ImmutableClass {
    public ExtendedClass(int value) {
        super(value); // 였λ₯˜: ImmutableClass의 μƒμ„±μžκ°€ private이라 μ ‘κ·Ό λΆˆκ°€
    }
}
    

ν•΄κ²° λ°©μ•ˆ:

ν•˜μœ„ 클래슀λ₯Ό ν—ˆμš©ν•˜λ €λ©΄ μ μ ˆν•œ μ ‘κ·Ό μ œμ–΄μžλ₯Ό μ‚¬μš©ν•˜μ—¬ μƒμ„±μžλ₯Ό μ œκ³΅ν•΄μ•Ό ν•©λ‹ˆλ‹€. κ·ΈλŸ¬λ‚˜ μ΄λŠ” 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œμ˜ 이점을 일뢀 포기해야 함을 μ˜λ―Έν•©λ‹ˆλ‹€.

2. 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλŠ” ν”„λ‘œκ·Έλž˜λ¨Έκ°€ μ°ΎκΈ° μ–΄λ ΅λ‹€

정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλŠ” 클래슀의 API에 숨겨져 μžˆμ„ 수 있으며, 이름이 직관적이지 μ•ŠμœΌλ©΄ μ‚¬μš©μžκ°€ 이λ₯Ό μ°ΎκΈ° μ–΄λ €μšΈ 수 μžˆμŠ΅λ‹ˆλ‹€. 반면, μƒμ„±μžλŠ” 클래슀 이름과 λ™μΌν•œ 이름을 κ°€μ§€λ―€λ‘œ μ‰½κ²Œ 찾을 수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜ˆμ‹œ:

public class ComplexNumber {
    private final double real;
    private final double imaginary;

    private ComplexNumber(double real, double imaginary) {
        this.real = real;
        this.imaginary = imaginary;
    }

    // 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œμ˜ 예
    public static ComplexNumber ofCartesian(double real, double imaginary) {
        return new ComplexNumber(real, imaginary);
    }

    public static ComplexNumber ofPolar(double magnitude, double angle) {
        return new ComplexNumber(magnitude * Math.cos(angle), magnitude * Math.sin(angle));
    }
}

// μ‚¬μš©μžκ°€ 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ°ΎκΈ° μ–΄λ €μš΄ 경우
public class Main {
    public static void main(String[] args) {
        // ComplexNumber cn = new ComplexNumber(1, 2); // 였λ₯˜: μƒμ„±μžκ°€ private
        ComplexNumber cn1 = ComplexNumber.ofCartesian(1, 2);
        ComplexNumber cn2 = ComplexNumber.ofPolar(1, Math.PI / 2);

        System.out.println("Cartesian: " + cn1.real + " + " + cn1.imaginary + "i");
        System.out.println("Polar: " + cn2.real + " + " + cn2.imaginary + "i");
    }
}
    

ν•΄κ²° λ°©μ•ˆ:

정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œμ˜ 이름을 μ§κ΄€μ μœΌλ‘œ μ§€μ •ν•˜κ³ , 클래슀 λ¬Έμ„œμ— 이λ₯Ό λͺ…ν™•νžˆ λͺ…μ‹œν•˜μ—¬ ν”„λ‘œκ·Έλž˜λ¨Έκ°€ μ‰½κ²Œ 찾을 수 μžˆλ„λ‘ ν•©λ‹ˆλ‹€.

κ²°λ‘ 

정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλŠ” μƒμ„±μžμ— λΉ„ν•΄ μ—¬λŸ¬ 가지 μž₯점을 μ œκ³΅ν•˜μ§€λ§Œ, 일뢀 단점도 μ‘΄μž¬ν•©λ‹ˆλ‹€. 특히 상속과 API μ‚¬μš©μ˜ νŽΈμ˜μ„± μΈ‘λ©΄μ—μ„œ κ³ λ €ν•΄μ•Ό ν•  사항이 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ μž₯단점을 잘 μ΄ν•΄ν•˜κ³  상황에 맞게 μ‚¬μš©ν•˜λŠ” 것이 μ€‘μš”ν•©λ‹ˆλ‹€. μ•„λž˜λŠ” μœ„μ˜ κ°œλ…μ„ μ’…ν•©ν•œ κ°„λ‹¨ν•œ μ˜ˆμ œμž…λ‹ˆλ‹€.

μ’…ν•© μ˜ˆμ‹œ:

// μΈν„°νŽ˜μ΄μŠ€ μ •μ˜
public interface Animal {
    void speak();
}

// ꡬ체적인 ν΄λž˜μŠ€λ“€
public class Dog implements Animal {
    @Override
    public void speak() {
        System.out.println("Woof!");
    }
}

public class Cat implements Animal {
    @Override
    public void speak() {
        System.out.println("Meow!");
    }
}

// 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜λŠ” νŒ©ν† λ¦¬ 클래슀
public class AnimalFactory {
    public static Animal createAnimal(String type) {
        switch(type.toLowerCase()) {
            case "dog":
                return new Dog();
            case "cat":
                return new Cat();
            default:
                throw new IllegalArgumentException("Unknown animal type");
        }
    }
}

// μ‚¬μš© 예
public class Main {
    public static void main(String[] args) {
        Animal dog = AnimalFactory.createAnimal("dog");
        Animal cat = AnimalFactory.createAnimal("cat");

        dog.speak(); // Woof!
        cat.speak(); // Meow!
    }
}
    

이 μ˜ˆμ œμ—μ„œλŠ” AnimalFactory의 정적 νŒ©ν† λ¦¬ λ©”μ„œλ“œ createAnimal을 μ‚¬μš©ν•˜μ—¬ Animal μΈν„°νŽ˜μ΄μŠ€μ˜ λ‹€μ–‘ν•œ κ΅¬ν˜„μ²΄λ₯Ό μƒμ„±ν•©λ‹ˆλ‹€. 이λ₯Ό 톡해 μ½”λ“œμ˜ μœ μ—°μ„±κ³Ό 가독성을 높일 수 μžˆμŠ΅λ‹ˆλ‹€.

λŒ“κΈ€