본문으로 바로가기

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

category JAVA 2022. 7. 10. 02:26

클래스의 객체를 얻는 수단 중 보편적인 방법은 public 생성자를 이용하는 것이다.

하지만 더 좋은 수단이 하나 더 있다.

static method를 이용하는 것이다. 정적 팩터리 메서드를 사용한다는 것은 그 클래스의 인스턴스를 반환하는 메서드를 사용하는 것이다. Boolean.valueOf는 boolean 값을 받아 Boolean 객체를 반환해준다.

    public static Boolean valueOf(boolean c) {  
        return c ? Boolean.TRUE : Boolean.FALSE;  
    }

이 방식에는 public 생성자를 이용하는 방식보다 좋은 점 5가지가 있다.

첫번째, 이름을 가질 수 있다.

  • public 생성자만으로는 반환될 객체의 특성을 제대로 설명하기 힘들다.
    반면 정적 팩터리 메서드는 쉽게 묘사가 가능하다. 예컨대
    BigInteger(int, int, Random) 와 BigInteger.getPrime 중 '값이 소수인 BigInteger를 반환'
    의 의미를 더 잘 묘사하고 있는지 생각해보라.

두번째, 호출할때마다 새로운 인스턴스를 반환할 필요가 없다.

  • 불변 클래스와 같이 불필요한 객체 생성을 막을 수 있고 같은 객체가 자주 요청되는 상황에
    성능을 상당히 끌어올릴 수 있고 인스턴스의 생성주기 통제가 쉬워진다. 이런 클래스를
    인스턴스 통제(instance-controlled) 클래스라고 한다. 인스턴스를 통제한다는 것은 클래스를
    싱글톤으로 만들수도, 인스턴스화 불가로 만들수도, 불변클래스의 동치인 인스턴스가 단하나 임을
    보장할 수 있게된다는 것이다.

     public class StaticFactoryMethod {
           private static final StaticFactoryMethod STATIC_FACTORY_METHOD 
                                                       = new StaticFactoryMethod();
           private StaticFactoryMethod() {}
           static public StaticFactoryMethod getNewInstanceByNumber() {
               return STATIC_FACTORY_METHOD;
           }
     }
       public static void main(String args[]){
             StaticFactoryMethod staticFactoryMethod  = StaticFactoryMethod.getNewInstance();
             StaticFactoryMethod staticFactoryMethod2 = StaticFactoryMethod.getNewInstance();
    
             System.out.println("staticFactoryMethod == staticFactoryMethod2 :" + (staticFactoryMethod == staticFactoryMethod2) );
       }    

세번째, 반환 타입의 하위 타입객체를 반환할 수 있다.

  • 반환할 객체의 타입을 자유롭게 선택할 수 있는 유연성으로 구현할 클래스 공개없이
    그 객체를 반환할 수 있는 장점이 있다. 예컨대 자바 컬렉션프레임워크는 핵심 인터페이스들에
    수정 불가나 동기화 등의 기능을 덧붙여 유틸리티적인 구현체를 제공하는데 이 구현체 대부분을
    하나의 인스턴스화 불가 클래스인 java.util.Collections 에서 정적 팩터리 메서드를 통해 얻도록
    구현햿다.

         public abstract class StaticFactoryMethod {
    
             abstract void getName();
    
             static public StaticFactoryMethod getNewInstance( String code ) {
                 StaticFactoryMethod staticFactoryMethod = null;
                 if( code.indexOf("2") == 1 ) {
                     staticFactoryMethod = new Point();
                 } else {
                     staticFactoryMethod = new Coupon();
                 }
                 return staticFactoryMethod;
             }
         }
         class Coupon extends StaticFactoryMethod {
             public void getName() {
                 System.out.println("쿠폰을 발행합니다.");
             }
         }
    
         class Point extends StaticFactoryMethod {
             public void getName() {
                 System.out.println("포인트 1000점을 적립합니다.");
             }
         }
    
         public static void main(String args[]){
             StaticFactoryMethod staticFactoryMethod = StaticFactoryMethod.getNewInstance("223123");
             StaticFactoryMethod staticFactoryMethod1 = StaticFactoryMethod.getNewInstance("123123");
             staticFactoryMethod.getName();
             staticFactoryMethod1.getName();
         }

네번째, 입력 파라미터의 따라 다른 클래스의 객체를 반환 할 수 있다.

  • EnumSet 클래스는 public 생성자 없이 오직 정적 팩터리 메서드을 통해서만 객체를 반환한다.
    원소의 수에 따라 64개 이하면 RecularEnumSet의 객체, 65개 이상이면 JunboEnumSet의 객체를
    반환한다. 클라이언트는 이 객체를 알수도 알 필요도 없다.

         public abstract class StaticFactoryMethodType {
         public abstract void getName();
    
         static public StaticFactoryMethodType getNewInstance( String code ) {
             return new OneClass();
         }
         static public StaticFactoryMethodType getNewInstance( String code, String name ) {
             return new TwoClass();
         }
    
         }
    
         class OneClass extends StaticFactoryMethodType {
             public void getName() {
                 System.out.println("쿠폰을 발행합니다.");
             }
         }
    
         class TwoClass extends StaticFactoryMethodType {
             public void getName() {
                 System.out.println("포인트 1000점을 적립합니다.");
             }
         }
         public static void main(String args[]){
             StaticFactoryMethodType isOneObj = StaticFactoryMethodType.getNewInstance("code");
             StaticFactoryMethodType isTwoObj = StaticFactoryMethodType.getNewInstance("code","name");
             isOneObj.getName();
             isTwoObj.getName();
         }

    다섯번째, 정적 팩터리 메서드 작성 시점에서는 해당 클래스가 존재하지 않아도 된다.

  • 대표적으론 JDBC가 있다.

          public static void main(String[] args) {
           String url = "jdbc:oracle:thin:@localhost:1521:xe";
           String sql = "select * from employees";
           Connection conn = null;
           java.sql.Statement st = null; //DB와 소통하는 통로
    
           ResultSet rs = null; //결과 받아서 처리할때
           try {
               Class.forName("oracle.jdbc.driver.OracleDriver");
               System.out.println("driver load 성공!");
               conn = DriverManager.getConnection(url, "hr", "hr");
               System.out.println("DB 연결 성공!");
    
               st = conn.createStatement();
               rs = st.executeQuery(sql); //쿼리 실행 후 데이터들이 rs 저장
               while(rs.next()){ //한건씩 처리
                   int empid = rs.getInt(1); //첫번째 칼럼 조회
                   String fname = rs.getString("first_name"); //컬럼이름도 지정 가능
                   int sal = rs.getInt("salary");
                   Date hireDate =  rs.getDate("hire_date");
                   System.out.println(empid+ "\t" + fname + "\t" + sal + "\t" + hireDate);
               }
           } catch (ClassNotFoundException e) {
               System.out.println("driver load 실패!");
               e.printStackTrace();
           } catch (SQLException e) {
               System.out.println("DB 연결 실패!");
               e.printStackTrace();
           } finally {
               try {
                   if(rs != null) rs.close();
                   if(st != null) st.close();
                   if(conn != null) conn.close();
               } catch (SQLException e) {
                   e.printStackTrace();
               }
           }    
       }