工厂模式
1、简单工厂模式
1.1、什么是简单工厂模式
简单工厂模式,又叫静态工厂方法,由一个工厂对象决定创建某一种产品对象类的实例,主要用来创建同一类对象。
1.2、简单工厂模式的种类
简单工厂模式1:
function Boy(name){
this.sex = 'male',
this.name = name
}
Boy.prototype = {
play:function(){
console.log('我们喜欢打篮球!')
}
}
function Girl(name){
this.sex = 'female',
this.name = name
}
Girl.prototype = {
play:function(){
console.log('我们喜欢打排球')
}
}
function Child(type,name){
if(type === 'boy'){
return new Boy(name)
}else{
return new Girl(name)
}
}
let Mike = new Child('boy','Mike')
let Jean = new Child('girl','Jean')
console.log(Mike.name)
console.log(Mike.sex)
Mike.play()
console.log(Jean.name)
console.log(Jean.sex)
Jean.play()
简单工厂模式2:
function Child(type,name){
let obj = new Object()
obj.name = name
if(type === 'boy'){
obj.sex = 'name'
obj.play = function(){
console.log('我们喜欢打篮球!')
}
}else{
obj.sex = 'female'
obj.play = function(){
console.log('我们喜欢打排球')
}
}
return obj
}
let Mike = new Child('boy','Mike')
let Jean = new Child('girl','Jean')
console.log(Mike.name)
console.log(Mike.sex)
Mike.play()
console.log(Jean.name)
console.log(Jean.sex)
Jean.play()
第一种简单工厂模式是通过类实例化对象创建的,第二种是通过创建一个新对象然后包装增强其功能和属性实现的。他们实现方式的差异性也导致了,第一种通过类创建的对象可以共用他们父类上的原型方法。而后面通过寄生方式创建的对象都是一个新的个体,他们的方法就不能共用了。
1.3、简单工厂模式和类的异同点
相同点:都是为了方便创建对象
不同点:简单工厂模式其实类,不同的是,它对类进行了进一步的分类,通过类型参数判断创建哪种类型的类。就像例子中的Child类,通过type参数,判断是创建一个Boy,还是创建一个Girl。
2、工厂方法模式
2.1、什么是工厂方法模式
工厂方法模式(Factory Method),通过对产品类的抽象时期创建业务主要用于创建多类产品的实例。
2.2、工厂方法模式实例
function Factory(type,content){
if(this instanceof Factory){
let s = new this[type](content)
return s
}else{
return new Factory(type,content)
}
}
Factory.prototype = {
Banana:function(content){
this.content = content
console.log('Banana: '+content)
},
Apple:function(content){
this.content = content
console.log('Apple: '+ content)
},
Orange:function(content){
this.content = content
console.log('Orange: '+content)
}
}
let banama = Factory('Banana','is yellow')
let apple = Factory('Banana','is red')
let orange = Factory('Orange','is orange')
前面学过的简单工厂模式,每次只要有新的类,就需要添加做一次类型判断。因此,当创建多类对象时,简单工厂模式就不太适用了。所以此时使用工厂模式,就可以轻松地创建多类对象,避免了使用者与对象类之间的耦合,用户不必关心创建对象的具体方法,只要调用工厂方法即可。另外,这里使用了安全模式,避免新来的同事忘记使用new关键字而导致的错误。
2.3、练习
通过工厂方法为页面创建不同功能的按钮
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
button{
width: 100px;
height: 30px;
background: none;
margin: 10px;
}
</style>
<title>Document</title>
</head>
<body>
<div id="container"></div>
</body>
<script>
// 通过工厂方法为页面创建不同功能的按钮
// 声明按钮的工厂方法模式
function ButtonFactory(type,name,content){
if(this instanceof ButtonFactory){
let button = new this[type](name,content)
}else{
return new ButtonFactory(type,name,content)
}
}
// 不同的按钮
ButtonFactory.prototype = {
// alert按钮
Alert:function(name,content){
(function(name,content){
let div = document.createElement('div')
div.innerHTML = `<button οnclick="alert(${
content})">${
name}</button>`
document.getElementById('container').appendChild(div)
})(name,content)
},
// console按钮
Console:function(name,content){
(function(name,content){
let div = document.createElement('div')
div.innerHTML = `<button οnclick="console.log(${
content})">${
name}</button>`
document.getElementById('container').appendChild(div)
})(name,content)
},
}
let alertBtn = new ButtonFactory('Alert','弹框按钮','123')
let consoleBtn = new ButtonFactory('Console','控制台按钮','123')
</script>
</html>
3、抽象工厂模式
3.1、抽象类
JavaScript中的abstract还是一个保留字,目前还不能像Java等其他语言一样轻松的创建一个抽象类。说到底,抽象就是一种声明但是不能够使用的类,当你使用时就会报错。JavaScript虽然没有abstract,但是我们可以在类的方法中手动地抛出错误来模拟抽象类。
var Car = function(){
}
Car.prototype = {
getPrice: function(){
return new Error('抽象方法不可调用')
},
getSpeed: function(){
return new Error('抽象方法不可调用')
}
}
let car = new Car()
console.log(car.getPrice())
console.log(car.getSpeed())
可以看到直接调用getPrice和getSpeed方法就会报错,如果有一个类是Car中的子类,如果没有重写这两个方法,就会抛出异常。可以说,抽象类的其中一个意义就在于为子类忘记重写方法时提供一个友好的异常提示。
3.2、抽象工厂模式
// 抽象工厂方法
var VehicleFactory = function(subType,superType){
// 判断抽象工厂中是否有该抽象类
if(typeof VehicleFactory[superType] === 'function'){
// 缓存类
function F(){
}
// 继承父类属性和方法
F.prototype = new VehicleFactory[superType]()
// 将子类constructor指向子类
subType.constructor = subType
// 子类原型继承父类
subType.prototype = new F()
}else{
// 不存在该抽象类时抛出错误
throw new Error('为创建该抽象类')
}
}
// 小汽车抽象类
VehicleFactory.Car.prototype = {
getPrice:function(){
return new Error('抽象方法不能调用')
},
getSpeed:function(){
return new Error('抽象方法不能调用')
}
}
// 货车抽象类
VehicleFactory.Truck.prototype = {
getPrice:function(){
return new Error('抽象方法不能调用')
},
getSpeed:function(){
return new Error('抽象方法不能调用')
}
}
我们可以看到,抽象工厂模式真的很抽象。如果直接取去看这段代码,虽然可能会理解,但是过一阵子可能就忘了。所以我们可以想象一下,如果是自己要写这么一个抽象工厂,应该怎么写?
1)既然是抽象工厂,那么就需要将抽象类都放到一个工厂里,而这里的工厂就是一个对象,那抽象类就是它的各个属性。
// 小汽车抽象类
VehicleFactory.Car = function(){
this.type = 'car'
}
VehicleFactory.Car.prototype = {
getPrice:function(){
return new Error('抽象方法不能调用')
},
getSpeed:function(){
return new Error('抽象方法不能调用')
}
}
// 货车抽象类
VehicleFactory.Truck = function(){
this.type = 'truck'
}
VehicleFactory.Truck.prototype = {
getPrice:function(){
return new Error('抽象方法不能调用')
},
getSpeed:function(){
return new Error('抽象方法不能调用')
}
}
2)既然已经有了抽象类工厂了,那么怎么能判定子类是要继承哪一种父类呢?欸,有办法了。直接将父类中的抽象类名称作为参数去做判断,不就可以了。
var VehicleFactory = function(subType,superType){
}
3)那要用哪种继承方式呢?直接使用类式继承可以吗?先试试。
// 抽象工厂方法
var VehicleFactory = function(subType,superType){
// 判断抽象工厂中是否有该抽象类
if(typeof VehicleFactory[superType] === 'function'){
// 将子类constructor指向子类
subType.constructor = subType
// 子类原型继承父类
subType.prototype = new VehicleFactory[superType]()
}else{
// 不存在该抽象类时抛出错误
throw new Error('为创建该抽象类')
}
}
// 小汽车抽象类
VehicleFactory.Car = function(){
this.type = 'car'
}
VehicleFactory.Car.prototype = {
getPrice:function(){
return new Error('抽象方法不能调用')
},
getSpeed:function(){
return new Error('抽象方法不能调用')
}
}
// 货车抽象类
VehicleFactory.Truck = function(){
this.type = 'truck'
}
VehicleFactory.Truck.prototype = {
getPrice:function(){
return new Error('抽象方法不能调用')
},
getSpeed:function(){
return new Error('抽象方法不能调用')
}
}
// 宝马汽车子类
var BMW = function(price,speed){
this.price = price
this.speed = speed
}
VehicleFactory(BMW,'Car')
BMW.prototype.getPrice = function(){
return this.price
}
BMW.prototype.getSpeed = function(){
return this.speed
}
// 奔驰货车子类
var BenzTruck = function(price,speed){
this.price = price
this.speed = speed
}
VehicleFactory(BenzTruck,'Truck')
let myCar = new BMW(1000000,1000)
console.log(myCar.type)
console.log(myCar.getPrice())
console.log(myCar.getSpeed())
let myBenz = new BenzTruck(99999999999999,9999999)
console.log(myBenz.type)
console.log(myBenz.getPrice())
console.log(myBenz.getSpeed())
发现居然第一次尝试就成功了,创建了2个子类,一个有重写父类方法,另一个没有。果然一个成功,另一个使用没重写的方法时抛出了异常。
3.3、抽象工厂模式与工厂模式方法以及简单工厂模式之间的异同点及其关系
1)简单工厂模式,就是声明多个相同类型的功能类(比如男孩、女孩,或者篮球、足球、羽毛球等相同类型的类)和一个工厂类,工厂类至少有一个参数(比如type,后面的几个参数用于传给功能类),用于判断创建何种类型的功能类。
2)工厂模式方法,可以说是对简单工厂模式的封装优化。因为我们发现使用简单工厂模式的时候,如果相同类型的类过多的话,那就要不断的增加工厂类的类型判断语句,十分不合理。所以就想到,不如直接将所有相同类型的功能类作为工厂类的属性,当需要创建某种类时,就直接将类的名称作为参数传入即可。
3)抽象工厂模式,可以说是对工厂模式方法的进一步优化。考虑到,我们在创建不同类型的功能类的时候有可能会忘记创建对应的方法(比如获取车的速度、价格等方法),所以索性就自己制作一个抽象类作为父类,将每种类大概会用到的方法提前进行声明并抛出异常,如果子类没有重写对应的方法,那就会有一个友好的异常提示。
4)抽象工厂模式也是创建模式中唯一一种抽象化创建模式,换句话说,就是该模式创建出的结果不是一个真实的对象实例,而是一个类簇,它制定了类的结构(类应该有哪些方法)。从而区别于简单工厂模式创建单一对象,工厂方法模式创建多类对象(虽然很多类挂在工厂原型下,但是实际上在创建的时候也只会创建一个对象)。
下一章 建造者模式