前言

从设计模式的类型上来说,简单工厂模式是属于创建型模式,又叫静态工厂模式(Simple Factory Pattern),但不属于23种GOF设计模式之一。简单工厂模式是由一个工厂对象决定创建出接口哪一种实现类的实例。简单工厂模式是工厂模式家族中最简单实用的模式,可以理解为是不同工厂模式的一个特殊实现。

在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

简单工厂模式只需要向工厂类传入一个正确的参数,就可以获取所需要的对象,而无需知道其实现过程。

  简单工厂模式是在什么场景下使用呢?这里简单的枚举几个使用场景:

1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方;

2、保险产品配置:保险产品往往有很多赔付等级,每个等级对应不同的保额和赔付金额;

3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。

定义中最重要的一句话就是,由一个工厂对象决定创建出哪一种实现类的实例。在文章《Spring注解之@Autowired:注入Arrays, Collections, and Maps》中,小编介绍了Spring Boot如何把某些接口实现类的Bean注入到Map和List等集合中,方便应用的时候直接读取需要的bean。结合@Autowired可以自动注入指定接口实现类到Map中,介绍简单工厂模式的一个实现策略。

简单工厂模式实践

我们将创建一个 Shape 接口和实现 Shape 接口的实体类。下一步是定义工厂类 ShapeFactory。在controller FactoryPatternDemo 中,我们演示使用 ShapeFactory 来获取 Shape 对象。它将向 ShapeFactory 传递信息(circle/rectangle/square),以便获取它所需对象的类型。

新增接口类Shape:

/**
* 定义bean接口
*/
public interface Shape {
void draw();
}

创建Shape的三个实现类,并且都添加注解@Service,将其注册为Spring Bean。

@Service
public class Rectangle implements Shape { @Override
public void draw() {
System.out.println("Inside Rectangle::draw() method.");
}
}
=========== 我是分割线 =============
@Service
public class Square implements Shape { @Override
public void draw() {
System.out.println("Inside Square::draw() method.");
}
}
=========== 我是分割线 =============
@Service
public class Circle implements Shape { @Override
public void draw() {
System.out.println("Inside Circle::draw() method.");
}
}

实现方法一:基于新建对象实现

兹创建一个工厂类,根据调用者的不同,创建不同的实现类并返回。而如果碰到不合法的请求参数,则会返回null。

/**
* 简单工厂类,通过新建对象实现
* 不添加Spring注解,调用的时候直接new
*/
public class ShapeFactory {
/**
* 使用 getShape 方法获取几何图形类型的对象
*/
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注解实现

下面新增一个类ShapeBeanFactory,通过@Autowired注解把bean注入map后,由变量map替换工厂类,实现简单工厂模式。

/**
* 简单工厂类,通过 Spring注解 @Autowired 和 @Service 实现
*/
@Component // 添加Spring注解
public class ShapeBeanFactory { @Autowired
private Map<String, Shape> shapeMap; public Shape getShape(String shapeType) {
Shape bean1 = shapeMap.get(shapeType);
System.out.println(bean1);
return bean1;
} }

通过对比两种实现方案可以发现,使用Spring注解的方式更加简洁,避开了 if else 分支。

客户端调用工厂类

创建一个controller,命名为FactoryPatternDemo,并把两种实现方案封装为不同的私有方法。客户端调用工厂类,传入几何图形类型参数获取几何图形对象并调用该对象的draw方法:

 @GetMapping("/drawMyShape")
public String drawMyShape(){
shapeFactoryDraw();
shapeBeanFactoryDraw();
return "成功";
}
private void shapeFactoryDraw() {
System.out.println("======= shapeFactory ======="); //获取 Circle 的对象,并调用它的 draw 方法
Shape shapeInterface1 = ShapeFactory.getShape("circle");
//调用 Circle 的 draw 方法
shapeInterface1.draw(); Shape shapeInterface2 = ShapeFactory.getShape("rectangle");
shapeInterface2.draw();
} @Autowired
private ShapeBeanFactory factory; // 使用注解注入 private void shapeBeanFactoryDraw() {
System.out.println("======= 实现二 shapeBeanFactory =======");
Shape shapeInterface1 = factory.getShape("circle");
shapeInterface1.draw(); Shape shapeInterface2 = factory.getShape("square");
shapeInterface2.draw();
}

客户端请求draw函数,控制台输出结果如下,说明对工厂类重构成功。

======= shapeFactory =======
Inside Circle::draw() method.
Inside Rectangle::draw() method.
======= 实现二 shapeBeanFactory =======
com.eg.wiener.service.impl.shape.Circle@3a0aa6f6
Inside Circle::draw() method.
com.eg.wiener.service.impl.shape.Square@6d48c8f9
Inside Square::draw() method.

优缺点和适用场景

优点

工厂类是整个模式的关键。包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过使用工厂类,外界可以从直接创建具体产品对象的尴尬局面摆脱出来,仅仅需要负责“消费”对象就可以了,而不必管这些对象究竟如何创建及如何组织的,只需要知道具体产品类所对应的参数即可,对于一些复杂的类名,通过简单工厂模式可以减少使用者的记忆量。明确了各自的职责和权利,有利于整个软件体系结构的优化。

通过引入配置文件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在一定程度上提高了系统的灵活性。

缺点

由于工厂类集中了所有实例的创建逻辑,违反了高内聚责任分配原则,将全部创建逻辑集中到了一个工厂类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变工厂类了。

当系统中的具体产品类不断增多时候,可能会出现要求工厂类根据不同条件创建不同实例的需求。这种对条件和具体产品类型的判断交错在一起,很难避免模块功能的蔓延,对系统的维护和扩展非常不利;

违背开闭原则。对于上面两种简单工厂模式的实现方法,如果我们要添加新的 几何图形,势必要改动 ShapeFactory的代码,那这是不是违反开闭原则呢?实际上,如果不需要频繁地添加新的几何图形实现类,只是偶尔修改一下代码,稍微不符合开闭原则,但权衡扩展性和可读性,这样的代码实现在大多数情况下,也是可以接受的。这是第一种实现方案的瑕疵,但是,第二种实现方案就没有违背开闭原则,完全由Spring IOC容器管理Bean,彰显了Spring IOC容器的强大之处。

适用场景

在以下场景适合使用简单工厂模式:

工厂类负责创建的对象比较少:由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

客户端只知道传入创建工厂类所需的参数,对于如何创建对象不关心:客户端既不需要关心创建细节,甚至连类名都不需要记住,只需要知道类型所对应的参数。

Reference

https://www.runoob.com/design-pattern/factory-pattern.html

http://www.cnblogs.com/java-my-life/archive/2012/03/22/2412308.html

https://www.jianshu.com/p/5cb52d84bd6d

Spring Boot中使用注解实现简单工厂模式的更多相关文章

  1. Spring Boot中@Scheduled注解的使用方法

    Spring Boot中@Scheduled注解的使用方法 一.定时任务注解为@Scheduled,使用方式举例如下 //定义一个按时间执行的定时任务,在每天16:00执行一次. @Scheduled ...

  2. spring boot 中@Autowired注解无法自动注入的错误

    版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/huihuilovei/article/de ...

  3. Spring Boot中的注解

    文章来源:http://www.tuicool.com/articles/bQnMra 在Spring Boot中几乎可以完全弃用xml配置文件,本文的主题是分析常用的注解. Spring最开始是为了 ...

  4. Spring Boot中自定义注解+AOP实现主备库切换

    摘要: 本篇文章的场景是做调度中心和监控中心时的需求,后端使用TDDL实现分表分库,需求:实现关键业务的查询监控,当用Mybatis查询数据时需要从主库切换到备库或者直接连到备库上查询,从而减小主库的 ...

  5. Spring Boot中使用redis的发布/订阅模式

    原文:https://www.cnblogs.com/meetzy/p/7986956.html redis不仅是一个非常强大的非关系型数据库,它同时还拥有消息中间件的pub/sub功能,在sprin ...

  6. Spring Boot中@ConfigurationProperties注解实现原理源码解析

    0. 开源项目推荐 Pepper Metrics是我与同事开发的一个开源工具(https://github.com/zrbcool/pepper-metrics),其通过收集jedis/mybatis ...

  7. Spring Boot 中 @SpringBootApplication注解背后的三体结构探秘

    概 述 SpringBoot 约定大于配置 的功力让我们如沐春风,在我之前写的文章<从SpringBoot到SpringMVC> 也对比过 SpringBoot 和 SpringMVC 这 ...

  8. Spring Boot中的缓存支持(一)注解配置与EhCache使用

    Spring Boot中的缓存支持(一)注解配置与EhCache使用 随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决 ...

  9. Spring Boot中使用缓存

    Spring Boot中使用缓存 随着时间的积累,应用的使用用户不断增加,数据规模也越来越大,往往数据库查询操作会成为影响用户使用体验的瓶颈,此时使用缓存往往是解决这一问题非常好的手段之一. 原始的使 ...

  10. Spring Boot中快速操作Mongodb

    Spring Boot中快速操作Mongodb 在Spring Boot中集成Mongodb非常简单,只需要加入Mongodb的Starter包即可,代码如下: <dependency> ...

随机推荐

  1. C# fleck websocket使用

    转载于:https://www.itspeeding.com/article/28 1.web页面 1 <html lang="en" xmlns="http:// ...

  2. SpringBoot整合Dubbox(无XML配置)

    简介 Dubbox是当当网对阿里的Dubbo进行增强的一个分支.在使用springboot之后,我们发现很多配置并不一定要使用xml.这篇文章的目的是让你使用Dubbox时能像使用springboot ...

  3. 『Plotly实战指南』--折线图绘制进阶篇

    上一篇介绍了Plotly绘制折线图的基础知识和数据预处理的技巧, 本文将重点探讨如何利用Plotly实现多线折线图的布局设计以及动态折线图的实现, 让我们一起掌握进阶的折线图绘制技巧. 1. 多折线图 ...

  4. Joker 可视化开发平台全局方法使用指南

    在 Joker 可视化开发平台中,全局方法是实现公共业务逻辑的有力工具,它能跨越组件和页面文件的界限,让开发者快速调用,显著提升开发效率.下面将详细介绍全局方法在平台中的使用方式. 一.全局方法的定义 ...

  5. SCRAPY入门学习(待完善)

    Scrapy介绍 Scrapy 是用 Python 实现的一个为了爬取网站数据.提取结构性数据而编写的应用框架. Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中. 通常我们 ...

  6. 前端必备的 CSS 库,normalize.css

    这是一个小 CSS 样式表,是著名的库,作为 CSS 基础样式的一部分,可消除客户端渲染不一致问题. 地址是 https://necolas.github.io/normalize.css/ 别小看这 ...

  7. java的反射是要先实例化的!

    java两种获得反射的方法 ,一种是Class.forName("A"); 另一种是 A a = new A(); a.getClass(); 第二种是自己实例化之后,我们在类的静 ...

  8. restful 服务器一个问题,看ChatGPT的威力 (续)

    资料很多,但是真正能经得住7X24运行的还真不容易.说穿了就是你的程序不够健壮. 玩数据处理的,也就是:数据库连接 → 查询 → 拉数据 → 生成结果 → 释放连接 → 返回数据 .可是看下面: FD ...

  9. 移除任务栏右端"显示桌面"按钮-AutoIt

    核心代码 $hwnd = WinGetHandle("[CLASS:Shell_TrayWnd]", "") ControlHide($hwnd, " ...

  10. FastAPI中实现动态条件必填字段的实践

    title: FastAPI中实现动态条件必填字段的实践 date: 2025/04/03 00:06:20 updated: 2025/04/03 00:06:20 author: cmdragon ...