定义
工厂模式(Factory Pattern)也是 Java中最常用的设计模式之一。这种类型的设计模式也属于创建型模式,它提供了一种创建对象的最佳方式。
工厂模式主要是为创建对象提供过渡接口来指向新创建的对象,以便将创建对象的具体过程屏蔽隔离起来,不会对客户端暴露创建逻辑,,从而达到提高灵活性的目的。
所以工厂模式就是通过定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。这就好比工厂里面负责物品的生产,我们需要什么东西直接去工厂里面拿就行了,而不用管这个物品是如何被造出来的。
根据抽象程度的不同可以将工厂模式分为三种具体的工厂模式,它们分别是:简单工厂模式(Simple Factory Pattern )、工厂方法模式(Factory Method Pattern)、抽象工厂模式(Abstract Factory)。接下来就分别说说这三种工厂模式。
简单工厂模式
简单工厂模式(Simple Factory Pattern):又称为静态工厂方法(Static Factory Method)模式。简单工厂模式的实质就是由一个工厂类根据传入的参数,动态决定应该创建哪一个产品类(这些产品类继承自一个父类或接口)的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
所以简单工厂模式包含以下三个部分:
Factory:工厂,工厂负责实现创建所有实例的内部逻辑
Product:抽象产品,抽象产品是所创建的所有对象的父类,负责描述所有实例所共有的公共接口
ConcreteProduct:具体产品,具体产品是创建目标,所有创建的对象都充当这个角色的某个具体类的实例。
栗子:首先声明一个抽象的产品类:
package com.jiangxia.Factory.SimpleFactory; /** * @Author: 江夏 * @Date: 2021/10/26/21:37 * @Description: 简单工厂方法的Product--抽象产品 */ public abstract class Product1 { public void method1(){ System.out.println("这里是抽象产品的公共方法"); } //这里声明抽象的业务方法 public abstract String method(); } 复制代码
接着需要一个工厂类:
package com.jiangxia.Factory.SimpleFactory; /** * @Author: 江夏 * @Date: 2021/10/26/21:42 * @Description: 工厂类 */ public class Factory1 { public static Product1 getProduct(String string){ Product1 product1 = null; if(string.equals("汽车")){ product1 = new ConcreteProduct1(); }else{ product1 = new ConcreteProduct2(); } return product1; } } 复制代码
然后再是具体的产品类,这里用于演示就是两个具体类:
package com.jiangxia.Factory.SimpleFactory; /** * @Author: 江夏 * @Date: 2021/10/26/21:40 * @Description: 具体产品1-- 汽车 */ public class ConcreteProduct1 extends Product1 { @Override public String method() { return "这里生产了汽车"; } } 复制代码
package com.jiangxia.Factory.SimpleFactory; /** * @Author: 江夏 * @Date: 2021/10/26/21:40 * @Description:具体产品2--电脑 */ public class ConcreteProduct2 extends Product1 { @Override public String method() { return "这里生产了电脑"; } } 复制代码
测试用例:
package com.jiangxia.Factory.SimpleFactory; /** * @Author: 江夏 * @Date: 2021/10/26/21:48 * @Description: 简单工厂的测试 */ public class SimpleFactoryTest { public static void main(String[] args) { Product1 product1 = Factory1.getProduct("汽车"); product1.method1(); System.out.println(product1.method()); Product1 product2 = Factory1.getProduct("电脑"); product2.method1(); System.out.println(product2.method()); } } 复制代码
运行后输出结果如下:
简单工厂模式的优点
通过上面的例子可以发现在简单工厂模式中工厂类含有必要的判断逻辑,可以决定在什么时候创建哪一个产品类的实例,客户端可以免除直接创建产品对象的责任,而仅仅消费产品;简单工厂模式通过这种做法实现了对责任的分割,它提供了专门的工厂类用于创建对象。所以客户端无须知道所创建的具体产品类的类名,只需要知道具体产品类所对应的参数即可,这样对于一些复杂的类名,通过简单工厂模式可以简化工作量。
也可以通过引入配置文件,在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。
简单工厂模式的缺点:
1、同理简单工厂模式存在一定的缺点,在工厂类集中了所有产品创建逻辑,那么工厂类有问题,整个系统都要受到影响。
2、还有一个问题就是在使用简单工厂模式会增加系统中类的个数,上面两个具体的就两个类了,如果其他的产品那么就得继续增加,这样就增加了系统的复杂度、理解难度以及代码量。
3、因为在工厂类中集中了所以产品的创建逻辑,那么如果需要添加新产品就不得不修改工厂逻辑,这样在产品类型较多时,有可能造成工厂逻辑过于复杂,不利于系统的扩展和维护。
4、简单工厂模式由于使用了静态工厂方法,造成工厂角色无法形成基于继承的等级结构。
应用场景
通过分析简单工厂方法的特点,我们就需要在合适的场景下使用简单工厂模式:
1、工厂类负责创建的对象比较少,因为一旦创建的对象较少,就不会造成工厂方法中的业务逻辑太过复杂,从而不利于拓展和维护了。
2、客户端如果只知道传入工厂类的参数,而对如何创建对象不关心,那么就可以使用简单工厂模式!
工厂方法模式
工厂方法模式(Factory Method Pattern)是粒度很小的设计模式,因为模式的表现只是一个抽象的方法。提前定义用于创建对象的接口,让子类决定实例化具体的某一个类,即在工厂和产品中间增加接口,工厂不再负责产品的创建,由接口针对不同条件返回具体的类实例,由具体类实例去实现。
通过上面的简单工厂模式的介绍,可以发现它违背了设计模式的开闭原则,就是对修改关闭,应当对拓展开放的原则,因为它不利于拓展。而工厂方法可以说是对简单工厂模式的进一步的抽象,其好处是可以使系统在不修改原来代码的情况下引进新的产品,即满足开闭原则。
可以理解为对之前的简单工厂进行优化,之前不管生产汽车还是电脑都是由工厂同一负责生产,而工厂方法则是将产品的创建过程由专门的工厂子类来完成。比如生产汽车,电脑,那么就需要定义一个抽象的工厂接口,再定义具体的汽车工厂类、电脑工厂类来分别生产汽车、电脑,它们实现在抽象工厂类中定义的方法即可。可以发现这种抽象化的结果就可以使得在不修改具体工厂类的情况下引进新的产品,如果出现新的产品类型,只需要为这种新类型的产品创建一个具体的工厂类就可以获得该新产品的实例,所以工厂方法模式具有超越简单工厂模式的优越性,更加符合了设计模式的开闭原则。
所以工厂方法模式应该包含以下几个部分:
抽象工厂(Abstract Factory):提供了创建产品的接口,调用者通过它访问具体工厂的工厂方法来创建产品。
具体工厂(ConcreteFactory):主要是实现抽象工厂中的抽象方法,创建具体产品。
抽象产品(Product):定义了产品的规范,描述了产品的主要特性和功能。
具体产品(ConcreteProduct):实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间一一对应。
看一个工厂方法的例子:
package com.jiangxia.Factory.FactoryMethod; /** * @Author: 江夏 * @Date: 2021/10/27/20:35 * @Description: */ public interface AbstractFactory { public AbstractProduct createProduct(); } 复制代码
package com.jiangxia.Factory.FactoryMethod; /** * @Author: 江夏 * @Date: 2021/10/27/20:37 * @Description:抽象产品 */ public interface AbstractProduct { public void over(); } 复制代码
package com.jiangxia.Factory.FactoryMethod; /** * @Author: 江夏 * @Date: 2021/10/27/20:41 * @Description:具体工厂 生产汽车 */ public class ConcreteFactory1 implements AbstractFactory{ @Override public AbstractProduct createProduct() { System.out.println("汽车工厂正在生产汽车"); return new ConcreteProduct1(); } } 复制代码
package com.jiangxia.Factory.FactoryMethod; /** * @Author: 江夏 * @Date: 2021/10/27/20:42 * @Description:电脑工厂 生产电脑 */ public class ConcreteFactory2 implements AbstractFactory{ @Override public AbstractProduct createProduct() { System.out.println("电脑工厂正在生产电脑"); return new ConcreteProduct2(); } } 复制代码
package com.jiangxia.Factory.FactoryMethod; /** * @Author: 江夏 * @Date: 2021/10/27/20:40 * @Description: 具体产品1 汽车 */ public class ConcreteProduct1 implements AbstractProduct{ @Override public void over() { System.out.println("汽车生产完成了"); } } 复制代码
package com.jiangxia.Factory.FactoryMethod; /** * @Author: 江夏 * @Date: 2021/10/27/20:40 * @Description:具体产品2 电脑 */ public class ConcreteProduct2 implements AbstractProduct{ @Override public void over() { System.out.println("电脑生产完成了"); } } 复制代码
测试代码:
package com.jiangxia.Factory.FactoryMethod; /** * @Author: 江夏 * @Date: 2021/10/27/20:33 * @Description:工厂方法测试 */ public class FactoryMethodTest { public static void main(String[] args) { AbstractFactory af1 = new ConcreteFactory1(); AbstractProduct cp1 = af1.createProduct(); System.out.println(cp1); cp1.over(); System.out.println("==============================="); AbstractFactory af2 = new ConcreteFactory2(); AbstractProduct cp2 = af2.createProduct(); System.out.println(cp2); cp2.over(); } } 复制代码
运行结果如下:
工厂方法模式的优点:
在工厂方法模式中,工厂方法用来创建指定的产品,同时隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。所以工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”,这一点优化了简单工厂模式。
工厂方法模式的缺点:
在创建新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,所以代码中类的个数将成对增加,增加了系统的复杂度和代码量,有更多的类需要编译和运行,会给系统带来一些额外的开销。
为了兼顾系统的可扩展性,引入了抽象层,代码中均使用抽象层进行定义,使得代码的理解难度增加,也增加了系统的实现难度。
而且抽象产品只能生产一种产品,这个缺点可以通过后面介绍的抽象工厂模式来解决。
应用场景
通过介绍了工厂方法的特点,可以总结出工厂方法设计模式的应用场景:
一个类不知道它所需要的对象的类:在工厂方法模式中,不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;只需要知道创建具体产品的工厂类。
一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
抽象工厂模式
抽象工厂模式(Abstract Factory)是当有多个抽象角色时使用的一种工厂模式。前面说到了工厂方法设计模式有个缺点,那就是抽象产品只能生产一种产品。而抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品对象。它有多个抽象产品类,每个抽象产品类可以派生出多个具体产品类,一个抽象工厂类,可以派生出多个具体工厂类,每个具体工厂类可以创建多个具体产品类的实例。
这里需要说明两个概念:
产品等级 :产品等级结构即产品的继承结构,如一个抽象类是电脑,其子类有小米电电脑、华为电脑、戴尔电脑等等,那么抽象电脑与具体品牌的电脑之间构成了一个产品等级结构,抽象电脑是父类,而具体品牌的电脑是其子类。
产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如小米公司生产的电脑、电冰箱,电视等,电视位于电视产品等级结构中,冰箱位于冰箱产品等级结构中。
所以可以这么理解:前面说的工厂方法设计模式考虑的是同等级的产品的生产,比如车厂只生产汽车,电器厂只生产电脑等等。显然这样是不符合实际情况的。因为比如格力它不仅仅生产冰箱,还生产空调,还生产洗衣机等等。抽象工厂模式考虑的就是这种多等级产品的生产。
抽象工厂模式可以理解为工厂方法模式的升级版,所以它的主要部分也和工厂方法一样,包含了:抽象工厂、具体工厂、抽象产品和具体产品。但它们又和工厂方法的功能有些不一样:
抽象工厂:提供了创建产品的接口,它包含多个创建产品的方法 newProduct(),可以创建多个不同等级的产品。
具体工厂:主要是实现抽象工厂中的多个抽象方法,完成具体产品的创建。
抽象产品:定义了产品的规范,描述了产品的主要特性和功能,抽象工厂模式有多个抽象产品。
具体产品:实现了抽象产品角色所定义的接口,由具体工厂来创建,它同具体工厂之间是多对一的关系。
栗子:
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/21:48 * @Description:工厂抽象类 */ public abstract class AbstractFactory { public abstract Computer createComputer(String computerCompanyName); public abstract Car createCar(String carCompanyName) ; } 复制代码
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/22:05 * @Description: */ public class FactoryProduce { public static AbstractFactory createFactory(String choice){ if(choice.equalsIgnoreCase("computer")){ return new ComputerFactory(); } else if(choice.equalsIgnoreCase("car")){ return new CarFactory(); } return null; } } 复制代码
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/22:00 * @Description: */ public class ComputerFactory extends AbstractFactory { @Override public Computer createComputer(String computerCompanyName) { if(computerCompanyName == null){ return null; } if(computerCompanyName.equalsIgnoreCase("XiaoMi")){ return new XiaoMi(); } else if(computerCompanyName.equalsIgnoreCase("HuaWei")){ return new HuaWei(); } return null; } @Override public Car createCar(String carCompanyName) { return null; } } 复制代码
package com.jiangxia.Factory.AbstractFactory; import javafx.scene.shape.Circle; /** * @Author: 江夏 * @Date: 2021/10/27/21:56 * @Description: */ public class CarFactory extends AbstractFactory { @Override public Computer createComputer(String computerCompanyName) { return null; } @Override public Car createCar(String carCompanyName) { if(carCompanyName == null){ return null; } if(carCompanyName.equalsIgnoreCase("Audi")){ return new Audi(); } else if(carCompanyName.equalsIgnoreCase("BYD")){ return new BYD(); } return null; } } 复制代码
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/21:50 * @Description:电脑接口 */ public interface Computer { void over(); } 复制代码
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/21:51 * @Description:汽车接口 */ public interface Car { void over(); } 复制代码
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/21:53 * @Description: */ public class HuaWei implements Computer { @Override public void over() { System.out.println("华为电脑"); } } 复制代码
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/21:52 * @Description:小米 */ public class XiaoMi implements Computer{ @Override public void over() { System.out.println("小米电脑"); } } 复制代码
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/21:54 * @Description:Audi */ public class Audi implements Car { @Override public void over() { System.out.println("奥迪汽车"); } } 复制代码
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/21:55 * @Description: */ public class BYD implements Car { @Override public void over() { System.out.println("比亚迪汽车"); } } 复制代码
测试代码:
package com.jiangxia.Factory.AbstractFactory; /** * @Author: 江夏 * @Date: 2021/10/27/22:04 * @Description: */ public class AbstractFactoryTest { public static void main(String[] args) { //获取电脑工厂 AbstractFactory cFactory = FactoryProduce.createFactory("computer"); //获取公司为小米的电脑 Computer computer1 = cFactory.createComputer("XiaoMi"); //调用小米电脑的over方法 computer1.over(); //获取公司为华为的电脑 Computer computer2 = cFactory.createComputer("HuaWei"); //调用华为电脑的over方法 computer2.over(); //获取汽车工厂 AbstractFactory carFactory = FactoryProduce.createFactory("car"); //获取公司为audi的汽车 Car car1 = carFactory.createCar("Audi"); //调用奥迪的over方法 car1.over(); //获取公司为byd的汽车 Car car2 = carFactory.createCar("BYD"); //调用比亚迪的over方法 car2.over(); } } 复制代码
运行结果如下:
抽象工厂模式的优点:
抽象工厂模式隔离了具体类的生成,使得客户并不需要知道什么被创建。由于这种隔离,更换一个具体工厂就变得相对容易。所有的具体工厂都实现了抽象工厂中定义的那些公共接口,因此只需改变具体工厂的实例,就可以在某种程度上改变整个软件系统的行为。另外,应用抽象工厂模式可以实现高内聚低耦合的设计目的,因此抽象工厂模式得到了广泛的应用。
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象。这对一些需要根据当前环境来决定其行为的软件系统来说,是一种非常实用的设计模式。
增加新的具体工厂和产品族很方便,无须修改已有系统,符合“开闭原则”。
抽象工厂模式的缺点
在添加新的产品对象时,难以扩展抽象工厂来生产新种类的产品,这是因为在抽象工厂角色中规定了所有可能被创建的产品集合,要支持新种类的产品就意味着要对该接口进行扩展,而这将涉及到对抽象工厂角色及其所有子类的修改,显然会带来较大的不便。
开闭原则的倾斜性(增加新的工厂和产品族容易,增加新的产品等级结构麻烦)。
应用场景
在以下情况下可以使用抽象工厂模式:
一个系统不应当依赖于产品类实例如何被创建、组合和表达的细节,这对于所有类型的工厂模式都是重要的。
系统中有多于一个的产品族,而每次只使用其中某一产品族。
属于同一个产品族的产品将在一起使用,这一约束必须在系统的设计中体现出来。
系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现。
总结
通过上述的代码有没有发现,工厂模式的效果和利用父类的向下转型(使用父类类型的引用指向子类的对象)几乎一样,把指向子类对象的父类引用赋给子类引用叫做向下转型,如:
Class Audi extends Car Car c = new Audi (); c = (Audi)Car ; 复制代码
那为什么还要用工厂模式呢?
使用向下转型在客户端实例化子类的时候,严重依赖具体的子类的名字。当我们需要更改子类的构造方法的时候,比如增加一个参数,或者更改了子类的类名,所有的new出来的子类都需要跟着更改。
但如果我们使用工厂模式,我们仅仅需要在工厂中修改一下new的代码,其余项目中用到此实例的都会跟着改,而不需要我们手动去操作。
无论是简单工厂模式、工厂模式还是抽象工厂模式,它们本质上都是将不变的部分提取出来,将可变的部分留作接口,以达到最大程度上的复用。究竟用哪种设计模式更适合,这要根据具体的业务需求来决定。