The Factory Pattern
팩토리 패턴은 객체를 생성하는 개념과 관련된 또 다른 생성 패턴입니다. 해당 카테고리의 다른 패턴과 다른 점은 명시 적으로 생성자를 사용해야한다는 것입니다. 대신, 팩토리는 객체 생성을위한 일반 인터페이스를 제공 할 수 있습니다. 여기에서 우리는 생성하고자하는 팩토리 객체의 유형을 지정할 수 있습니다.
UI 구성 요소 유형을 생성하라는 UI 팩토리가 있다고 가정 해보십시오. new
연산자를 사용하거나 다른 생성자를 통해이 컴포넌트를 직접 생성하는 대신 Factory 객체에 대신 새 컴포넌트를 요청합니다. 어떤 유형의 객체가 필요한지 (예 : "Button", "Panel") Factory에 알리고이를 인스턴스화하여 사용하기 위해 반환합니다.
객체 생성 프로세스가 상대적으로 복잡하다면 특히 유용합니다. 동적 요소 또는 응용 프로그램 구성에 크게 의존하는 경우
이 패턴의 예는 ExtJS와 같은 UI 라이브러리에서 찾을 수 있습니다. 여기에서 객체 또는 구성 요소를 만드는 메소드는 하위 클래스로 추가 될 수 있습니다.
다음은 생성자 패턴 로직을 사용하여 자동차를 정의하는 이전 스 니펫을 기반으로 작성된 예제입니다. 팩토리 패턴을 사용하여 자동차 공장을 구현하는 방법을 보여줍니다.
// Types.js - Constructors used behind the scenes
// A constructor for defining new cars
function Car( options ) {
// some defaults
this.doors = options.doors || 4;
this.state = options.state || "brand new";
this.color = options.color || "silver";
}
// A constructor for defining new trucks
function Truck( options){
this.state = options.state || "used";
this.wheelSize = options.wheelSize || "large";
this.color = options.color || "blue";
}
// FactoryExample.js
// Define a skeleton vehicle factory
function VehicleFactory() {}
// Define the prototypes and utilities for this factory
// Our default vehicleClass is Car
VehicleFactory.prototype.vehicleClass = Car;
// Our Factory method for creating new Vehicle instances
VehicleFactory.prototype.createVehicle = function ( options ) {
switch(options.vehicleType){
case "car":
this.vehicleClass = Car;
break;
case "truck":
this.vehicleClass = Truck;
break;
//defaults to VehicleFactory.prototype.vehicleClass (Car)
}
return new this.vehicleClass( options );
};
// Create an instance of our factory that makes cars
var carFactory = new VehicleFactory();
var car = carFactory.createVehicle( {
vehicleType: "car",
color: "yellow",
doors: 6 } );
// Test to confirm our car was created using the vehicleClass/prototype Car
// Outputs: true
console.log( car instanceof Car );
// Outputs: Car object of color "yellow", doors: 6 in a "brand new" state
console.log( car );
접근법 # 1 : Truck 클래스를 사용하도록 VehicleFactory 인스턴스 수정
var movingTruck = carFactory.createVehicle( {
vehicleType: "truck",
state: "like new",
color: "red",
wheelSize: "small" } );
// Test to confirm our truck was created with the vehicleClass/prototype Truck
// Outputs: true
console.log( movingTruck instanceof Truck );
// Outputs: Truck object of color "red", a "like new" state
// and a "small" wheelSize
console.log( movingTruck );
접근법 # 2 : Trucks를 빌드하는 팩토리 클래스를 작성하는 VehicleFactory 서브 클래스
function TruckFactory () {}
TruckFactory.prototype = new VehicleFactory();
TruckFactory.prototype.vehicleClass = Truck;
var truckFactory = new TruckFactory();
var myBigTruck = truckFactory.createVehicle( {
state: "omg..so bad.",
color: "pink",
wheelSize: "so big" } );
// Confirms that myBigTruck was created with the prototype Truck
// Outputs: true
console.log( myBigTruck instanceof Truck );
// Outputs: Truck object with the color "pink", wheelSize "so big"
// and state "omg. so bad"
console.log( myBigTruck );
언제 Factory 패턴을 사용할까?
Factory 패턴은 다음 상황에 적용 할 때 특히 유용 할 수 있습니다.
- 객체 또는 구성 요소 설정에 높은 수준의 복잡성이 포함되는 경우
- 우리가있는 환경에 따라 다른 객체 인스턴스를 쉽게 생성해야 할 때
- 동일한 속성을 공유하는 많은 작은 객체 또는 구성 요소로 작업 할 때
- API 계약 (일명 오리 유형) 만 충족 시켜야만 다른 객체의 인스턴스로 객체를 작성할 때 작동합니다. 이는 디커플링에 유용합니다.
언제 Factory 패턴을 사용하면 안될까?
잘못된 유형의 문제에 적용될 때이 패턴은 응용 프로그램에 불필요하게 많은 복잡성을 초래할 수 있습니다. 객체 생성을위한 인터페이스를 제공하는 것이 우리가 작성하는 라이브러리 또는 프레임 워크의 디자인 목표가 아니라면 불필요한 오버 헤드를 피하기 위해 명시 적 생성자를 사용하는 것이 좋습니다.
객체 생성 프로세스가 인터페이스 뒤에 효과적으로 추상화되어 있기 때문에이 프로세스가 얼마나 복잡한 지에 따라 단위 테스트에 문제가 발생할 수 있습니다.
Abstract Factories
공통된 목표를 가진 개별 공장 그룹을 요약하는 것을 목표로하는 추상 팩토리 패턴을 알고있는 것도 유용합니다. 객체 집합의 구현 세부 정보를 일반적인 용도와 구분합니다.
추상 팩토리는 생성 된 객체가 생성되는 방식이나 여러 유형의 객체로 작업해야하는 방식에서 시스템이 독립적이어야하는 곳에서 사용해야합니다.
간단하고 이해하기 쉬운 예제는 차량 유형을 얻거나 등록하는 방법을 정의하는 차량 공장입니다. abstract 팩토리의 이름은 abstractVehicleFactory가 될 수 있습니다. 초록 공장에서는 "자동차"또는 "트럭"과 같은 차량 유형을 정의 할 수 있으며 콘크리트 공장에서는 차량 계약 (예 : Vehicle.prototype.drive
및 Vehicle.prototype.breakDown
)을 충족하는 클래스 만 구현합니다.
var abstractVehicleFactory = (function () {
// Storage for our vehicle types
var types = {};
return {
getVehicle: function ( type, customizations ) {
var Vehicle = types[type];
return (Vehicle ? new Vehicle(customizations) : null);
},
registerVehicle: function ( type, Vehicle ) {
var proto = Vehicle.prototype;
// only register classes that fulfill the vehicle contract
if ( proto.drive && proto.breakDown ) {
types[type] = Vehicle;
}
return abstractVehicleFactory;
}
};
})();
// Usage:
abstractVehicleFactory.registerVehicle( "car", Car );
abstractVehicleFactory.registerVehicle( "truck", Truck );
// Instantiate a new car based on the abstract vehicle type
var car = abstractVehicleFactory.getVehicle( "car", {
color: "lime green",
state: "like new" } );
// Instantiate a new truck in a similar manner
var truck = abstractVehicleFactory.getVehicle( "truck", {
wheelSize: "medium",
color: "neon yellow" } );