# 工厂模式

## 工厂模式

> Factory pattern

### 知识点一：定义

定义一个工厂方法（生产者）返回一个实例对象（产品），最终让一个子类（实际生产者）去完成实例化（生产产品）。工厂模式甚至允许不使用一个构造函数，就可以提供一个通用接口来创建对象。

### 知识点二：具体例子

![image](/files/-LRk4VPN0biOijlspRJ_)

```javascript
//定义工厂
function Factory() {
    this.createEmployee = function (type) {
        var employee;

        if (type === "fulltime") {
            employee = new FullTime();
        } else if (type === "parttime") {
            employee = new PartTime();
        } else if (type === "temporary") {
            employee = new Temporary();
        } else if (type === "contractor") {
            employee = new Contractor();
        }

        employee.type = type;

        employee.say = function () {
            log.add(this.type + ": rate " + this.hourly + "/hour");
        }

        //生成ConcreteProduct实例
        return employee;
    }
}

//定义ConcreteProduct产品类
var FullTime = function () {
    this.hourly = "$12";
};

var PartTime = function () {
    this.hourly = "$11";
};

var Temporary = function () {
    this.hourly = "$10";
};

var Contractor = function () {
    this.hourly = "$15";
};

// log helper
var log = (function () {
    var log = "";

    return {
        add: function (msg) { log += msg + "\n"; },
        show: function () { alert(log); log = ""; }
    }
})();

function run() {
    var employees = [];
    var factory = new Factory();

    employees.push(factory.createEmployee("fulltime"));
    employees.push(factory.createEmployee("parttime"));
    employees.push(factory.createEmployee("temporary"));
    employees.push(factory.createEmployee("contractor"));

    for (var i = 0, len = employees.length; i < len; i++) {
        employees[i].say();
    }

    log.show();
}
```

#### 例子分析

***Creator*** 工厂，例子中的Factory 1. 此工厂实例可以生成不同种类的产品 1. 实现了一个工厂方法(factoryMethod)返回产品

***AbstractProduct*** 抽象产品，例子中没使用 1. 声明产品的统一接口

***ConcreteProduct*** 具体产品，JavaScript没有此概念 1. 被生产出来的产品 1. 所有的产品都会含有统一的接口(相同的属性或者方法)

假设有一个工厂，可以生成不同种类的工人。每一个工人有不同的时薪。createEmployee方法就是一个工厂方法。试想象一些客户端就是客户，按照不同的需要添加参数，通过调用createEmployee进行下单。工厂就会内部去实现创造一个工人实例出来。需要注意的是，由于JavaScript不支持抽象类概念，这些FullTime、PartTime等子类需要一个统一的接口（属性或者方法，例如本事例的this.hourly）。当不同种类的工人创建出来，工厂还会统一实现一些方法，例如（employee.type、employee.say）。

### 知识点三：特点

对以下使用场景特别有用&优点： 1. 当对象或组件设置比较复杂 1. 需要根据不同的环境生产不同的实例 1. 需要处理很多具有相同属性的小型对象或组件

什么情况下不应该使用&缺点： 1. 如果要实现的具体产品不具有统一的接口，使用此模式会带来很多不必要的复杂性。建议使用显式构造函数，减少不必要开销。 1. 由于对象创建的过程隐藏在接口之后抽象出来，单元测试可能会带来不便，这取决于创建过程的复杂性

## 抽象工厂（Abstract Factory）模式

### 知识点一：定义

提供一个接口（抽象工厂类）用来新建一些相似的有关联的或者互相依赖的对象（产品族），而不是通过一个明确指定的具体类

### 知识点二：具体例子

```javascript
// Types.js - Constructors used behind the scenes

// A constructor for defining new cars
class Vehicle {
  constructor(){
    this.steering = 'left'
  }
}

class Car extends Vehicle{
 constructor(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
class Truck extends Vehicle{
  constructor(options){
      this.state = options.state || "used";
      this.wheelSize = options.wheelSize || "large";
      this.color = options.color || "blue";
  }
}

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" } );
```

#### 例子分析

***AbstractFactory*** 抽象工厂：abstractVehicleFactory 1. 声明一个接口用于生产产品

***concreteFactory*** 具体工厂：registerVehicle方法 1. 根据条件生产产品

***Products*** 产品类：Car、Truck 1. 提供要生产产品的接口给工厂

***AbstractProduct*** 抽象产品类：Vehicle 1. 为产品族声明接口


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://wikinote.gitbook.io/js-pattern/javascript-she-ji-mo-shi/sheng-chan-mo-shi/factory-pattern.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
