본문 바로가기
Spring/Java

유틸리티 클래스 & 설계 방식

by 제우제우 2024. 8. 21.

Math 클래스

public final class Math {

    // Private constructor to prevent instantiation
    private Math() {}

    // Static utility methods
    public static int add(int a, int b) {
        return a + b;
    }

    public static double sqrt(double value) {
        return java.lang.Math.sqrt(value);
    }

    // More utility methods...
}
  • package java.lang;
  • Math 클래스는 final로 선언되어 있어 상속할 수 없다.
  • 생성자가 private로 선언되어 있어, 외부에서 인스턴스화를 방지
  • add나 sqrt 같은 메서드들은 정적 메서드로 선언되어, Math.add(5, 10)과 같은 방식으로 호출

StringUtils 클래스 

/**
 * Utility class for working with Strings.
 */
public abstract class StringUtils {

    // Private constructor to prevent instantiation
    private StringUtils() {}

    /**
     * Check whether the given String is empty (""), null or only whitespace.
     * 
     * @param str the String to check
     * @return {@code true} if the String is empty or {@code null}, otherwise {@code false}
     */
    public static boolean isEmpty(CharSequence str) {
        return (str == null || str.length() == 0);
    }

    /**
     * Check whether the given String has text (i.e. not null, not empty, and not only whitespace).
     * 
     * @param str the String to check
     * @return {@code true} if the String has text, otherwise {@code false}
     */
    public static boolean hasText(CharSequence str) {
        if (str instanceof String) {
            return hasText((String) str);
        }
        return (str.length() > 0);
    }

    private static boolean hasText(String str) {
        int strLen = str.length();
        if (strLen == 0) {
            return false;
        }
        for (int i = 0; i < strLen; i++) {
            if (!Character.isWhitespace(str.charAt(i))) {
                return true;
            }
        }
        return false;
    }
	// 생략 .. 
}
  • package org.springframework.util;
  • 여러 유틸리티 메서드들을 정적(static)으로 제공
  • 인스턴스화되거나 상속될 필요가 없으므로, final로 선언되고 private 생성자를 갖는다.

유틸리티 클래스 설계

static 메서드 & static 필드를  모아둔 클래스를 만든 경우 해당 클래스를 abstract로 만들어도 인스턴스를 만드는 걸 막을 순 없다. 상속 받아서 인스턴스를 만들 수 있기 때문이다.

그러면 어떻게 해야 인스턴스화를 막을까? 

2가지 방법이 있다. 

 

1. abstract + private 생성자 

public abstract class Utility {
    private Utility() {
    }
    static int plus(int a, int b){
        return a + b;
    }
}

public class UtilityChild extends Utility{
    public UtilityChild() {
        super(); // 컴파일 에러 -> 자식 클래스 상속 불가능
    }
}
  • 자바에서 상속을 받으면 부모 생성자를 호출해야한다. 
  • 하지만 부모의 생성자가 private이면 생성자 호출을 불가능하다.

2.  final + private 생성자 

public final class Utility {
    private Utility() {
    }
    static int plus(int a, int b){
        return a + b;
    }
}
  • fianl 키워드로 상속 자체를 막는다. 
  • privae 생성자를 통해서 자체 인스턴스화를 막는다. 

 

어떤 방식이 더 좋을까?

 

내 생각은 fianl + private 방식이 더 괜찮다고 생각한다.

final 키워드, private 생성자는 클래스의 상속과 인스턴스화를 모두 명확하게 방지

즉 설계 의도가 명확해지고, 다른 개발자들이 클래스를 사용하는 데 혼란이 없다.