设计模式中牵涉到的工厂的模式共有三种,分别是简单工厂模式(Simple Factory)、工厂方法模式(Factory Method)以及抽象工厂(Abstract Factory)模式。三种设计模式层层递进,都克服了前一个模式在某一个方面的缺点,下面分别介绍。
准备知识
OCP(开闭原则,Open-Closed Principle):一个软件的实体应当对扩展开放,对修改关闭。我的理解是,对于一个已有的软件,如果需要扩展,应当在不需修改已有代码的基础上进行。
DIP(依赖倒转原则,Dependence Inversion Principle):要针对接口编程,不要针对实现编程。我的理解是,对于不同层次的编程,高层次暴露给低层次的应当只是接口,而不是它的具体类。
LoD(迪米特法则,Law of Demeter)又叫叫作最少知道原则:只与你直接的朋友通信,而避免和陌生人通信。众所周知类(或模块)之间的通信越少,耦合度就越低,从而更有利于我们对软件的宏观管理。
简单工厂模式及实例
简单工厂模式又叫静态工厂模式,顾名思义,它是用来实例化目标类的静态类,是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
简单工厂模式中包含的角色及其相应的职责如下:
工厂角色(Creator):这是简单工厂模式的核心,由它负责创建所有的类的内部逻辑。当然工厂类必须能够被外界调用,创建所需要的产品对象。
抽象(Product)产品角色:简单工厂模式所创建的所有对象的父类,注意,这里的父类可以是接口也可以是抽象类,它负责描述所有实例所共有的公共接口。
具体产品(Concrete Product)角色:简单工厂所创建的具体实例对象,这些具体的产品往往都拥有共同的父类,即都继承自抽象产品角色。
我们接触过的一个简单工厂模式下的实例就是Java的JDBC。
JDBC是SUN公司提供的一套数据库编程接口API,它利用Java语言提供简单、一致的方式来访问各种关系型数据库。Java程序通过JDBC可以执行SQL语句,对获取的数据进行处理,并将变化了的数据存回数据库,因此,JDBC是Java应用程序与各种关系数据进行对话的一种机制。用JDBC进行数据库访问时,要使用数据库厂商提供的驱动程序接口与数据库管理系统进行数据交互。
客户端要使用使用数据时,只需要和工厂进行交互即可,这就导致操作步骤得到极大的简化,操作步骤按照顺序依次为:注册并加载数据库驱动,一般使用Class.forName();创建与数据库的链接Connection对象;创建SQL语句对象preparedStatement(sql);提交SQL语句,根据实际情况使用executeQuery()或者executeUpdate();显示相应的结果;关闭数据库。
下面从例子说明简单工厂模式
假设有一家企业生产各种品牌的轿车(实际生活中不存在这种企业。。。。。) 对照上面三个职责,其中消费者是客户端,提出需要购买轿车的品牌,这家汽车生产企业是简单工厂,抽象产品是轿车,这里的轿车没有品牌,只是抽象意义上轿车,具体的产品就是各个品牌下的轿车,如奥迪、奔驰、劳斯莱斯、法拉利等具体品牌的轿车(这家汽车企业很不简单。。。。。)。
public interface Car{
public void go();
}
建立具体产品,这里我们建立3种具体的汽车,分别是奥迪、法拉利以及宾利。
public class AudiCar implements Car{
public void go(){
System.out.println("开着奥迪去东北!");
}
}
public class FerrariCar implements Car{
public void go(){
System.out.println("开着法拉利去东北!");
}
}
public class BentleyCar implements Car{
public void go(){
System.out.println("开着宾利去东北!");
}
}
接着我们建立模式的核心:唯一的静态工厂方法类
public class SimpleCarFactroy{
public static Car produceCar(String cartype){
if(cartype.equalsIgnoreCase("audi")){
return new AduiCar();
}else if(cartype.equalsIgnoreCase("ferrari")){
return new FerrariCar();
}else if(cartype.equalsIgnoreCase("bentley")){
return new BentleyCar();
}
else{
System.out.println("对不起,我们不生产"+cartype+"这种高端大气上档次的牌子的轿车!");
}
}
}
最后建立客户端类,就是消费者购买轿车
public class Consumer{
public static void main(String[] args){
Car audiCar=SimpleCarFactory.produceCar("Audi");
Car ferrariCar=SimpleCarFactory.produceCar("Ferrari");
Car bentleyCar=SimpleCarFactory.produceCar("Bentley");
Car hongqiCar=SimpleCarFactory.produceCar("Hongqi");
if(null!=audiCar){
audiCar.go();
}
if(null!=ferrariCar){
ferrariCar.go();
}
if(null!=bentleyCar){
bentleyCar.go();
}
if(null!=hongqiCar){
hongqiCar.go();
}
}
}
结果输出为:
至此,简单工厂模式下的代码已经写完了,我们根据DIP设计了一个Car接口,不同品牌的汽车都实现了这个接口。
对于不同的消费者,他们都是面向Car接口编程,也就是说实现了Car接口的具体类无需暴漏给消费者,这也满足了DIP。同时其也满足LoD原则,也就是消费者不直接与不同品牌的轿车通信,而是通过它们之间共同的桥梁SimpleCarFactory通信。
我们用OCP看看简单工厂,会发现如果要对系统进行扩展的话治需要增加实现产品接口的产品类(上例表现比如要增加个“宝马轿车”类),而无需对原有的产品类进行修改。这咋一看好像满足OCP,但是实际上还是需要修改代码的——对,就是修改工厂类。上例中如果增加“宝马轿车”产品类,就必须相应的修改“SimpleCarFactory”工厂类,增加个“宝马的go”方法。所以可以看出,简单工厂模式是不满足OCP的。
工厂方法模式及其实例
下面继续谈谈工厂方法模式。前一节的最末点明了简单工厂模式最大的缺点——不完全满足OCP。为了解决这一缺点,设计师们提出了工厂方法模式。工厂方法模式和简单工厂模式最大的不同在于,简单工厂模式只有一个(对于一个项目或者一个独立模块而言)工厂类,而工厂方法模式有一组实现了相同接口的工厂类。下面我们通过修改上一节的实例来介绍工厂方法模式。
我们在不改变产品类(“奥迪汽车”类和“法拉利汽车”类)的情况下,修改下工厂类的结构,如所示:
工厂类共同的抽象接口:
public interface CarFactory{
public Car produceCar();
}
3种具体的工厂,每一种工厂只生产一种品牌的轿车
public class AudiFactory implements CarFactory{
public Car produceCar(){
return new AudiCar();
}
}
public class FerrariFactory implements CarFactory{
public Car produceCar(){
return new FerrariCar();
}
}
public class BentleyFactory implements CarFactory{
public Car produceCar(){
return new BentleyCar();
}
}
最后是新的消费者类:
public class NewConsumer{
public static void main(String[] args){
CarFactory audiFactory=new AudiFactory();
CarFactory ferrariFactory=new FerrariFactory();
CarFactory bentleyFactory=new BentleyFactory();
Car audiCar=audiFactory.produceCar();
Car ferrariCar=ferrariFactory.produceCar();
Car bentleyCar=bentleyFactory.produceCar();
audiCar.go();
ferrariCar.go();
bentleyCar.go();
}
}
最终的运行结果为:
上面的这个例子和实际生活中倒有些类似,一个工厂只生产一种品牌的轿车,消费者需要购买哪种品牌的轿车,在直销的模式下,就直接去找生产这种品牌的汽车企业就行。
这样做有什么好处呢?很明显,这样做就完全OCP了。如果需要再加入(或扩展)产品类(比如加多个“宝马汽车类”)的话就不再需要修改工厂类了,而只需相应的再添加一个实现了工厂接口(“CarFactory”接口)的具体工厂类。
一句话总结工厂方法模式:定义一个用于创建对象的接口(CarFactory),让子类(不同的汽车工厂)决定实例化哪一个类。
抽象工厂模式及其实例
上面两种的工厂方法都属于普通的工厂方法,对于普通的工厂方法,我们是可以在产品这个维度上进行扩展,可以产生新的产品,可以产生产品的工厂,但是却不能增加产品系列,假如在上面的例子中,我们除了生产各种品牌的轿车之外,还要生产这些品牌的飞机,轿车和飞机属于两个产品系列,而且每个系列中都包含了相同数量的产品,并且不同系列下的产品有一定的关联关系,相关或者互相依赖,在本例中他们的关系为都属于同一个品牌。在抽象工厂模式中,产品系列称为等级结构,如这个例子中有2个等级结构,每个等级结构上有3种产品,此例子中同一个品牌下的轿车和飞机属于同一个产品族。产品族是指位于不同产品等级结构中,功能相关联的产品组成的家族。一般是位于不同的等级结构中的相同位置上。显然,每一个产品族中含有产品的数目,与产品等级结构的数目是相等的,形成一个二维的坐标系,水平坐标是产品等级结构,纵坐标是产品族。叫做相图。
在我们的例子中,每一个产品族中包含一个品牌的轿车和这个品牌的飞机。
下面是具体的例子
定义Plane接口以及该接口下的3个子类
public interface Plane{
public void fly();
}
public class AudiPlane implements Plane{
public void fly(){
System.out.println("开着奥迪飞机去东北!");
}
}
public class FerrariPlane implements Plane{
public void fly(){
System.out.println("开着法拉利飞机去东北!");
}
}
public class BentleyPlane implements Plane{
public void fly(){
System.out.println("开着宾利飞机去东北!");
}
}
和之前定义的Car系列的一个接口和3种子类组成了2个产品等级结构。
定义抽象工厂,实现能够生产一个产品族中所有的产品:
public interface AbstractFactory{
public Car produceCar();
public Plane producePlane();
}
定义具体的工厂,具体生产某一个产品族中的产品:
public class AudiFactory implements AbstractFactory{
public Car produceCar(){
return new AudiCar();
}
public Plane producePlane(){
return new AudiPlane();
}
}
public class FerrariFactory implements AbstractFactory{
public Car produceCar(){
return new FerrariCar();
}
public Plane producePlane(){
return new FerrariPlane();
}
}
public class BentleyFactory implements AbstractFactory{
public Car produceCar(){
return new BentleyCar();
}
public Plane producePlane(){
return new BentleyPlane();
}
}
最后定义客户端:
public class Consumer{
public static void main(String[] args){
AbstractFactory audifactory = new AudiFactory();
AbstractFactory ferrarifactory = new FerrariFactory();
AbstractFactory bentleyfactory=new BentleyFactory();
Car car1=audifactory.produceCar();
car1.go();
Plane plane1=audifactory.producePlane();
plane1.fly();
Car car2=ferrarifactory.produceCar();
car2.go();
Plane plane2=ferrarifactory.producePlane();
plane2.fly();
Car car3=bentleyfactory.produceCar();
car3.go();
Plane plane3=bentleyfactory.producePlane();
plane3.fly();
}
}
运行结果为:
在抽象工厂模式中,增加产品族时很方便,如在此例子中增加宝马产品族,只用增加继承了AbstractFactory的具体工厂以及涉及到的宝马轿车和宝马飞机类即可,原有代码不用改变,符合OCP原则。 但是增加增加新产品的等级结构:需要修改所有的工厂角色,没有很好支持"开放-封闭"原则。
综合起来,抽象工厂模式以一种倾斜的方式支持增加新的产品,它为新产品族的增加提供方便,而不能为新的产品等级结构的增加提供这样的方便。
抽象工厂的结构为:
可以看出有两个系列的产品A和B,每一个工厂都生产一种所有系列的产品。
三种工厂模式没有绝对的谁好谁劣,就看在具体的项目中谁更适合。