SpringBoot中使⽤注解实现简单⼯⼚模式
前⾔
从设计模式的类型上来说,简单⼯⼚模式是属于创建型模式,⼜叫静态⼯⼚模式(Simple Factory Pattern),但不属于23种GOF设计模式之⼀。简单⼯⼚模式是由⼀个⼯⼚对象决定创建出接⼝哪⼀种实现类的实例。简单⼯⼚模式是⼯⼚模式家族中最简单实⽤的模式,可以理解为是不同⼯⼚模式的⼀个特殊实现。
在简单⼯⼚模式中,可以根据参数的不同返回不同类的实例。简单⼯⼚模式专门定义⼀个类来负责创建其他类的实例,被创建的实例通常都具有共同的⽗类。
简单⼯⼚模式只需要向⼯⼚类传⼊⼀个正确的参数,就可以获取所需要的对象,⽽⽆需知道其实现过程。
简单⼯⼚模式是在什么场景下使⽤呢?这⾥简单的枚举⼏个使⽤场景:
1、⽇志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,⽤户可以选择记录⽇志到什么地⽅;
2、保险产品配置:保险产品往往有很多赔付等级,每个等级对应不同的保额和赔付⾦额;
3、设计⼀个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现⼀个
接⼝。
定义中最重要的⼀句话就是,由⼀个⼯⼚对象决定创建出哪⼀种实现类的实例。在⽂章《》中,⼩编介绍了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 {
springboot结构@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 = (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 = Shape("circle");
//调⽤ Circle 的 draw ⽅法
shapeInterface1.draw();
Shape shapeInterface2 = Shape("rectangle");
shapeInterface2.draw();
}
@Autowired
private ShapeBeanFactory factory; // 使⽤注解注⼊
private void shapeBeanFactoryDraw() {
System.out.println("======= 实现⼆ shapeBeanFactory =======");
Shape shapeInterface1 = Shape("circle");
shapeInterface1.draw();
Shape shapeInterface2 = Shape("square");
shapeInterface2.draw();
}
客户端请求draw函数,控制台输出结果如下,说明对⼯⼚类重构成功。
======= shapeFactory =======
Inside Circle::draw() method.
Inside Rectangle::draw() method.
======= 实现⼆ shapeBeanFactory =======
Inside Circle::draw() method.
Inside Square::draw() method.
优缺点和适⽤场景
优点
⼯⼚类是整个模式的关键。包含了必要的逻辑判断,根据外界给定的信息,决定究竟应该创建哪个具体类的对象。通过使⽤⼯⼚类,外界可以从直接创建具体产品对象的尴尬局⾯摆脱出来,仅仅需要负责“消费”对象就可以了,⽽不必管这些对象究竟如何创建及如何组织的,只需要知道具体产品类所对应的参数即可,对于⼀些复杂的类名,通过简单⼯⼚模式可以减少使⽤者的记忆量。明确了各⾃的职责和权利,有利于整个软件体系结构的优化。
通过引⼊配置⽂件,可以在不修改任何客户端代码的情况下更换和增加新的具体产品类,在⼀定程度上提⾼了系统的灵活性。
缺点
由于⼯⼚类集中了所有实例的创建逻辑,违反了⾼内聚责任分配原则,将全部创建逻辑集中到了⼀个⼯⼚类中;它所能创建的类只能是事先考虑到的,如果需要添加新的类,则就需要改变⼯⼚类了。
当系统中的具体产品类不断增多时候,可能会出现要求⼯⼚类根据不同条件创建不同实例的需求。这种对条件和具体产品类型的判断交错在⼀起,很难避免模块功能的蔓延,对系统的维护和扩展⾮常不利;
违背开闭原则。对于上⾯两种简单⼯⼚模式的实现⽅法,如果我们要添加新的⼏何图形,势必要改动
ShapeFactory的代码,那这是不是违反开闭原则呢?实际上,如果不需要频繁地添加新的⼏何图形实现类,只是偶尔修改⼀下代码,稍微不符合开闭原则,但权衡扩展性和可读性,这样的代码实现在⼤多数情况下,也是可以接受的。这是第⼀种实现⽅案的瑕疵,但是,第⼆种实现⽅案就没有违背开闭原则,完全由Spring IOC容器管理Bean,彰显了Spring IOC容器的强⼤之处。
适⽤场景
在以下场景适合使⽤简单⼯⼚模式:
⼯⼚类负责创建的对象⽐较少:由于创建的对象较少,不会造成⼯⼚⽅法中的业务逻辑太过复杂。
客户端只知道传⼊创建⼯⼚类所需的参数,对于如何创建对象不关⼼:客户端既不需要关⼼创建细节,甚⾄连类名都不需要记住,只需要知道类型所对应的参数。
Reference

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。