Spring Boot中利用注解实现工厂模式
在软件设计中,工厂模式是一种常用的创建型模式,它能够根据传入的参数动态地创建不同类的实例。简单工厂模式(Simple Factory Pattern),有时也被称为静态工厂方法模式,虽然不属于GoF的23种设计模式,但因其简洁和实用而广受欢迎。其核心思想是由一个专门的工厂类负责根据客户端提供的标识来决定并实例化哪个具体的产品类。
简单工厂模式的优势在于,它将对象的创建逻辑集中管理,客户端只需知道如何请求对象,而无需关心对象的具体创建过程和实现细节。这在多种场景下都能发挥作用,例如:
- 日志系统:根据用户选择将日志输出到文件、数据库或网络。
- 产品配置:根据不同参数生成不同配置的产品。
- 协议框架:根据指定的协议类型(如HTTP, FTP)创建相应的连接对象。
Spring框架,特别是Spring Boot,提供了强大的依赖注入(DI)和注解功能,可以极大地简化工厂模式的实现。通过结合Spring的Bean管理和注解,我们可以构建出更加灵活和易于维护的工厂。
简单工厂模式的实现
我们将以一个图形绘制的例子来演示。首先,定义一个通用的图形接口 Shape:
/**
* 图形接口
*/
public interface Shape {
void draw();
}
接着,创建几个实现该接口的具体图形类,并使用 @Service 注解将它们注册为Spring Bean:
@Service("rectangle") // 为Bean指定一个名称,方便后续按名称获取
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("绘制矩形");
}
}
@Service("square")
public class Square implements Shape {
@Override
public void draw() {
System.out.println("绘制正方形");
}
}
@Service("circle")
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("绘制圆形");
}
}
实现方式一:传统的静态工厂方法
这种方式不依赖Spring的Bean管理,通过一个静态方法根据传入的字符串参数创建并返回具体的Shape实例。这种方法虽然简单,但如果需要添加新的图形类型,就需要修改工厂类本身。
public class TraditionalShapeFactory {
/**
* 根据形状类型获取对应的Shape实例
*/
public static Shape getShape(String shapeType) {
if ("circle".equalsIgnoreCase(shapeType)) {
return new Circle();
} else if ("rectangle".equalsIgnoreCase(shapeType)) {
return new Rectangle();
} else if ("square".equalsIgnoreCase(shapeType)) {
return new Square();
}
return null; // 或抛出异常
}
}
实现方式二:基于Spring注解的工厂
这种方式充分利用了Spring的DI能力。我们创建一个Bean工厂类,使用 @Autowired 将所有 Shape 接口的实现类(Spring Bean)自动注入到一个Map中。Map的键通常是Bean的名称(在 @Service 注解中指定,或Spring自动生成的默认名称),值则是对应的实现类实例。
@Component
public class SpringShapeBeanFactory {
// Spring 会将所有 Shape 接口的实现类注入到这个 Map 中
// Map 的 key 是 Bean 的名称 (例如 "circle", "rectangle", "square")
private final Map<String, Shape> shapeMap;
@Autowired
public SpringShapeBeanFactory(Map<String, Shape> shapeMap) {
this.shapeMap = shapeMap;
}
public Shape getShape(String shapeType) {
// 直接从Map中根据名称获取对应的Bean
Shape shape = shapeMap.get(shapeType);
if (shape == null) {
System.err.println("未找到类型为 " + shapeType + " 的图形 Bean");
// 可以选择返回null,或抛出异常
}
return shape;
}
}
与第一种方式相比,基于Spring注解的实现更加简洁,避免了硬编码的 if-else 逻辑,并且更符合Spring的开发习惯。
客户端调用
现在,我们创建一个Controller来演示如何调用这两种工厂:
@RestController
public class ShapeController {
// 注入 Spring 提供的 ShapeBeanFactory
@Autowired
private SpringShapeBeanFactory springShapeBeanFactory;
@GetMapping("/drawShapes")
public String drawShapes() {
System.out.println("--- 使用传统静态工厂 ---");
// 调用传统工厂
Shape circleFromTraditional = TraditionalShapeFactory.getShape("circle");
if (circleFromTraditional != null) {
circleFromTraditional.draw();
}
Shape rectangleFromTraditional = TraditionalShapeFactory.getShape("rectangle");
if (rectangleFromTraditional != null) {
rectangleFromTraditional.draw();
}
System.out.println("\n--- 使用Spring注解工厂 ---");
// 调用Spring工厂
Shape circleFromSpring = springShapeBeanFactory.getShape("circle");
if (circleFromSpring != null) {
circleFromSpring.draw();
}
Shape squareFromSpring = springShapeBeanFactory.getShape("square");
if (squareFromSpring != null) {
squareFromSpring.draw();
}
return "图形绘制完成,请查看控制台输出";
}
}
当客户端访问 /drawShapes 接口时,控制台会输出类似以下内容:
--- 使用传统静态工厂 ---
绘制圆形
绘制矩形
--- 使用Spring注解工厂 ---
绘制圆形
绘制正方形
优缺点与适用场景
优点
- 封装性强:将对象的创建逻辑集中到工厂类中,客户端无需了解具体的实现类和创建过程,只需知道如何请求。
- 易于扩展(Spring方式):通过Spring注解管理,添加新的图形实现类,只需将其注册为Bean,工厂类无需修改。
- 提高代码可读性:隐藏了复杂的对象创建逻辑,使客户端代码更简洁。
- 降低耦合:客户端依赖于工厂接口,而不是具体的实现类。
缺点
- 违反开闭原则(传统方式):当需要添加新的产品类型时,必须修改工厂类。
- 职责过重:传统工厂类可能集中了过多的创建逻辑,当产品种类繁多时,工厂类会变得臃肿。
- Spring方式的限制:如果不使用Spring,则无法享受到其带来的便利。
适用场景
- 当创建对象有复杂的逻辑时。
- 当客户端只关心对象的功能,而不需要知道其具体实现细节时。
- 当需要根据不同的条件返回不同的实例时。
- 在Spring Boot中,当需要动态地根据某种标识选择并注入某个接口的实现类时,基于Spring注解的工厂方式是首选。