Javascript 中的设计模式(二):创建型设计模式

更多文章参见:
github.com/elevenbeans…

前言

本系列可以看作是我个人对于 Addy Osmani 的著作《Learning JavaScript Design Patterns》中内容的提炼,类似阅读笔记,目的是为了简单快速、又不失全面地了解设计模式的相关概念、特点、分类及其在 Javascript 中的实际产出。

分为五篇:

  1. 概述
  2. 创建型设计模式(本篇)
  3. 结构型(待补充)
  4. 行为型(待补充)
  5. MV*(待补充)

创建型设计模式

1. Constructor(构造器) 模式

基本的 Constructor

Javascript 不支持类,但支持 constractor 构造器函数。可以通过构造器前加new关键字告诉 Javascript 实例化一个对象。一个构造器的例子:

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;
    this.toString = function() {
        return this.model + 'has done' + this.miles + 'miles';
    };
}
var benz = new Car('GLS', 2018, 20000);
console.log(benz.toString()); // GLShas done20000miles

存在的问题是: this.toString() 这样的函数在每次创建新的对象的时候都会重新定义,这样很不理想,这类共有方法应该在所有实例间共享。

带原型的 Constructor

function Car(model, year, miles) {
    this.model = model;
    this.year = year;
    this.miles = miles;
}
Car.prototype.toString = function() {
    return this.model + 'has done' + this.miles + 'miles';
};
var benz = new Car('GLS', 2018, 20000);
var byd = new Car('qin', 2016, 68000);

如此一来,toString() 方法的单一实例就能通过原型链在 Car 的实例对象 benzbyd 中共享。

2. Factory(工厂) 模式

工厂模式并不显式要求使用构造函数。他通过提供一个通用的接口来创建对象,在接口的入参中指定需要创建的对象类型。具体例子如下:

首先定义不同类型的构造函数供给工厂消费。

// Car 构造函数
function Car(opt) {
    this.doors = opt.doors || 4;
    this.state = opt.state || 'new';
    this.color = opt.color || 'slivler';    
}
// Truck 构造函数
function Truck(opt) {
    this.state = opt.state || 'used';
    this.whellSize = opt. whellSize || 'large';
    this.color = opt.color || 'blue';
}

然后定义 vehicle 工厂:

// vehicle 工厂
function VehicleFactory() { }
// 定义 vehicle 工厂的原型和默认工具(Car)
VehicleFactory.prototype.vehicleClass = Car;
// 定义工厂创建实例的 API: createVehicle
VehicleFactory.prototype.createVehicle = function (opt) {
    if (opt.vehicleType === 'car') {
        this.prototype.vehicleClass = Car;
    } else if (opt.vehicleType === 'truck') {
        this.prototype.vehicleClass = Truck;
    } else {
        this.prototype.vehicleClass = Car;
    }
    return new this.prototype.vehicleClass(opt);
};

工厂创建完毕,具体实例的创建方式如下:

// 创建一个汽车(Car)实例
var factory = new VehicleFactory(); // 首先实例化一个 vehicle 工厂
var carIntance = factory.createVehicle({ // 使用工厂 API 创建 Car 实例
    vehicleType: 'car',
    color: 'yellow',
    door: 6
});
// 创建一个卡车(Truck)实例
var trunkIntance = factory.createVehicle({ // 使用工厂 API 创建 Truck 实例
    vehicleType: 'truck',
    color: 'yellow',
    state: 'new',
    wheelSize: 'small'
});

一般来讲,Factory 模式适用于

  • 复杂度较高的对象和组件 / 处理很多共享属性的对象或组件
  • 需要在不同环境生成不同类型的实例对象的时候

3. Abstract(抽象) 模式

对于上述的工厂,createVehicle 一经定义,就只支持 Car 和 Truck 两种类型的实例产出,无法动态增加新的实例类型。

所以需要抽离出来实例对象的产出细节,封装一个通用工厂。

抽象模式(在工厂模式中的)实践如下:

var AbstractVehicleFactory = (function(){
    var types = [];
    return {
        getVehicle: function (type, opt) {
            var Vehicle = types[type];
            return (Vehicle) ? return new Vehicle(opt) : null;
        },
        registerVehicle: function (type, Vehicle) {
            var proto = Vehicle.prototype;
            // 只对符合基本契约的进行注册
            if (proto.drive && proto.breakDown) {
                types[type] = Vehicle;
            }
            return AbstractVehicleFactory;
        }
    }
})();
// 用法
// 动态注册一个 Car
AbstractVehicleFactory.registerVehicle('car', Car);
// 实例化刚才注册的 Car
var car = AbstractVehicleFactory.getVehicle('car', {
    color: 'yellow',
    door: 6
});

4. Prototype(原型) 模式

原型模式是一种基于现有对象模版,通过克隆方式创建新对象的一种模式。

该模式是 Javascript 语言本身的优势。具体例子:

var car = {
    name: 'Ford Escort',
    drive: function () {
        console.log('Weeee. I am driving');
    },
    panic: function () {
        console.log('Wait. How do you stop this?');
    }
}
// 使用 Object.create 实例化一个 Car
var myCar = Object.create(car);
// car 是 myCar 的原型
console.log(myCar)

当然,Object.create() 完全可以通过第二个参数实现差异化继承。

Object.create(orgObj, {
    id: {
        value: 23324324,
        enumerable: true
    },
    'model': {
        value: 'Ford',
        enumerable: true
    }
})

如果不用 Object.create() 实现 Prototype(原型) 模式也很简单:

car.init = function (opt) {
    this.name = opt;
};
function Car () {
    function C() {};
    C.prototype = car;
    var c = new C();
    return c;
}
var myCar = Car();
myCar.init('Benz');

本质其实就是将源对象赋值给目标对象的原型,初始化可以通过为源对象预留 API(本例中为init())来实现。

5. Singleton(单例) 模式

一句话来形容,Singleton(单例) 模式限制类的实例化次数只能是一次。也就是说,在不存在该类实例的情况下,可以通过方法来创建实例;如果实例已经存在,返回该实例对象的引用。

function Car(){
    this.name = 'Singleton Car' ;
};
var Singleton = {
    instance: null,
    getInstance: function() {
        if (!this.instance) {
            this.instance = new Car();
        }
        return this.instance;
    }
};

就像这样当我们每次调用 Singleton.getInstance() 时,就会得到唯一的实例。

如此,可以将我们反复使用的对象只创建一次。减少不必要开销。

Singleton 模式还可以结合 JS 闭包反复引用内存中的同一对象,使运用程序更快速的执行。

6. Builder(生成器) 模式

将一个复杂对象的构造与它的表示相分离,使同样的创建过程可有不同的表示,这就叫做建造者模式。主要角色:

  1. Builder 这个接口类,定义这个建造者,统一的可操作的行为方式,它表示一个复杂的结构对象;
  2. Director 这个指挥者,用于指导 Builder 实例的执行过程跟形式,用于与 Builder 的实例 表现 相分离,用于指导 这个 Builder 实例 按某规则顺序来创建生成 产品结果;
  3. ResultObject 创建的结果都会生成一个结果对象;这是具体创建者根据 Director 指导创建的结果;

建造者模式实际,就是一个指挥者,一个建造者,外加一个使用指挥者调用具体建造者工作、并得从具体建造者得出结果的客户。模拟场景:

一户家人要建房子(ResultObject / 结果),但房子主人或家里其他人是不懂得如何去建房子的,所以他得去请几个工人(Builder / 建造者),这个建房子的队伍还得有个工头(Director / 指挥者),来按房主人的想法来建一套房子,工头按房主人(客户)的要求设计要求工人如何如何做。

工头说,第一步先把房整体骨架搭起来,第二步睡房建造好,第三步把厨房装饰好,第四步把客厅建造装饰完毕…

工头是不做事的,但具体工人必须按照工头的要求来做,第一步,第二步的这样步骤来建造,直至整个房子完成;

工人必须要有创建这个房屋的所有技能,即建骨架,装饰卧室等,即工人所做的事,或所具有的能力,必须大于或等于工头要求要做的事,或具有的能力;

即工头是个组织者,而工人提供技能;

实例代码:

1. Builder 类:

function workerBuilder() {
    this.workOne = function() {
        // 建房子骨架
    }
    this.workTwo=function() {
        // 建卧室
    }
    this.workThree=function() {
        // 建厨房
    }
    this.workFour=function() {
        // 建客厅
    }
    //....
       
    this.getResult = function() {
       // 建成房子
       var house = new House();
       // house.HouseFrame ...
       return house;
    }
}

workBuilder 是具体建造者类,workOne, Two是要做的事情,建骨架等。
当然 workBuilder 可以多建几个,用于表示建造者对于每个工作执行的方法不一样;但工作内容是一样的;

2. Director 类

function Director() {
    this.construct = function(builder) {
        builder.workOne();
        builder.workTwo();
        builder.workThree();
        builder.workFour();
        //...
        //上面的内容,顺序可设置,并且工作项也可以设定
    }
}

指挥者类下的指导方法,有对建造者的回调引用,内容包括建者工作内容几项或全部; 指挥者对建造者要做的事情进行组织跟安排。

3. ResultObject

function House() {
    this.HouseFrame = '';
    this.Room = '';
    this.Kitchen = '';
    this.LivingRoom = '';
    //...
}
``` 
#### 4. 使用方法
```js
var director = new Director(); // 房主人请 Director 工头来建房子
var builder = new workBuilder(); // 工头带来了建造者
director.construct(builder); // 工头指挥建造者来建房子
var house = builder.getResult(); // 房主人从建造者那里取得建好的房子

Referrence

www.2cto.com/kf/201412/3…

创建型设计模式汇总介绍到此结束,持续关注请 Star and Watch This github repo, 谢谢 :)

    原文作者:算法小白
    原文地址: https://juejin.im/entry/5ac19160518825558723b4fa
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞