深入解析设计模式中常用的工厂模式:简单工厂的起源与做法
工厂模式在众多设计模式中占据着极其重要的地位,它归属于对象创建类设计模式范畴。简而言之,这种模式通过不直接使用new关键字来生成对象,旨在实现解耦,并有效减轻代码重构的复杂性。

工厂模式分为简单工厂、工厂方法。
一、简单工厂
简单工厂模式的起源并不复杂,我们可以设想这样一个场景:在对象X中,需要调用对象Y和Z,而Y和Z都继承了M对象。最直接的方法是在X中独立创建Y和Z的新实例,这样的操作会分别调用Y和Z的构造函数,使用起来相当便捷。然而,若未来需要将Y替换为Z,那么修改的复杂度将与Y被调用的频率直接相关。是否存在一种途径,通过单一地点的修改,便能使所有"new Y"变为"new Z"呢?
工厂模式的诞生源于此。其操作方式一目了然,即通过一个工厂类来生成Y和Z两个对象。依据特定字段的判定,能够生成不同的对象。在应用过程中,外部只需通过共同的父类M来接收这些返回的对象即可。
简单工厂模式有三个角色:
工厂类:负责根据特定的标志生成不同的对象实例;
共同父类:拥有子类共同属性,用于工厂模式调用时接收实例;
具体子类:实现各个子类特定属性
以下是一个示例工厂,专门负责制造各种品牌的汽车。该工厂能够根据客户指定的品牌,生产出相应品牌的汽车模型。其中,汽车类型包括宝马(bwm)和奥迪(audi),它们均继承自共同的父类“Car”。相应的实例代码如下:
//接口父类
public interface Car {
void printBrand();
}
//bwm子类
public class Bmw implements Car {
public void printBrand() {
System.out.println("this is a bmw Car");
}
}
//audi子类
public class Audi implements Car {
public void printBrand() {
System.out.println("this is an audi Car");
}
}
//工厂方法
public class CarFactory {
public static Car produceCar(String brand){
if ("bwm".equalsIgnoreCase(brand)){
return new Bmw();
}else if ("audi".equalsIgnoreCase(brand)){
return new Audi();
}else {
return null;
}
}
}
调用方法如下:
public static void main(String[] args){
创建车辆对象car1,该对象是通过调用CarFactory类的produceCar方法生成的。"audi");
创建汽车对象car2,该对象是通过调用CarFactory类中的produceCar方法生成的。"bwm");
car1.printBrand();
car2.printBrand();
}
运行 结果如下:
this is an audi Car
this is a bmw Car
从上述案例中我们可以观察到,工厂类并未实现任何具体业务,它仅提供了生产对象的途径。若需将汽车类型更改为其他,我们仅需调整传入工厂的方法。然而,若我的汽车品牌达到1000种,使用1000个if…else if结构显然过于繁琐。那么,这种简单的工厂模式是否真的不适用呢?实则不然,通过适当的优化,我们仍能轻松地运用这一模式。对这种形式的优化首先会想到泛型,但比泛型的更合适的是反射。
使用反射方式首先要修改工厂方法:
public class CarFactory {
public static Car produceCar对(String 类名)进行操作时,可能会引发异常情况。
Class>通过调用Class类的forName方法,我们成功获取了名为carClassName的类对象。
return (Car)clazz.newInstance();
}
}
调用方式为:
public static void main(String[] args) throws Exception{
Car car1 = CarFactory.produceCar("com.test.Audi");
Car car2 = CarFactory.produceCar("com.test.Bwm");
car1.printBrand();
car2.printBrand();
}
运行结果与先前一致。或许有同学留意到,运用反射时必须提供完整的类名,这样一来,书写大量完整的类名似乎显得颇为繁琐。确实如此,对于这类完整的类名,我们可以通过设定一个常量类来将其定义为常量,或者通过配置文件来实现,这样一来,操作起来岂不是更加便捷?
当然,采用反射技术只是众多途径之一,更佳的选择将在下一节中详细介绍,即工厂模式。
二、工厂方法
前文所述,简单工厂存在诸多不足之处,概括而言,这些缺陷主要可归纳为以下三个方面:
简单工厂模式下的工厂类涵盖了所有实例的生成机制,若此工厂类出现故障,将导致所有实例的生成受阻;这违反了设计模式中的开闭原则,即新增实例时需对工厂类的既有逻辑进行修改,长此以往,工厂类将变得越来越复杂(前文提到的反射机制虽解决了这一问题,却可能带来配置文件负担等问题);此外,工厂类中的方法被static关键字修饰,使其无法被继承或重写,从而引发问题。
工厂方法模式与简单工厂模式存在差异,它通过设定一个父类工厂来规范创建对象的接口,而具体对象的生成则交由子类来完成。这实际上是将简单工厂的方法进行了抽象化处理,并借助多态特性,使得不同的子类可以有不同的实现方式。因此,工厂方法模式又被称为多态工厂模式。
工厂方法模式需要以下四种角色:
抽象工厂作为具体工厂的上级类别,负责设定生产方法的接口;具体工厂作为抽象工厂的派生类别,负责具体实例的创建实现,并供外部调用;抽象产品是具体产品的基类,负责定义产品的通用接口;具体产品则详细说明了生产出的具体产品类型。
工厂方法的类示意图如下:
以下通过一个实例进行阐述,依旧以生产汽车为例,我们在原有品牌的基础上,进一步细分车辆种类,比如分为轿车和跑车两大类。首先,我们需要将之前那个简易工厂所生产的汽车类别进行抽象化处理:
public interface Car {
void display();
}
然后具体产品和类别的汽车实现抽象产品来实现:
public class BMWCar implements Car{
public void display() {
System.out.println("This is a car of bmw!");
}
}
public class BMWSuperCar implements Car{
public void display() {
System.out.println("This is a super car of bmw!");
}
}
public class AudiCar implements Car{
public void display() {
System.out.println("This is a car of audi!");
}
}
public class AudiSuperCar implements Car{
public void display() {
System.out.println(这是一款非常出色的奥迪超级跑车!);
}
}
在成功构建了抽象产品与具体产品的基础上,我们进一步明确了公共的抽象工厂角色,具体如下:
public interface AbstartCarFactory {
Car product(String classic);
}
具体工厂实现如下:
public class BMWFactory implements AbstartCarFactory{
public Car product(String classic){
if ("superCar".equalsIgnoreCase(classic)){
return new BMWSuperCar();
}else if ("car".equalsIgnoreCase(classic)){
return new BMWCar();
}
return null;
}
}
public class AudiFactory implements AbstartCarFactory{
public Car product(String classic){
if ("superCar".equalsIgnoreCase(classic)){
return new AudiSuperCar();
}else if ("car".equalsIgnoreCase(classic)){
return new AudiCar();
}
return null;
}
}
至此,工厂方法中的四个关键角色均已明确界定,接下来的步骤便是实际应用。举例来说,若需制造一辆宝马牌跑车和一辆奥迪牌轿车,其调用过程可以这样进行:
public class CarTest {
public static void main定义一个字符串数组参数,名为args,并在执行过程中可能抛出异常。
//宝马车
创建了一个名为AbstartCarFactory的宝马汽车制造厂实例,并将其赋值给变量bwmFactory。new BMWFactory();
创建了一个宝马品牌的汽车实例,该实例通过调用bwmFactory的product方法生成。"superCar");
car1.display();
在AbstractCarFactory类中,创建了一个名为audiFactory的实例对象。new AudiFactory();
创建了一个宝马品牌的汽车对象,名为car2,通过调用bwmFactory的product方法实现。"car");
car2.display();
}
}
得到输出如下:
This is a super car of bmw!
This is a car of bmw!
观察可知,工厂方法模式相较于简单工厂结构在结构上更为明确,且在新增产品时操作更为简便。实际上,类别同样可以抽象化,此处暂不逐一展示。抽象工厂与抽象产品并非仅限于接口,它们也可以是抽象类或普通类。在Java 8版本中,通过引入default关键字,使得接口中的方法不必每个子类都必须实现,从而使得选择接口的方式变得更加清晰。