设计模式——工厂方法模式

在简介一文的最后,提到了一个简单工厂模式,但是它并不属于23种设计模式之一,可能是它太过于简单,又或者是它具有以下一些不足:在简单工厂模式中,只提供了一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它知道每一个产品对象的创建细节,并决定何时实例化哪一个产品类。 简单工厂模式最大的缺点是当有新产品要加入到系统中时,必须修改工厂类,加入必要的处理逻辑,这违背了 “ 开闭原则” 。

 

在简单工厂模式中,所有的产品都是由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性,而工厂方法模式则可以很好地解决这一问题。

 

一、工厂方法模式动机

来看一个例子:某电视机厂专为各知名电视机品牌代工生产各类电视机,当需要海尔牌电视机时只需要在调用该工厂的工厂方法时传入参数 “ Haier” ” ,需要海信电视机时只需要传入参数“ “ Hisense” ” ,工厂可以根据传入的不同参数返回不同品牌的电视机。

 

这个按之前的简单工厂模式来设计相信对大家来说不是什么难事了。问题的关键在于简单工厂模式的局限性,这样,每次增加一个产品的时候,如长虹电视,就需要修改工厂类的代码,这违背了“开闭原则”。

 

现在对该系统进行修改,不再设计一个电视工厂类来统一负责所有产品的创建,而是将具体产品的创建过程交给专门的工厂子类去完成 ,我们先定义一个抽象的电视产品工厂类 ,再定义具体的工厂类来生产海信电视、海尔电视、长虹电视等 ,它们实现在抽象电视产品工厂类中定义的方法。这种抽象化的结果使这种结构 可以在不修改具体工厂类的情况下引进新的产品 ,如果出现新的电视产品类型,只需要为这种新类型的电视产品创建一个具体的工厂类就可以获得该新电视产品的实例,这一特点无疑使得工厂方法模式具有超越简单工厂模式的优越性,更加符合“开闭原则”。

 

二、工厂方法模式定义

工厂方法模式 (Factory Method Pattern) 又称为工厂模式,也叫 虚拟构造器 (Virtual Constructor) 模式或者 多态工厂 (Polymorphic Factory) 模式 ,它属于类创建型模式。在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。

 

三、工厂方法模式的结构

工厂方法模式包含如下角色:

•  Product :抽象产品

•  ConcreteProduct :具体产品

•  Factory :抽象工厂

•  ConcreteFactory


 

四、模式分析

工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。 在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。 这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责哪一个产品类被实例化这种细节,这使得 工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品 。

 

当系统扩展需要添加新的产品对象时,仅仅需要添加一个具体产品对象以及一个具体工厂对象,原有工厂对象不需要进行任何修改,也不需要修改客户端, 很好地符合了“开闭原则” 。而简单工厂模式在添加新产品对象后不得不修改工厂方法,扩展性不好。 工厂方法模式退化后可以演变成简单工厂模式。

 

五、实例(参见第一部分的模式引用,电视产品那个)

1、创建电视产品抽象类(接口)

 

public interface TVProduct {
	public void play();
}
 2、创建生产电视产品对象的抽象工厂类(接口)

  

public interface TVFactory {
	public TVProduct produceTV();
}
 3、创建具体的电视产品子类(HaierTV)

  

public class HaierTV implements TVProduct{
	public void play() {
		System.out.println("This is HaierTV!");
	}
}
 4、创建专门负责生产HaierTV对象的具体工厂子类(HaierTVFactory) 

 

public class HaierTVFactory implements TVFactory {
	public TVProduct produceTV(){
		return new HaierTV();
	}
}
 5、创建客户端类进行测试 

 

public class Test {
	public static void main(String args[]){
		//声明一个工厂类对象yvf
		TVFactory tvf;
		//声明一个TV类对象tvp
		TVProduct tvp;
		//创建具体子工厂对象,并向上转型给父类工厂对象tvf
		tvf = new HaierTVFactory();
		//父类对象通过向上转型后而到对象去调用produceTV()方法,生产出具体的产品对象
		tvp = tvf.produceTV();
		//产品对象调用自己的play()方法
		tvp.play();
	}
}
 6、模式优势

通过以上五点,我们用工厂方法模式设计了一个模拟电视产品的场景,现在假设我们需要更改电视产品,不再使用海尔(即第5点中的HaierTVFactory对象),而是需要改成海信电视,此时,我们只需要创建一个具体的海信电视类(HisenseTV),让其继承TVProduct类,并重写父类的play()方法,同时再创建一个负责生产HisenseTV类对象的具体工厂(HisenseTVFactory)即可,具体可看以下代码:

 

public class HisenseTV implements TVProduct{
	public void play(){
		System.out.println("This is HisenseTV!");
	}
}
 
public class HisenseTVFactory implements TVFactory{
	public TVProduct produceTV(){
		return new HisenseTV();
	}
}
 

创建好新的产品子类和工厂子类后,我们再来看第5点的Test.java中的代码,可以发现,此时我们只需要改tvf = new HaierTVFactory();这一行代码,将HaierTVFactory()改成HisenseTVFactory()即可。如果使用java的反射机制和XML文档的结合,还可以这么写:

tvf = (PayMethodFactory)XMLUtil.getBean(); //getBean()的返回类型

这样一来只需要修改XML中的名字,连java文件都不需要改动,很好的符号了“开闭原则”。

谢谢您的关注和阅读,文章不当之处还请您不吝赐教~~~微笑微笑微笑

猜你喜欢

转载自bill56.iteye.com/blog/2279160