객체를 만들 때 new를 직접 쓰면 어떤 문제가 생길까. 코드 곳곳에 new Dog(), new Cat(), new Bird()가 흩어져 있다고 생각해보자. 나중에 new Animal()로 바꿔야 한다면? 모든 new를 찾아다녀야 한다.
Factory Method는 객체 생성을 한 곳에 모아두는 패턴이다.
객체를 생성하는 코드를 별도의 메서드(factory method)로 분리하고, 어떤 클래스의 인스턴스를 만들지는 서브클래스가 결정하게 하는 패턴이다.
생성 로직이 바뀌어도 호출하는 쪽 코드는 손댈 필요가 없다.

// 공통 인터페이스
public interface Notification {
void send(String message);
}
// 구체 구현체들
public class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("[Email] " + message);
}
}
public class SmsNotification implements Notification {
@Override
public void send(String message) {
System.out.println("[SMS] " + message);
}
}
// 팩토리 — 생성 로직을 여기에 모음
public class NotificationFactory {
public static Notification create(String type) {
switch (type) {
case "email": return new EmailNotification();
case "sms": return new SmsNotification();
default: throw new IllegalArgumentException("알 수 없는 타입: " + type);
}
}
}
// 사용하는 쪽 — new를 직접 쓰지 않음
Notification noti = NotificationFactory.create("email");
noti.send("회원가입을 환영합니다.");
나중에 KakaoNotification을 추가할 때 팩토리 안에만 case "kakao":를 추가하면 된다. 호출하는 쪽은 건드리지 않아도 된다.
위 예제는 static 팩토리 메서드 방식이다. 패턴의 원형은 추상 클래스와 서브클래스를 활용한다.
// 추상 Creator
public abstract class NotificationSender {
// 팩토리 메서드 — 서브클래스가 구현
protected abstract Notification createNotification();
// 공통 로직은 여기서 처리
public void notify(String message) {
Notification noti = createNotification();
noti.send(message);
}
}
// 구체 Creator
public class EmailSender extends NotificationSender {
@Override
protected Notification createNotification() {
return new EmailNotification();
}
}
public class SmsSender extends NotificationSender {
@Override
protected Notification createNotification() {
return new SmsNotification();
}
}
NotificationSender sender = new EmailSender();
sender.notify("비밀번호가 변경되었습니다.");
notify() 메서드는 어떤 Notification이 만들어지는지 신경 쓰지 않는다. 서브클래스가 결정한 객체를 그냥 쓸 뿐이다.

new를 직접 쓰는 코드가 여러 곳에 퍼지기 시작하면 Factory Method를 고민해볼 시점이다. 생성 책임을 한 곳으로 모으는 것, 그게 이 패턴이 하는 일이다.