팩토리 메소드는 생성적 디자인 패턴 중 하나로서, 부모 클래스에서 객체를 생성하는 인터페이스를 제공한다. 자식 클래스들은 객체를 생성할 때 형을 변환할 수 있다.
물류 관리 애플리케이션을 만든다고 생각해보자. 앱의 첫 버전은 오직 트럭 운송을 다루기 때문에, 앱의 코드는 Truck 클래스 속에 있다.
얼마 후, 이 앱이 유명해져서 해상 운송 회사로부터 해상 운송도 지원해달라는 요청이 쏟아지고 있다.
이미 Truck 클래스와 많이 엮여있는 코드에 Ships를 추가하여 변경하는 것은 쉽지 않을 것이다.
확장성이 떨어지는 코드에 문제가 있는 것이다.
이 때, 팩토리 메소드 디자인 패턴은 new를 통해 객체를 생성하는 부분을 특별한 팩토리 메소드를 호출하는 것으로 대체할 것을 제안한다. 즉, 팩토리 메소드 안에서 new를 통해 객체가 생성되는 것이다. 팩토리 메소드를 통해 생성되는 객체들은 product로 불린다.
효과 : 팩토리 메소드(createTransport)를 통해 자식 클래스에서 만들어지는 객체의 타입을 변경할 수 있다!
Logistics l=new RoadLogistics(); or new SeaLogistics();
Transtport t=l.createTransport();
t.deliver()
팩토리 메소드를 통해 만들어지는 Product 부분. 팩토리메소드를 통해 생성된 모든 객체들(Concrete Product들)에게 적용되는 특징을 고려하여 Product 인터페이스를 정의함.
Concrete Product들은 Product의 인터페이스를 각자에 맞게 구현함.
Creator 클래스에서는 Product object를 생성하는 팩토리 메소드를 선언한다. 메소드의 반환 타입이 product와 매치되는 것이 중요하다.
팩토리 메소드를 추상화하여 Creator의 자식클래스들이 각자의 버전으로 메소드를 구현하도록 강제하는 것도 가능하다. 아니면 base 팩토리 메소드가 디폴트 버전을 리턴하는 것도 하나의 방법이다.
Creator 이름을 보고 오해하지 말아야할 것은, Product를 생성하는 것이 Creator의 주기능이 아니라는 것이다. 통상적으로, Creator 클래스는 이미 기존 Product와 관련된 핵심 비즈니스 로직을 가지고 있다. 팩토리 메소드는 이 로직을 Concrete Product로부터 떼어내는 것을 도와준다.
Concrete Creators는 Creator 클래스의 base 팩토리 메소드를 상속받아 다른 타입의 product를 반환한다.
매번 비즈니스 로직을 다시 짜는 것 대신 기존에 존재하는 object들을 재사용함으로써 시스템 리소스들을 아끼고 싶을 때
기존 객체를 재사용하고 싶을 때 거쳐야 하는 과정
1. 생성된 모든 객체들을 추적하고 있는 저장공간 생성
2. 누가 객체를 요청했을 때, 프로그램은 pool에서 free object를 찾음.
3. return
4. 없으면 new를 통해 생성 or pool에 등록
해당 로직은 클래스의 생성자 부분에 넣기 적합하나, 생성자들은 무조건 new objects들을 return 해야함. 기존 존재하는 객체를 반환할 수 없음.
따라서, 팩토리 메소드를 통해 생성/재사용을 둘다 할 수 있음.
코드와 연관이 깊은 객체의 정확한 타입과 연관관계, 의존관계를 사전에 모르는 경우
라이브러리 또는 프레임워크의 사용자에게 내부 구성요소를 확장하는 방법을 제공하고 싶을 때
Shape.java
public interface Shape{
void draw();
}
Circle.java
public class Circle implements Shape{
@Override
public void draw(){
System.out.println("Inside Circle::draw() method.");
}
}
Rectangle.java
public class Circle implements Shape{
@Override
public void draw(){
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Circle implements Shape{
@Override
public void draw(){
System.out.println("Inside Square::draw() method.");
}
}
ShapeFactory.java
public class ShapeFactory{
public Shape getShape(String shapeType){
if(shapeType==null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
FactoryPatternDemo.java
public class FactoryPatternDemo{
public static void main(String[] args){
ShapeFactory shapeFactory=new ShapeFactory();
Shape shape1=shapeFactory.getShape("CIRCLE");
shape1.draw();
Shape shape2=shapeFactory.getShape("RECTANGLE");
shape1.draw();
Shape shape3=shapeFactory.getShape("SQUAREa");
shape1.draw();
}
}