一、工厂模式简介
工厂是通常用来批量生产货物的建筑,通过工厂生产的货物往往可以批量生产同样的货物。
在软件开发中,如果需要创建对象时可以将对象当作是产品,将创建产品的对象叫做工厂。这样就可以将创建对象和使用对象之间进行解耦,对象引用者无需关系对象是如何创建的,而工厂又可以保证创建出来的对象是一样的。
工厂模式的定义:定义一个创建对象的工厂接口,将产品对象的实际创建工作交给工厂类或工厂类的子类中实现。
按照实际业务场景可以将工厂模式分为三种模式:简单工厂模式、工厂方法模式和抽象工厂模式,其中工厂方法模式和抽象工厂模式都是GoF设计模式中的一种
二、简单工厂模式
当创建的产品类型是已知且有限的情况下,往往只需要一个工厂类就可以完成,此时就是简单工厂模式。
简单工厂模式有一个具体的工厂类,可以创建不同类型的产品,通常会提供一个静态获取对象的方法,根据传入的参数创建指定类型的对象,所以也可以叫做静态工厂方法模式。
简单工厂模式虽然实现简单,但是扩展性不强,每增加一个产品类型就需要增加一个具体的产品类和修改对应的具体工厂类,扩展成本较大,且违背了开闭原则
2.2、简单工厂模式的角色
简单工厂:负责创建所有产品的类
抽象对象:简单工厂创建的所有对象的抽象,可以是抽象父类也可以是接口
具体产品:简单工厂创建的具体对象类型
2.2、简单工厂模式案例
汽车工厂可以创建不同品牌汽车,此时可以定义一个创建汽车的工厂,根据传入的汽车品牌,创建不同品牌的汽车对象
代码实现:
定义汽车抽象类
1 /** 2 * 汽车接口 3 */ 4 public interface Car { 5 6 public void showCarBrand(); 7 8 }
定义具体品牌的汽车类
1 /** 2 * 奔驰车产品类 3 */ 4 public class BenzCar implements Car{ 5 @Override 6 public void showCarBrand() { 7 System.out.println("我是一辆奔驰车"); 8 } 9 }
1 /** 2 * 宝马车产品类型 3 */ 4 public class BmwCar implements Car{ 5 @Override 6 public void showCarBrand() { 7 System.out.println("我是一辆宝马车"); 8 } 9 }
1 /** 2 * 奥迪车产品类 3 */ 4 public class AudiCar implements Car{ 5 6 @Override 7 public void showCarBrand() { 8 System.out.println("我是一辆奥迪车"); 9 } 10 }
定义汽车工厂类
1 /** 2 * 创建汽车产品的工厂类 3 */ 4 public class CarFactory { 5 6 public static Car getCar(String brand){ 7 Car car = null; 8 switch (brand){ 9 case "benz": 10 car = new BenzCar(); 11 break; 12 case "bmw": 13 car = new BmwCar(); 14 break; 15 case "audi": 16 car = new AudiCar(); 17 break; 18 default: 19 System.out.println("未知品牌"); 20 break; 21 } 22 return car; 23 } 24 }
测试代码:
1 public static void main(String[] args) throws CloneNotSupportedException { 2 Car benz = CarFactory.getCar("benz"); 3 Car audi = CarFactory.getCar("audi"); 4 benz.showCarBrand(); 5 audi.showCarBrand(); 6 }
1 我是一辆奔驰车 2 我是一辆奥迪车
对于调用方而言无需关心具体的汽车产品是如何创建的,只需要从工厂中获取指定类型的具体对象即可,这样引用者类和具体产品类之间就实现了解耦,遵循了设计模式中的迪米特法则。
三、工厂方法模式
简单工厂模式虽然简单,但是扩展性较差,违背了开闭原则,而工厂方法模式就是对简单工厂模式的进一步抽象,可以实现不修改工厂的情况下可以扩展创建更多类型的产品,即满足了开闭原则
简单工厂模式中创建对象是由工厂本身来实现,而工厂方法模式中工厂只负责提供具体产品的工厂对象,而并不直接创建产品。
3.1、工厂方法模式中的角色
抽象工厂:提供创建产品的抽象,调用者通过抽象访问具体工厂的工厂方法来创建对象
具体工厂:实现抽象工厂的抽象方法,完成具体产品的创建
抽象对象:简单工厂创建的所有对象的抽象,可以是抽象父类也可以是接口
具体产品:简单工厂创建的具体对象类型
3.2、工厂方法模式案例
汽车工厂越做越大,不同类型汽车的制造越来越专业化,于是将不同品牌的汽车制造分别单独建厂制造,而总工厂负责给各个具体的子工厂提供基础规范
代码实现:
在3.1的案例代码基础之上修改CarFactory,定义为抽象接口,并定义一个创建汽车产品的方法
1 /** 2 * 创建汽车产品的工厂接口 3 */ 4 public interface CarFactory { 5 6 /** 定义获取汽车产品的方法 */ 7 public Car getCar(); 8 }
分别定义具体生产汽车的工厂类实现抽象工厂接口
1 public class BenzCarFactory implements CarFactory{ 2 3 @Override 4 public Car getCar() { 5 System.out.println("奔驰工厂创建奔驰汽车"); 6 return new BenzCar(); 7 } 8 }
1 public class AudiCarFactory implements CarFactory{ 2 @Override 3 public Car getCar() { 4 System.out.println("奥迪汽车生产奥迪汽车"); 5 return new AudiCar(); 6 } 7 }
测试代码:
1 public static void main(String[] args) throws CloneNotSupportedException { 2 CarFactory carFactory = new BenzCarFactory(); 3 Car benz = carFactory.getCar(); 4 benz.showCarBrand(); 5 }
1 奔驰工厂创建奔驰汽车 2 我是一辆奔驰车
相比于简单工厂模式,工厂方法模式的扩展性更好,如果需要添加新的汽车品牌,只需要新增一个具体工厂实现抽象工厂接口即可,抽象工厂的定义不需要修改,从而实现对开闭原则的满足。
四、抽象工厂模式
抽象工厂模式的定义是一种为访问类提供一个创建一组相关或相互依赖对象的接口,且访问类无需指定所要产品的具体类就能够得到同族的不同等级的产品的模式结构。
在工厂方法模式中,每一个具体的工厂类只可以创建同一种类型,比如奥迪工厂只能生产奥迪汽车,奔驰工厂只能生产奔驰汽车。但现实中工厂通常不会只生产一种产品,而是会生产多种种类的产品。比如奔驰工厂除了造奔驰轿车之外还可以生产跑车和商务车。
抽象工厂模式是工厂方法模式的升级版本,工厂方法模式只提供生产一种产品的工厂,而抽象工厂模式可以提供生产多个种类的产品。
抽象工厂模式优缺点:
1、可以在类的内部对产品族中相关联的多等级产品共同管理,不必要引入多个新的类进行管理
2、抽象工厂增强了程序的可扩展性,增加一个新的产品族时,不需要修改原代码,满足开闭原则
3、当产品族添加新产品时,所有的工厂类都需要进行修改,比较难以抽象和理解
4.1、抽象工厂模式的角色
抽象工厂:提供创建产品的接口,包含多个创建产品的方法,可以创建多个不同的产品
具体工厂:实现抽象工厂的多个抽象方法,完成具体产品的创建
抽象对象:简单工厂创建的所有对象的抽象,可以是抽象父类也可以是接口
具体产品:简单工厂创建的具体对象类型
可以发现抽象工厂模式和工厂方法模式中的角色基本上差不多,不同的是抽象工厂不止定义了一个产品的创建方法,而是定义了多个产品的创建产品的方法定义,而具体的工厂也需要实现多个产品创建的方法
4.2、抽象工厂模式案例
不同的汽车工厂除了生产汽车之外,又额外扩展了其他的产品,比如奔驰工厂还可以奔驰的发动机,奥迪工厂可以生产奥迪的发动机
代码实现:
定义发动机产品接口
1 /** 2 * 定义发动机型号 3 */ 4 public interface Engine { 5 public void showEngineBrand(); 6 }
定义发动机接口实现类,如奔驰发动机
1 public class BenzEngine implements Engine{ 2 @Override 3 public void showEngineBrand() { 4 System.out.println("我是奔驰的发动机"); 5 } 6 }
在3.2案例代码的基础上修改抽象工厂类,不仅可以生产汽车还可以生产发动机
1 /** 2 * 创建汽车产品族的工厂接口 3 */ 4 public interface CarFactory { 5 6 /** 定义获取汽车产品的方法 */ 7 public Car getCar(); 8 9 /** 定义获取发动机的方法 */ 10 public Engine getEngine(); 11 }
具体产品工厂实现抽象工厂,如奔驰工厂需要创建奔驰汽车和奔驰发动机
1 public class BenzCarFactory implements CarFactory{ 2 3 @Override 4 public Car getCar() { 5 System.out.println("奔驰工厂创建奔驰汽车"); 6 return new BenzCar(); 7 } 8 9 @Override 10 public Engine getEngine() { 11 System.out.println("奔驰工厂创建奔驰发动机"); 12 return new BenzEngine(); 13 } 14 }
测试代码:
1 public static void main(String[] args) throws CloneNotSupportedException { 2 CarFactory carFactory = new BenzCarFactory(); 3 Car benz = carFactory.getCar(); 4 Engine engine = carFactory.getEngine(); 5 benz.showCarBrand(); 6 engine.showEngineBrand(); 7 }
1 奔驰工厂创建奔驰汽车 2 奔驰工厂创建奔驰发动机 3 我是一辆奔驰车 4 我是一个奔驰发动机
可以看出抽象工厂模式相对于工厂方法模式而言丰富了工厂的产品种类,避免了为不同的产品创建不同的工厂,而是将相同族群的产品交给同一个工厂来创建,降低了类的数量也降低了复杂度
不过抽象工厂并非优于工厂方法模式,主要看后期的业务扩展需要。
1、当业务扩展需要新增一个具体的工厂时,比如从奔驰工厂扩展添加奥迪工厂、保时捷工厂时,则不需要修改抽象工厂,满足开闭原则
2、当业务扩展需要新增一个具体的产品时,比如奔驰工厂从生产汽车扩添加生产发动机、生产车轮、生产油箱时,则需要修改抽象工厂,且可能会影响其他的具体工厂,此时则不满足开闭原则
所以具体业务场景该使用哪一种工厂模式需要看后期的业务扩展需求,但是应该尽量满足开闭原则即可。