“这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战”
工厂模式(Factory Pattern) 是 Java 中最常用的设计模式之一,Spring中的依赖注入即是通过工厂模式实现的。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式主要有三种:简单工厂、工厂方法、抽象工厂。
简单工厂模式
当我们创建一个对象的时候,一般采用new
的方式创建一个新对象。
A a = new A();
这种方法在一般情况下没有问题。但是当该对象经常被创建,并且创建对象初始化过程太复杂的时候,代码会显得太过冗余,并且可维护性也很差。
这时候我们可以采用一个工厂类将对象的创建过程封装,可以避免这个问题。
public class Factory {
public static Car create(String brand){
if (brand.equals("特斯拉")){
Tesla tesla = new Tesla();
// 初始化代码...
return tesla;
}else if (brand.equals("宝马")){
BMW bmw = new BMW();
// 初始化代码...
return bmw;
}else if (brand.equals("奔驰")){
Mercedes mercedes = new Mercedes();
// 初始化代码...
return mercedes;
}
return null;
}
}
如以上代码所示,当我们需要创建某个方法,直接调用Factory
中的create(String brand)
方法即可获得相应的实例化对象(参数brand采用enum形式实现会更合适)
。类图如下图所示。
工厂方法模式
简单工厂模式的代码实现中,使用了大量的if...else
语句,这会带来一个问题:当我们对产品种类进行添加或者减少的时候,我们需要对if...else
进行添加或者删除,这违反了开闭原则。
开闭原则:软件中的对象(类,模块,函数等等)应该对于扩展是开放的,但是对于修改是封闭的。
要想解决if...else
语句问题,我们可以为每个产品创建一个工厂子类,每个工厂子类实现抽象工厂接口。这样一来,我们可以直接调用工厂子类来获取产品对象,这就是工厂方法模式。
工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
public interface Factory {
Car create();
}
public class TeslaFactory implements Factory{
@Override
public Car create() {
Tesla tesla = new Tesla();
// 初始化...
return tesla;
}
}
// ...
类图如下所示:
抽象工厂模式
工厂方法模式也不是十全十美的,当产品数量过多的时候,会导致产生大量的工厂子类,太过繁琐。对此,我们可以不将每个产品对应一个工厂子类,而是将产品进行分组,每组产品对应一个工厂子类,即可解决这个问题,这就是抽象工厂模式。
抽象工厂模式:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体的类。
对于汽车工厂的示例来说,我们按品牌分可以分为宝马、奔驰等品牌,品牌内按能源可以分为电动汽车和燃油汽车 ,类图如下:
代码示例如下:
public interface Factory {
ElectricCar createElectricCar();
FuelCar createFuelCar();
}
public class MercedesFactory implements Factory{
@Override
public ElectricCar createElectricCar() {
ElectricMercedes electricMercedes = new ElectricMercedes();
return electricMercedes;
}
@Override
public FuelCar createFuelCar() {
FuelMercedes fuelMercedes = new FuelMercedes();
return fuelMercedes;
}
}
public class BMWFactory implements Factory{
@Override
public ElectricCar createElectricCar() {
ElectricBMW electricBMW = new ElectricBMW();
return electricBMW;
}
@Override
public FuelCar createFuelCar() {
FuelBMW fuelBMW = new FuelBMW();
return fuelBMW;
}
}
在该场景下,相比工厂方法模式的4个工厂子类,抽象工厂模式只需要2个工厂模式即可达成目的,弥补了工厂方法模式的缺点。但是,当需要扩展工厂子类内的产品组需要扩展的时候,只能对接口进行修改,扩展性较差。
总结
工厂模式主要有三种:简单工厂模式,工厂方法模式和抽象工厂模式。
- 简单工厂模式:由一个工厂对象决定创建出哪一种产品类的实例。
- 工厂方法模式:定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。
- 抽象工厂模式:抽象工厂模式提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体的类。
使用工厂模式的目的:
- 解耦:把对象的创建和使用的过程分开。就是当Class A 想调用 Class B 的时候,只是调用B的方法,不需要负责对B进行实例化,B的实例化交给工厂类负责。
- 降低代码重复:如果创建对象B的过程都很复杂,需要一定的代码量,而且很多地方都要用到,那么就会有很多的重复代码。我们可以这些创建对象B的代码放到工厂里统一管理。既减少了重复代码,也方便以后对B的创建过程的修改维护。(当然,也可以把这些创建过程的代码放到类的构造函数里,同样可以降低重复率,而且构造函数本身的作用也是初始化对象。不过,这样也会导致构造函数过于复杂,代码可读性比较差)。
- 降低维护成本:由于创建过程都由工厂统一管理,所以发生业务逻辑变化,不需要找到所有需要创建B的地方去逐个修正,只需要在工厂里修改即可,降低维护成本。同理,想把所有调用B的地方改成B的子类B1,只需要在对应生产B的工厂中或者工厂的方法中修改其生产的对象为B1即可,而不需要找到所有的new B()改为new B1()。
- 降低错误率:因为工厂管理了对象的创建逻辑,使用者并不需要知道具体的创建过程,只管使用即可,减少了使用者因为创建逻辑导致的错误。