NMP-Study / EffectiveJava2018

Effective Java Study
9 stars 0 forks source link

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

Closed madplay closed 5 years ago

madplay commented 5 years ago

장점

  1. 이름을 가질 수 있다.

    new Contact("xenos", "010-0000-0000");
    new Contact("xenos", "friend");

    vs.

    Contact.makeWithTelephone("xenos", "010-0000-0000");
    Contact.makeWithRelation("xenos", "friend");
  2. 호출될 때마다 인스턴스를 새로 생성하지 않아도 된다.

    public class Something {
    List<String> someList;
    Something(String prefix) {
      someList = new ArrayList<>();
      for(int i=0; i < 9999999999; i++) {
         someList.add(Sring.format("%s_%d", prefix,i));
      }
    }
    
    private static Map<String, Something> instanceMap = new HashMap<String, Something>();
    public static makeSomething(String prefix) {
      Something s = instanceMap.get(prefix);
      if(s == null) {
         s = new Something(prefix);
         instnaceMap.put(prefix, s);
      }
      return s;
    }
    }
    • immutable class(#17) 을 사용할때 좋음
  3. 반환 타입의 하위 타입 객체를 반환할 수 있는 능력이 있다.

    
    public interface Car {
    void start();
    
    static Car makeElectricCar(int motorNumber) {
        return new ElectricCar() {
            @Override
            int getMotorNumber() {
                return motorNumber;
            }
        };
    }
    }

abstract class ElectricCar implements Car { @Override public void start() { System.out.println(getMotorNumber() + "개의 모터로 달립니다. 우애앵~"); }

abstract int getMotorNumber();

}

class TheElectricCar extends ElectricCar { private motorNumber;

TheElectricCar(int motorNumber) {
    this.motorNumber = motorNumber;
}

@Override
int getMotorNumber() {
    return this.motorNumber;
}

}

* java8 부터 정적메서드를 interface에 생성할 수 있다.
* 사용자는 하위 타입이 뭐가 오든 상관없이 사용할 수 있는것이 장점
```java
Car c = new TheElectricCar(2);
vs.
Car c = Car.makeElectricCar(2);
  1. 입력 매개변수에 따라 매번 다른 클래스의 객체를 반환할 수 있다.

    
    class SuperElectricCar extends ElectricCar {
    private int motorNumber;
    
    SuperElectricCar(int motorNumber) {
        this.motorNumber = motorNumber;
    }
    
    @Override
    public void start() {
        System.out.println(getMotorNumber() + "개의 모터로 달립니다. 우와아아아아아앙~");
    }
    
    @Override
    int getMotorNumber() {
        return this.motorNumber;
    }
    }

public interface Car { void start();

static Car makeElectricCar(int motorNumber) {
    if(motorNumber < 4) {
        return new TheElectricCar(motorNumber);
    } else {
        return new SuperElectricCar(motorNumber);
    }
}

}


5. 정적 팩터리 메서드를 작성하는 시점에는 반환할 객체의 클래스가 존재하지 않아도 된다.
```java
public class CarRegister {
    private static Map<String, Car> carMap;

    public static void regist(String type, Car car) {
        carMap.put(type, car);
    }
    public static Car get(String type) {
        return carMap.get(type);
    }
}

public interface Car {
    void start();

    static Car getCar(String type) {
        return CarRegister.getCar(type);
    }
}

단점

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

    굳이 상속이 필요하다면, 생성자 만들어 주면 안됨?

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

    문서화 잘 안해놓으면 생성자도 마찬가지 아님?

    • 자주 사용하는 정적 메서드 이름
      • from: 하나의 매개변수로 해당 타입의 인스턴스를 반환 - Date.from(instnace);
      • of : 여러개의 매개변수로 적합한 인스턴스를 반환 - EnumSet.of(A,B,C);
      • valueOf : from 과 of의 더 자세한 버젼? - BigInteger.valueOf(Integer.MAX_VALUE);
      • instance or getInstance: 매개변수로 명시한 인스턴스를 반환하지만, 같은 인스턴스임을 보장하지 않는다. - StackWalker.getInstacne(options);
      • create or newInstance : 매번 새로운 인스턴를 반환하는것을 보장 Array.newInstance(callObject, arrayList);
      • getType : getInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. - Files.getFileStore(path);
      • newType : newInstance와 같으나, 생성할 클래스가 아닌 다른 클래스에 팩터리 메서드를 정의할 때 쓴다. - Files.newBufferedRader(path);
      • type : getType과 newType의 간결한 버젼 - Collections.list(someList);