이 시점에서 이미 디자인 패턴을 접했을 것입니다. 예를 들어, 싱글톤 .

패턴이 무엇인지, 패턴이 왜 필요한지, 생성 패턴이 무엇인지 생각해 봅시다(싱글톤이 그 예입니다). 우리는 또한 새로운 패턴인 팩토리 방식을 연구할 것입니다.

소프트웨어 개발에서 디자인 패턴은 일부 반복되는 컨텍스트 내에서 디자인 문제에 대한 솔루션을 나타내는 반복 가능한 아키텍처 구조입니다.

일반적으로 패턴은 코드로 직접 변환할 수 있는 최종 솔루션이 아닙니다. 다양한 상황에서 사용할 수 있는 문제에 대한 모델 솔루션일 뿐입니다.

생성 패턴은 객체 생성 프로세스를 다루는 디자인 패턴입니다. 개체를 생성, 구성 및 표시하는 데 사용되는 방법과 독립적인 시스템을 생성할 수 있습니다.

팩토리 메서드는 부모 클래스에서 객체를 생성하기 위한 공통 인터페이스를 정의하는 생성 디자인 패턴으로, 자손에게 이러한 객체를 생성할 수 있는 기능을 제공합니다. 생성 시 자손은 생성할 클래스를 결정할 수 있습니다.

패턴은 어떤 문제를 해결합니까?

배달 프로그램을 만들기로 결정했다고 상상해 보십시오. 처음에는 자동차로 택배를 고용하고자동차프로그램에서 배달 차량을 나타내는 개체입니다. 택배는 A지점에서 B지점으로 소포를 배달합니다. 쉬워요.

프로그램이 인기를 얻고 있습니다. 귀하의 비즈니스는 성장하고 있으며 새로운 시장으로 확장하기를 원합니다. 예를 들어 음식 배달과 화물 운송도 시작할 수 있습니다. 이 경우 음식은 도보, 스쿠터, 자전거로 택배로 배달할 수 있지만 화물에는 트럭이 필요합니다.

이제 각 운송업체가 운송할 수 있는 양을 포함하여 몇 가지 사항(언제, 누구에게, 무엇을, 얼마나 배달할지)을 추적해야 합니다. 새로운 운송 수단은 속도와 용량이 다릅니다. 그런 다음 프로그램의 대부분의 엔터티가자동차수업. 프로그램이 다른 전달 방법과 함께 작동하도록 하려면 기존 코드 베이스를 다시 작성하고 새 차량을 추가할 때마다 다시 작성해야 한다는 것을 알고 있습니다.

결과는 운송 유형에 따라 다른 작업을 수행하는 조건문으로 채워진 끔찍한 코드입니다.

해결책

팩토리 메소드 패턴은 new 연산자 를 직접 사용하는 대신 특별한 팩토리 메소드를 호출하여 객체를 생성하도록 제안합니다 . 팩토리 메서드가 있는 클래스의 서브클래스는 특정 차량의 생성된 객체를 수정할 수 있습니다. 언뜻 보면 이것은 무의미해 보일 수 있습니다. 생성자 호출을 프로그램의 한 위치에서 다른 위치로 이동했을 뿐입니다. 그러나 이제 하위 클래스의 팩토리 메서드를 재정의하여 생성 중인 운송 수단의 유형을 변경할 수 있습니다.

이 접근 방식에 대한 클래스 다이어그램을 살펴보겠습니다.

이 시스템이 작동하려면 반환된 모든 개체에 공통 인터페이스가 있어야 합니다. 하위 클래스는 이 인터페이스를 구현하는 다른 클래스의 개체를 생성할 수 있습니다.

예를 들어 TruckCar 클래스는 전달 메서드를 사용하여 CourierTransport 인터페이스를 구현합니다 . 이러한 각 클래스는 다른 방식으로 메서드를 구현합니다. 트럭은 화물을 배달하고 자동차는 음식, 패키지 등을 배달합니다. TruckCreator 클래스 의 팩토리 메서드는 트럭 개체를 반환하고 CarCreator 클래스는 자동차 개체를 반환합니다.

팩토리 메소드의 클라이언트의 경우 이러한 객체 간에 차이가 없습니다. 일종의 추상 CourierTransport 로 취급하기 때문입니다 . 클라이언트는 개체에 전달 방법이 있는지 깊이 관심을 가지지만 해당 방법이 정확히 어떻게 작동하는지는 중요하지 않습니다.

자바 구현:


public interface CourierTransport {
	void deliver();
}
public class Car implements CourierTransport {
	@Override
	public void deliver() {
    		System.out.println("The package is being delivered by car");
	}
}
public class Truck implements CourierTransport {
	@Override
	public void deliver() {
    		System.out.println("The freight is being delivered by truck");
	}
}
public abstract class CourierTransportCreator {
	public abstract CourierTransport createTransport();
}
public class CarCreator extends CourierTransportCreator {
	@Override
	public CourierTransport createTransport() {
    		return new Car();
	}
}
public class TruckCreator extends CourierTransportCreator {
	@Override
	public CourierTransport createTransport() {
    		return new Truck();
	}
}
 
public class Delivery {
	private String address;
	private CourierTransport courierTransport;
 
	public void Delivery() {
	}
 
	public Delivery(String address, CourierTransport courierTransport) {
    	this.address = address;
    	this.courierTransport = courierTransport;
	}
 
	public CourierTransport getCourierTransport() {
    		return courierTransport;
	}
 
	public void setCourierTransport(CourierTransport courierTransport) {
    		this.courierTransport = courierTransport;
	}
 
	public String getAddress() {
    		return address;
	}
 
	public void setAddress(String address) {
    		this.address = address;
	}
}
public static void main(String[] args) {
    	// Accept a new type of order from the database (pseudocode)
    	String type = database.getTypeOfDeliver();
 
    	Delivery delivery = new Delivery();
    	
    	// Set the transport for delivery
        delivery.setCourierTransport(getCourierTransportByType(type));
    	
    	// Make the delivery
        delivery.getCourierTransport().deliver();
 
	}
 
	public static CourierTransport getCourierTransportByType(String type) {
    	switch (type) {
        	case "CarDelivery":
            	return new CarCreator().createTransport();
        	case "TruckDelivery":
            	return new TruckCreator().createTransport();
        	default:
            	throw new RuntimeException();
	    }
	}
    

새 배달 개체를 만들려면 프로그램에서 자동으로 적절한 전송 개체를 만듭니다.

이 패턴을 언제 적용해야 합니까?

1. 코드에서 작업해야 하는 개체의 유형과 종속성을 미리 알지 못하는 경우.

팩토리 방식은 운송 형태를 생산하는 코드와 운송 수단을 사용하는 코드를 분리합니다. 그 결과 나머지 코드를 건드리지 않고도 개체를 생성하는 코드를 확장할 수 있습니다.

예를 들어 새로운 유형의 운송에 대한 지원을 추가하려면 새 하위 클래스를 만들고 새 운송의 인스턴스를 반환하는 팩터리 메서드를 정의해야 합니다.

2. 새로운 객체를 생성하는 대신 기존 객체를 재사용하여 시스템 자원을 절약하고 싶을 때.

이 문제는 일반적으로 데이터베이스 연결, 파일 시스템 등과 같이 리소스를 많이 사용하는 개체로 작업할 때 발생합니다.

기존 개체를 재사용하기 위해 수행해야 하는 단계를 생각해 보십시오.

  1. 먼저 생성한 모든 개체를 저장할 공유 리포지토리를 생성해야 합니다.

  2. 새 개체를 요청할 때 저장소를 살펴보고 사용 가능한 개체가 포함되어 있는지 확인해야 합니다.

  3. 개체를 클라이언트 코드로 반환합니다.

  4. 그러나 사용 가능한 개체가 없으면 새 개체를 만들어 리포지토리에 추가합니다.

이 모든 코드는 클라이언트 코드를 어지럽히지 않는 곳에 배치해야 합니다. 객체를 생성할 때 이러한 모든 검사만 필요하기 때문에 가장 편리한 위치는 생성자일 것입니다. 아아, 생성자는 항상 새 개체를 만듭니다. 기존 개체를 반환할 수 없습니다.

즉, 기존 개체와 새 개체를 모두 반환할 수 있는 다른 메서드가 필요합니다. 이것은 공장 방법이 될 것입니다.

3. 사용자가 프레임워크 또는 라이브러리의 일부를 확장하도록 허용하려는 경우.

사용자는 상속을 통해 프레임워크 클래스를 확장할 수 있습니다. 그러나 프레임워크가 표준 클래스가 아닌 이러한 새 클래스의 객체를 생성하도록 하려면 어떻게 해야 합니까?

솔루션은 사용자가 구성 요소뿐만 아니라 해당 구성 요소를 생성하는 클래스도 확장할 수 있도록 하는 것입니다. 그리고 이를 위해 생성 클래스에는 정의할 수 있는 특정 생성 방법이 있어야 합니다.

장점

  • 특정 운송 클래스에서 클래스를 분리합니다.
  • 교통 수단을 만드는 코드를 한 곳에 보관하여 코드를 유지 관리하기 쉽게 만듭니다.
  • 프로그램에 새로운 교통 수단 추가를 단순화합니다.
  • 개방형 폐쇄 원칙을 구현합니다.

단점

각 제품 클래스에는 고유한 생성자 하위 클래스가 있어야 하므로 대규모 병렬 클래스 계층으로 이어질 수 있습니다.

요약하자

팩토리 메서드 패턴에 대해 배웠고 한 가지 가능한 구현을 보았습니다. 이 패턴은 다른 개체를 만들기 위한 개체를 제공하는 다양한 라이브러리에서 자주 사용됩니다.

기본 비즈니스 논리와 상호 작용하고 다른 컨텍스트로 인해 코드를 팽창시키지 않기 위해 기존 클래스의 하위 클래스에 새 개체를 쉽게 추가하려는 경우 팩토리 메서드 패턴을 사용합니다.