《JavaScirpt设计模式》(6)——工厂模式

1、简介:

一个类或对象往往会包含别的对象。在创建这种对象时,你可能习惯于使用常规方法,即用new关键字和类构造函数。问题在于这会导致两个类之间产生依赖性。比如:现在有几个PC生厂商,生产台式机,每个台式机的组成部分都是CPU、Hardisk、MontherBoard。生产商从各个硬件商收购硬件,并自行组装他们卖给顾客,于是我们如下设计这些类:

function CPU(){
	this.frequency  = 1.2;
	this.core = 1;
}
function Hardisk(){
	this.capacity = 320;
}

function MotherBoard(){}

function PC(){}
PC.prototype.addCPU = function(cpu){
	if(!(cpu instanceof CPU)){
		throw new Error('param type error: the param should be  instanceof "CPU"');
	}
	this.cpu = cpu;
	return this;
};
PC.prototype.addDisk = function(disk){
	if(!(disk instanceof Hardisk)){
		throw new Error('param type error: the param should be  instanceof "Hardisk"');
	}
	this.hardisk = disk;
	return this;
};
PC.prototype.addMotherBoard = function(mboard){
	if(!(mboard instanceof MotherBoard)){
		throw new Error('param type error: the param should be  instanceof "Hardisk"');
	}
	this.MotherBoard = mboard;
	return this;
};

/*PC生产商*/
function PCShop(){}
PCShop.prototype.createPC = function(){
	var pc = new PC();
	pc.addCPU(new CPU()).addDisk(new Hardisk()).addMotherBoard(new MotherBoard);
	return pc;
};
PCShop的createPC函数所做的工作是生产一个PC,接着给他组装上各种硬件,看起来似乎没啥大问题,假如生厂商业务扩大了,开始做笔记本的生意了,而笔记本虽然也包括CPU、Hardisk、MotherBoard,但是这些硬件和台式机的又有区别,适用于笔记本上的硬件继承于原来的硬件,你怎样才能较容易的改变createPC以让它用这些新类型的对象创建PC呢? 这种情况下,改变的最大障碍是createPC对被实例化的类进行了硬编码,也许你可以在函数中增加参数,并增加一些代码组装笔记本。如果某天,厂商又要生产平板电脑了呢? 这种不灵活意味着我们应该重新设计,通过以下工厂模式可以弱化对象之间的耦合。

2、抽象工厂(Abstrack Factory)

抽象工程提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。我们把上述代码中的“创建新实例”这部分工作分离出来,转交给工厂对象,上述情景可以抽象出一个基本的工厂对象,这个基本的工厂对象提供生产产品的接口,更具体的工厂继承这个基本工厂,根据需要实现更具体的接口。一个应用中一般每个产品系列只需一个具体的工厂,因此工厂通常最好实现为一个单例。于是我们首先增加一个基本工厂的单例,在很多情况下,这个基本工厂都实现为抽象类,即只是提供公共接口,不过特定于我们的环境下,实现为提供默认的产品:

*=====修改和增加的代码=====*/
/*PC工厂*/
var PCFactory = {
	makeCPU:function(){
		return new CPU();
	},
	makeHardisk:function(){
		return new Hardisk();
	},
	makeMotherBoard:function(){
		return new MotherBoard();
	}
};

/*PC生产商*/
function PCShop(){}
PCShop.prototype.createPC = function(factory){
	var pc = new PC(),
		cpu = factory.makeCPU(),
		disk = factory.makeHardisk(),
		mbo  = factory.makeMotherBoard();
		
	pc.addCPU(cpu).addDisk(disk).addMotherBoard(mbo);
	return pc;
};
上述代码新增加了一个PCFactroy对象,提供了生产基本产品的接口,在createPC函数中,增加了一个工厂参数,用于获得各种产品,这里并未检查参数的类型,实际应用中,应该配合接口一起工作。我们现在来看看,加入厂商增加笔记本产品的实现,我们说过,一个实例化的工厂对应一个产品系列,于是我们增加一个笔记本工厂,同时根据笔记本硬件和台式机的差距,新增加适合于笔记本的硬件产品类。设计如下:

/*=====修改和增加的代码=====*/
function NoteCPU(){
	NoteCPU.superClass(this);
	this.type = '笔记本CPU';
}
NoteCPU.extend(CPU); //用到了以前我们增加的extend函数

/*笔记本工厂单例,内部继承PC工厂,*/
var NoteBookFactory = function(){
	var F = function(){};
	F.prototype.makeCPU = function(){ //实现自己的mkaeCPU方法,产生笔记本CPU
		return new NoteCPU();
	}; 
	F.extend(PCFactory);
	var obj = new F(); 
	return obj;
}();
上述代码新增了一个NoteCPU类,继承于CPU类,这里简单起见,没有增加其他新的硬件类,笔记本工厂单例对象通过自运行函数在内部创建构造函数,实现了单例的继承,当然也可以直接定义NoteBookFactroy为字面量对象,自己扩展一个函数将PCFactory中的未改变的接口赋给NoteBookFactory。我们无需改动createPC,就可以任意的增加新的产品系列,生产台式机这样调用: var shop = new PCShop(); pc.createPC(PCFactroy); 生产笔记本则可以这样调用:pc.createPC(NoteBookFactory);

3、工厂方法(Factroy Method)

工厂方法定义了一个用于创建对象的接口,让子类决定实例化哪一个类,它使一个类的实例化延迟到子类。 从示例角度看, PC厂商增加了笔记本业务,可以看成新开了一家笔记本店,即NoteBookShop, 台式机店面按着自己的流程组装机器(createPC),同样是获得CPU(makeCPU),获得Hardisk(makeHardisk),获得MotherBoard(makeMotherBoard), 笔记本店面的组装流程与台式机是一样的,唯一不同的是获得的硬件类型稍有差异,也许哪天扩展了平板电脑的业务,但流程还是一致的,区别的只是硬件获得的类型,于是我们可以把PCShop作为基类,具有一组基本的制作硬件的接口,而其他类型的PC从这继承而来,实现自己的接口。设计如下:

/*=====修改和增加的代码=====*/
/*PC工厂,这里默认生产基本的台式机*/
var PCShop = {
	makeCPU:function(){
		return new CPU();
	},
	makeHardisk:function(){
		return new Hardisk();
	},
	makeMotherBoard:function(){
		return new MotherBoard();
	},
	createPC:function(){
		var pc   = new PC(),
			cpu  = this.makeCPU(),
			disk = this.makeHardisk(),
			mbo  = this.makeMotherBoard();
			
		pc.addCPU(cpu).addDisk(disk).addMotherBoard(mbo);
		return pc;
	}
};


/*笔记本工厂单例,内部继承PCShop*/
var NoteBookShop = function(){
	var F = function(){};
	F.prototype.makeCPU = function(){ //实现自己的mkaeCPU方法,产生笔记本CPU
		return new NoteCPU();
	}; 
	F.extend(PCShop);
	var obj = new F(); 
	return obj;
}();
上述代码中,PCShop为最基本的PC工厂,默认生产最基本的台式机,提供了一个公共的产生对象的接口createPC, 并提供了基本的硬件获得方法,NoteBookShop继承了PCShop,并实现了自己的CPU获得途径以适应笔记本,若哪天增加平板业务,我们也只需要增加一个PADShop类,并根据需要实现对应的接口。





你可能感兴趣的:(javascirpt)