工厂模式(Factory Pattern)是Java中最常用的设计模式之一,这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。工厂模式提供了一种将对象的实例化过程封装在工厂类中的方式。通过使用工厂模式,可以将对象的创建与使用代码分离,提供一种统一的接口来创建不同类型的对象。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
介绍
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:主要解决接口选择的问题。
何时使用:我们明确地计划不同条件下创建不同实例时。
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
应用实例:
-
您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
-
Hibernate 换数据库只需换方言和驱动就可以。
-
java.util.Calendar, ResourceBundle and NumberFormat getInstance() 使用了工厂方法模式
-
valueOf() 在包装类中,如Boolean, Integer 也使用了工厂方法模式
工厂设计模式的优点:
-
面向接口编程,体现了面向对象的思想
-
将创建对象的工作转移到了工厂类
-
一个调用者想创建一个对象,只要知道其名称就可以了
-
扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以
-
屏蔽产品的具体实现,调用者只关心产品的接口
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景:
-
日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
-
数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。
-
设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项:作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度
工厂模式包含以下几个核心角色:
-
抽象产品(Abstract Product):定义了产品的共同接口或抽象类。它可以是具体产品类的父类或接口,规定了产品对象的共同方法
-
具体产品(Concrete Product):实现了抽象产品接口,定义了具体产品的特定行为和属性
-
抽象工厂(Abstract Factory):声明了创建产品的抽象方法,可以是接口或抽象类。它可以有多个方法用于创建不同类型的产品
-
具体工厂(Concrete Factory):实现了抽象工厂接口,负责实际创建具体产品的对象
实现
我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory。FactoryPatternDemo 类使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(CIRCLE / RECTANGLE / SQUARE),以便获取它所需对象的类型。
步骤一:
创建一个接口:
public interface Shape {
void draw();
}
步骤二:
创建实现接口的实体类:
Rectangle.java
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
Square.java
public class Square implements Shape {
@Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
Circle.java
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}
步骤三:
创建一个工厂,生成基于给定信息的实体类的对象ShapeFactory.java
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
步骤四:
使用该工厂,通过传递类型信息来获取实体类的对象FactoryPatternDemo.java
public class FactoryPatternDemo {
public static void main(String[] args) {
ShapeFactory shapeFactory = new ShapeFactory();
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = shapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = shapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = shapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
}
输出结果:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
上述是通过接口的方式进行演示工厂模式的,也叫做简单工厂模式。
如果将其ShapeFactory.java中的public Shape getShape(String shapeType)方法添加static修饰符的话,如下代码
public class ShapeFactory {
//使用 getShape 方法获取形状类型的对象
public static Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
if(shapeType.equalsIgnoreCase("CIRCLE")){
return new Circle();
} else if(shapeType.equalsIgnoreCase("RECTANGLE")){
return new Rectangle();
} else if(shapeType.equalsIgnoreCase("SQUARE")){
return new Square();
}
return null;
}
}
改造FactoryPatternDemo.java的main为代码如下:
...
public static void main(String[] args) {
//获取 Circle 的对象,并调用它的 draw 方法
Shape shape1 = ShapeFactory.getShape("CIRCLE");
//调用 Circle 的 draw 方法
shape1.draw();
//获取 Rectangle 的对象,并调用它的 draw 方法
Shape shape2 = ShapeFactory.getShape("RECTANGLE");
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 Square 的对象,并调用它的 draw 方法
Shape shape3 = ShapeFactory.getShape("SQUARE");
//调用 Square 的 draw 方法
shape3.draw();
}
...
输出结果是一样的,上述是通过接口的方式进行演示工厂模式的,也叫做静态工厂模式。
工厂模式之工厂方法模式
话不多说直接上代码:
步骤一:
创建ShapeFactory.java
public interface ShapeFactory {
Shape draw();
}
分别创建CircleFactory.java、RectangleFactory.java、SquareFactory.java。代码分别如下:
public class CircleFactory implements ShapeFactory {
@Override
public Shape draw() {
return new Circle();
}
}
public class RectangleFactory implements ShapeFactory {
@Override
public Shape draw() {
return new Rectangle();
}
}
public class SquareFactory implements ShapeFactory {
@Override
public Shape draw() {
return new Square();
}
}
改造FactoryPatternDemo.java的main为代码如下:
...
public static void main(String[] args) {
//获取 CircleFactory 的对象,并调用它的 draw 方法
Shape shape1 = new CircleFactory().draw();
//调用 Circle 的 draw 方法
shape1.draw();
//获取 RectangleFactory 的对象,并调用它的 draw 方法
Shape shape2 = new RectangleFactory().draw();
//调用 Rectangle 的 draw 方法
shape2.draw();
//获取 SquareFactory 的对象,并调用它的 draw 方法
Shape shape3 = new SquareFactory().draw();
//调用 Square 的 draw 方法
shape3.draw();
}
...
输出结果:
Inside Circle::draw() method.
Inside Rectangle::draw() method.
Inside Square::draw() method.
这里需要注意:
工厂方法模式解决简单工厂模式是需要付出代价的,每新增一个Shape就需要增加,对应新的工厂,会发现需要花费很大的成本,现在才三个新的Shape,那么等到十个、一百个的时候就会变得更加的复杂和难以维护。
工厂方法模式这里还有一个点:看上面工厂方法的消费者代码示例里用到new是我们耦合的最大元凶,可以看看这篇文章里写的,利用反射对工厂模式的解耦操作;
Java高级特性——通过反射获取类的几种方式——并利用反射对工厂模式解耦操作
https://blog.csdn.net/A_hxy/article/details/105992426
总结:
第一、二个工厂模式:在实际去开发中会发现比较常用,尽管上面讲了不符合(面向对象)OOP原则。
第三个工厂方法模式:不修改工厂类的前提,也就是说不修改已有类,实现对扩展是开发,对修改关闭。
注:本文内容主要参考了
菜鸟教程之设计模式章节
https://www.runoob.com/design-pattern/factory-pattern.html
Java设计模式:工厂模式——图文+代码示例(通俗易懂)
https://blog.csdn.net/A_hxy/article/details/105758657
技术不分好坏,合适的地方使用合适的技术;优雅的代码,清晰的逻辑,给你的每一个项目更好的归宿。