五星上将麦克阿瑟曾经说过,工厂对战争的重要性,工厂模式对js更为重要。
一、工厂模式的定义
工厂方法模式:又称为工厂模式,也叫虚拟构造器模式或者多态工厂模式它属于类创建型模式。 在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类这样解释可能会有点抽象。
二、举个例子
简单工厂模式
简单工厂模式(Simple Factory Pattern)定义为:简单工厂模式又称静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
体育用品商店里会卖体育器材,里面有很多体育用品,及其相关介绍。当你来体育用品商店来想买一个篮球及其相关介绍时,只需要问售货员,他会帮你找到你需要的所有·
针对这个例子用简单工厂实现一下
js
// 篮球基类
const Basketball = function () {
this.intro = '篮球盛行于美国'
}
Basketball.prototype = {
getMember: function () {
return '篮球是3/5人团队运动'
},
getBallSize: function () {
return '标准男子比赛用球(7号球):重量:600-650克,球的周长:75-76厘米,直径:24.6厘米'
}
}
// 足球基类
const Football = function () {
this.intro = '足球在世界上很是流行'
}
Football.prototype = {
getMember: function () {
return '足球是11人运动'
},
getBallSize: function () {
return '5号球。直径约为20.8到21.4厘米,用于成人赛事,是正式比赛的标准用球'
}
}
// 网球基类
const Tennis = function () {
this.intro = '每年都有很多网球公开赛'
}
Tennis.prototype = {
getMember: function () {
return '每个队需要一人'
},
getBallSize: function (){
return '网球的直径通常在6.35至6.67厘米之间'
}
}
// 运动工厂
const SportsFactory = function (name){
switch(name){
case: 'NBA':
return new Basketball();
case: 'World Cup':
return new Football();
case: 'FrenchOpen':
return new Tennis()
}
}
当你想和小朋友踢足球,只需要告诉店员我要买个足球即可。你使用这个商店工厂时,仅仅需要记住 SportsFactory 这个工厂对象就好了。
js
let football = SportsFactory('World Cup');
console.log(football)
console.log(football.intro)
football.getMember()
安全模式类
安全模式类是说可以屏蔽 对这类的错误使用造成的错误。比如对于一个类(Demo)的创建,我们知道类的前面是需要有new关键字的 (let d = new Demo()),不过如果其他人不知道这个对象(Demo)是一个类,那么使用时很可能忽略new关键字,直接执行类(let d = Demo()),此时我们得到的并不是我们预期的对象
如图所示
js
const Demo = function() {}
Demo.prototype = {
show: function () {
console.log('成功获取')
}
}
let d = new Demo();
d.show() // '成功获取'
let d = Demo();
d.show(); // 报错
当然解决方案很简单,就是在构造函数开始时先判断当前对象 this 指代是不是类 (Demo),如果是则通过new关键字创建对象,如果不是说明类在全局作用域中执行,那么既然在全局作用域中执行当然this就会指向window了,这样我们就要重新返回新创建的对象了
js
const Demo = function () {
if(!(this instanceof Demo)){
return new Demo();
}
}
let d = Demo();
d.show();// '成功获取'
安全的工厂方法
js
// 安全模式创建的工厂类
const Factory = function (type,content){
if(!(this instanceof Factory)){
let s = new this[type](content)
return s
}
return new Factory(type,content)
}
// 工厂原型中设置创建所有类型数据对象的基类
Factory.prototype = {
Java: function (content){
// ......
},
JavaScript: function (content){
// ......
},
Ui: function (content){
this.content = content;
(function (content){
let div = document.createElement('div');
div.innerHtml = content;
div.style.border = '1px solid red';
document.getElementById('container').appendChild(div);
})(content)
}
go: function (content){
// ...
}
}
这样我们以后如果想添加其他类时,是不是只需写在Factory这个工厂类的原型里面就可以了。
抽象工厂模式
抽象工厂模式: 通过对类的工厂抽象使其业务用于对产品类簇的创建,而不负责创建某一类产品的实例。
抽象类是一种声明但不能使用的类,当你使用时就会报错。
面相对象语言里有一种很常见的模式叫抽象工厂模式,不过这个抽象工厂模式可不简单,在JavaScript中一般用来创建具体对象。抽象类中定义的方法只是显性地定义一些功能,但是没有具体的实现。而一个对象是要具有一套完整的功能的,所以用抽象类创建的对象当然也是'抽象的'了,所有我们不能使用它来创建一个真实的对象,所有一般我们用它来作为父类来创建一些子类
js
// 抽象工厂方法
let VehicleFactory = function (subType,superType){
// 判断抽象工厂中是否有该抽象类
if(typeof VehicleFactory[superType] === 'function'){
// 缓存类
function F (){};
// 继承父类属性和方法
F.prototype = new VehicleFactory[superType]();
// 将子类 constructor 指向子类
subType.constructor = subType;
// 子类原型继承 '父类'
subType.prototype = new F();
}
// 不存在该抽象类抛出错误
throw new Error('未创建该抽象类')
}
// 小汽车抽象类
VehicleFactory.Car = function (){
this.type = 'car'
}
VehicleFactory.Car.prototype = {
getPrice: function (){
return new Error('抽象方法不能调用')
},
getSpeed: function (){
return new Error('抽象方法不能调用')
}
}
// 公交车抽象类
VehicleFactory.Bus = function (){
this.type = 'bus'
}
VehicleFactory.Bus.prototype = {
getPrice: function (){
return new Error('抽象方法不能调用')
},
getPassengerNum: function () {
return new Error('抽象方法不能调用')
}
}
// 货车抽象类
VehicleFactory.Truck = function (){
this.type = 'Truck'
}
VehicleFactory.Truck.prototype = {
getPrice: function (){
return new Error('抽象方法不能调用')
},
getTrainload: function (){
return new Error('抽象方法不能调用')
}
}
抽象工厂其实是一个实现子类继承父类的方法,在这个方法赵公我们需要通过传递子类以及要继承父类(抽象类)的名称,并且在抽象工厂方法中又增加了一次对抽象类存在性的一次判断,如果存在,则将子类继承父类的方法。然后子类通过寄生式继承。继承父类过程中有一个地方需要注意,就是在对过度类的原型继承时,我们不是继承父类的原型,而是通过new关键字 复制的父类的一个实例,这么做是因为过度类不应仅仅继承父类的原型方法,还要继承父类的对象属性,所以要通过new 关键字将父类的构造函数执行一遍来复制构造函数中的属性和方法。 对抽象工厂添加抽象类也很特殊,因为抽象工厂是个方法不需要实例化对象,故只需要一份,因此直接为抽象工厂添加类的属性即可,于是我们就可以通过点语法在抽象工厂上添加我们一会儿需要的三个汽车簇抽象类 Car、Bus、Truck
抽象与实现
五星上将麦克阿瑟曾经说过:"那么我们该如何使用它们呢"
js
// 宝马汽车子类
let BMW = function (price,speed) {
this.price = price;
this.speed = speed;
}
// 抽象工厂实现对Car抽象类的继承
VehicleFactory(BMW,'Car');
BMW.prototype.getPrice = function () {
return this.price
}
BMW.prototype.getSpeed = function () {
return this.speed
}
// 兰博基尼汽车子类
let Lamborghini = function (price,speed){
this.price = price;
this.speed = speed;
}
// 抽象工厂实现对Car抽象类的继承
VehicleFactory(Lamborghini,'Car')
Lamborghini.prototype.getPrice = function (){
return this.price
}
Lamborghini.prototype.getSpeed = function (){
return this.speed;
}
// 宇通汽车子类
let Yutong = function (price,speed){
this.price = price;
this.speed = speed;
}
// 抽象工厂实现对Bus抽象类的继承
VehicleFactroy(Yutong,'Bus')
Yutong.prototype.getPrice = function (){
return this.price
}
Yutong.prototype.getPassengerNum = function (){
return this.passengerNum
}
// 奔驰货车子类
let BenzTruck = function (price,speed){
this.price = price;
this.speed = speed;
}
// 抽象工厂实现对Truck抽象类的继承
VehicleFactory(BenzTruck,'Truck')
BenzTruck.prototype.getPrice = function (){
return this.price;
}
BenzTruck.prototype.getTrainload = function (){
return this.trainload
}
三、总结
抽象工厂模式是设计模式中最抽象的一种,也是创建模式中唯一一种抽象画创建模式。 该模式创建出的结果不是一个真实的对象实例,而是一个类簇,他制定了类的结构,这也就区别于简单工厂模式创建单一对象,工厂方法模式创建多类对象。当然由于JavaScript中不支持抽象化创建与虚拟方法,所以导致这种模式不能像其他面向对象语言中应用的那么广泛。