工厂模式是我们最常使用的设计模式之一,在工厂模式中我们在创建类时,不会暴露客户端调用逻辑,通过使用一个共同的接口来指向创建的对象
下面我们以车厂造车为业务原型,来讲解工厂模式的三种类型:简单工厂模式、方法工厂模式、抽象工厂模式
简单工厂模式
简单工厂模式是一种创建型模式,又叫静态方法工厂模式,是通过定义一个类用来创建其他的类,被创建的类通常都具有相同的父类。简单工厂设计模式相当于是一个工厂中有各种不同的产品,创建在一个类中,调用者无需知道具体产品的名称,只需要知道产品类所对应的参数即可。但是工厂的职责过重,而且当类型过多时不利于系统的扩展维护。
要做个工厂来造车这个业务来说:
类图:
代码设计:
-
张三想开一家工厂,这个工厂的产品类型就是汽车,下面用代码描述汽车这一对象:汽车都能跑,没毛病吧。
/** * 汽车 */ public interface Car { /** * 汽车都能跑起来 */ void run(); }
-
那么确定了要干什么,要造车。现在要确定都造什么牌子什么样子的车了吧,起步阶段,先造这些
AudiCar
、BmwCar
牌子的汽车吧/** * 奥迪汽车 */ public class AudiCar implements Car{ public void run() { System.out.println("奥迪汽车跑起来了..."); } } /** * 宝马汽车 */ public class BmwCar implements Car{ public void run() { System.out.println("宝马汽车跑起来了..."); } }
-
现在职责和需求已经确定了,那么我工厂是不是要建设出来
/** * 汽车生产工厂 */ public class CarFactory { /** * 生产汽车方法 * * @param carType 生产什么品牌的汽车 * @return 汽车对象 */ public static Car createCar(String carType) { if (carType.equals("Audi")) { return new AudiCar(); } else if (carType.equals("Bmw")) { return new BmwCar(); } else { return null; } } }
-
只需要告诉工厂,你要什么牌子的汽车,他直接就给你创建好,拿去开就完事儿了。
public static void main(String[] args) { // 小明跟汽车厂说,想要一台奥迪 Car audiCar=CarFactory.createCar("Audi"); audiCar.run(); // 小明突然又想要一台宝马 Car bmwCar=CarFactory.createCar("Bmw"); bmwCar.run(); }
输出:
优点:
工厂类含有必要的判断逻辑,调用者给出信息后通过工厂类来决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅“消费”产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象,有利于整个软件体系结构的优化。
缺点:
由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求.这种对条件的判断和对具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;
适用范围:
工厂类负责创建的对象比较少,客户只知道传入了工厂类的参数,对于始何创建对象(逻辑)不关心。
方法工厂模式
方法工厂模式其实非常简单,和简单工厂模式差不多。简单工厂模式时同一个工厂根据你输入的类型返回不同的汽车,而工厂方法模式是通过不同的对象而创建出不同的工厂,单独生产,也就是说,以原来的工厂为模型创建不同的工厂生产不同的商品。
类图:
代码设计:
-
张三业务越做越大,想要在自己的工厂增加一个新的汽车品牌:奔驰 生产,但是发现增加一个汽车品牌需要在工厂做很大的改动,成本就比较高,而且张三野心很大,不排除后续还会增加其他汽车品牌。那么一不做二不休,按照原本工厂的工作模式直接把一个车厂拆分成三个分厂
AudiCarFactory
、BmwCarFactory
、BenzCarFactory
/** * 汽车生产工厂统一接口 */ public interface CarFactory { /** * 生产汽车方法 * @return 汽车对象 */ Car createCar(); } /** * 奥迪汽车工厂 */ public class AudiCarFactory implements CarFactory{ /** * 生产汽车方法 * * @return 汽车对象 */ public Car createCar() { return new AudiCar(); } } /** * 宝马汽车工厂 */ public class BmwCarFactory implements CarFactory{ /** * 生产汽车方法 * * @return 汽车对象 */ public Car createCar() { return new BmwCar(); } } /** * 奔驰汽车工厂 */ public class BenzCarFactory implements CarFactory{ /** * 生产汽车方法 * * @return 汽车对象 */ public Car createCar() { return new BenzCar(); } } /** * 奔驰汽车 */ public class BenzCar implements Car { public void run() { System.out.println("奔驰汽车跑起来了..."); } } /** * 奥迪汽车 */ public class AudiCar implements Car { public void run() { System.out.println("奥迪汽车跑起来了..."); } } /** * 宝马汽车 */ public class BmwCar implements Car { public void run() { System.out.println("宝马汽车跑起来了..."); } }
-
这个时候生产汽车,跟原来就不一样了,不需要传参,直接让相应工厂生产。
/** * 方法工厂模式实例 */ public static void main(String[] args) { // 生产奥迪汽车 CarFactory audiCarFactory=new AudiCarFactory(); Car audiCar=audiCarFactory.createCar(); audiCar.run(); // 生产宝马汽车 CarFactory bmwCarFactory=new BmwCarFactory(); Car bmwCar=bmwCarFactory.createCar(); bmwCar.run(); // 生产奔驰汽车 CarFactory benzCarFactory=new BenzCarFactory(); Car benzCar=benzCarFactory.createCar(); benzCar.run(); }
运行结果:
优点:
工厂方法模式是为了克服简单工厂模式的缺点(主要是为了满足OCP)而设计出来的。简单工厂模式的工厂类随着产品类的增加需要增加很多方法(或代码),而工厂方法模式每个具体工厂类只完成单一任务,代码简洁。工厂方法模式完全满足OCP,即它有非常良好的拓展性
缺点:
假如某个具体产品类需要进行一定的修改,很可能需要修改对应的工厂类。当同时需要修改多个产品类的时候,对工厂类的修改会变得相当麻烦。比如说,每增加一个产品,相应的也要增加一个子工厂,会加大了额外的开发量。
适用范围
当一个类不知道它所必须创建对象的类或一个类希望由子类来指定它所创建的对象时,当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候,可以使用工厂方法,支持多扩展少修改的OCP原则
抽象工厂模式
抽象工厂模式,提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式,是工厂方法模式的升级版,在有多个业务品种、业务分类时,通过抽象工厂模式产生需要的对象是一种非常好的解决方式。
我们还是以张三的车厂生产汽车为例,张三的工厂能生产不同品牌的汽车,那么汽车还有不同的颜色,这里简单分成白色和黑色。接下来怎么设计呢,这里先理解两个词:产品族、产品等级。产品族就是某一种类型的产品,产品等级就是在某一产品族的内部分了不同的等级,或者是不同的包装。对于我们这个例子来说,汽车三个品牌 奥迪、宝马、奔驰就是三个产品族、他们都有黑色和白色的车,这就可以认为是不同的产品等级。
那么在抽象工厂模式中,应该如何创建相应的步骤来完成设计呢???
类图:
代码设计:
-
创建抽象汽车抽象类
/** * 汽车 */ public abstract class AbstractCar { /** * 汽车都能跑 */ abstract void run(); }
-
创建两种产品等级抽象类: 黑色汽车抽象类、白色汽车抽象类
/** * 白色汽车 */ public abstract class WhiteCar extends AbstractCar{ } /** * 黑色汽车 */ public abstract class BlackCar extends AbstractCar{ }
-
创建三个产品族各自的 黑色、白色汽车实体类
/** * 白色奥迪 */ public class WhiteAudiCar extends WhiteCar{ /** * 汽车都能跑 */ public void run() { System.out.println("一辆白色奥迪跑起来了..."); } } /** * 黑色奥迪 */ public class BlackAudiCar extends BlackCar{ /** * 汽车都能跑 */ public void run() { System.out.println("一辆黑色奥迪跑起来了..."); } } /** * 白色宝马 */ public class WhiteBmwCar extends WhiteCar{ /** * 汽车都能跑 */ public void run() { System.out.println("一辆白色宝马跑起来了..."); } } /** * 黑色宝马 */ public class BlackBmwCar extends BlackCar{ /** * 汽车都能跑 */ public void run() { System.out.println("一辆黑色宝马跑起来了..."); } } /** * 白色奔驰 */ public class WhiteBenzCar extends WhiteCar{ /** * 汽车都能跑 */ public void run() { System.out.println("一辆白色奔驰跑起来了..."); } } /** * 黑色奔驰 */ public class BlackBenzCar extends BlackCar{ /** * 汽车都能跑 */ public void run() { System.out.println("一辆黑色奔驰跑起来了..."); } }
-
创建汽车抽象工厂类,可以生产三个产品族(品牌)的汽车
/** * 汽车工厂抽象类 */ public abstract class AbstractCarFactory { /** * 生产一辆奥迪汽车 */ abstract AbstractCar createAudiCar(); /** * 生产一辆宝马汽车 */ abstract AbstractCar createBmwCar(); /** * 生产一辆奔驰汽车 */ abstract AbstractCar createBenzCar(); }
-
创建两个产品等级各自的生产工厂类
/** * 生产白色汽车工厂 */ public class WhiteCarFactory extends AbstractCarFactory{ /** * 生产一辆白色奥迪汽车 */ AbstractCar createAudiCar() { return new WhiteAudiCar(); } /** * 生产一辆白色宝马汽车 */ AbstractCar createBmwCar() { return new WhiteBmwCar(); } /** * 生产一辆白色奔驰汽车 */ AbstractCar createBenzCar() { return new WhiteBenzCar(); } } /** * 生产黑色汽车工厂 */ public class BlackCarFactory extends AbstractCarFactory{ /** * 生产一辆黑色奥迪汽车 */ AbstractCar createAudiCar() { return new BlackAudiCar(); } /** * 生产一辆黑色宝马汽车 */ AbstractCar createBmwCar() { return new BlackBmwCar(); } /** * 生产一辆黑色奔驰汽车 */ AbstractCar createBenzCar() { return new BlackBenzCar(); } }
-
实际使用
public static void main(String[] args) { WhiteCarFactory whiteCarFactory=new WhiteCarFactory(); BlackCarFactory blackCarFactory=new BlackCarFactory(); // 生产一辆白色奥迪汽车 AbstractCar whiteAudiCar=whiteCarFactory.createAudiCar(); whiteAudiCar.run(); // 生产一辆黑色奥迪汽车 AbstractCar blackAudiCar=blackCarFactory.createAudiCar(); blackAudiCar.run(); // 生产一辆白色宝马汽车 AbstractCar whiteBmwCar=whiteCarFactory.createBmwCar(); whiteBmwCar.run(); // 生产一辆黑色宝马汽车 AbstractCar blackBmwCar=blackCarFactory.createBmwCar(); blackBmwCar.run(); // 生产一辆白色奔驰汽车 AbstractCar whiteBenzCar=whiteCarFactory.createBenzCar(); whiteBenzCar.run(); // 生产一辆黑色奔驰汽车 AbstractCar blackBenzCar=blackCarFactory.createBenzCar(); blackBenzCar.run(); }
运行输出:
优点:
抽象工厂模式主要在于应对“新系列”的需求变化。分离了具体的类,抽象工厂模式帮助你控制一个应用创建的对象的类,因为一个工厂封装创建产品对象的责任和过程。它将客户和类的实现分离,客户通过他们的抽象接口操纵实例,产品的类名也在具体工厂的实现中被分离,它们不出现在客户代码中。它使得易于交换产品系列。一个具体工厂类在一个应用中仅出现一次——即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。它有利于产品的一致性。当一个系列的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要,而抽象工厂很容易实现这一点。抽象工厂模式有助于这样的团队的分工,降低了模块间的耦合性,提高了团队开发效率
缺点:
抽象工厂模式在于难于应付“新对象”的需求变动。难以支持新种类的产品。难以扩展抽象工厂以生产新种类的产品。这是因为抽象工厂几乎确定了可以被创建的产品集合,支持新种类的产品就需要扩展该工厂接口,这将涉及抽象工厂类及其所有子类的改变
适用范围:
一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有形态的工厂模式都是重要的。这个系统有多于一个的产品族,而系统只消费其中某一产品族。同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现
总结
其实,无论是简单工厂模式**、工厂模式还是抽象工厂模式,它们本质上都是将不变的部分提取出来,将可变的部分留作接口,以达到最大程度上的复用。**究竟用哪种设计模式更适合,这需要根据具体的业务需求来决定。
代码实例:项目Github传送门