工厂模式:创建型设计模式
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:接口选择的问题。
何时使用:我们明确计划不同条件下创建不同实例。
如何解决:让子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:创建过程在其子类执行。
优点:
1.一个调用者想要创建一个对象,只需要知道其名称即可。
2.扩展性高,如果想增加一个产品,只要扩展一个工厂类即可。
3.屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:
每次增加一个产品,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
应用场景:
1.日志记录器:记录可能记录到本地磁盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2.数据库访问:当用户不知道系统采用哪一类数据库,以及数据库可能有变化时
3.设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。
代码示例开始
工厂接口:
package com.yuxx.factory;
/**
* 图形接口
*/
public interface Shape {
void draw();
}
具体产品类:
package com.yuxx.factory;
/**
* 长方形实现类
*/
public class Rectangle implements Shape {
@Override
public void draw() {
System.out.println("draw a rectangle");
}
}
package com.yuxx.factory;
/**
* 正方形实现类
*/
public class Square implements Shape {
@Override
public void draw() {
System.out.println("draw a square");
}
}
package com.yuxx.factory;
/**
* 圆形实现类
*/
public class Circle implements Shape {
@Override
public void draw() {
System.out.println("draw a circle");
}
}
工厂类:
package com.yuxx.factory;
public class ShapeFactory {
public Shape getShape(String shapeType){
if(shapeType == null){
return null;
}
switch (shapeType){
case "CIRCLE":
return new Circle();
case "RECTANGLE":
return new Rectangle();
case "SQUARE":
return new Square();
default:
return null;
}
}
}
测试调用类:
package com.yuxx.factory;
import org.junit.Test;
public class FactoryPatternDemo {
@Test
public void test(){
ShapeFactory shapeFactory = new ShapeFactory();
//获取圆形的对象,并调用draw方法
Shape circle = shapeFactory.getShape("CIRCLE");
circle.draw();
//获取长方形的对象,并调用draw方法
Shape rectangle = shapeFactory.getShape("RECTANGLE");
rectangle.draw();
//获取正方形的对象,并调用draw方法
Shape square = shapeFactory.getShape("SQUARE");
square.draw();
}
}
由上面代码可以看到,工厂模式的实现逻辑就是:先创建一个工厂接口类,然后由不同的产品类去实现这个工厂接口。然后创建一个工厂类,工厂类中根据产品名称分别实现不同产品对象的构造方法。调用时,也是需要先创建工厂类的对象,然后根据产品名称创建产品类对象,并进行方法调用。
在学习工厂模式之前,在工作中遇到一个需求,我觉得正好可以用工厂模式。需求是这样的:
这个业务流程是关于体育赛事报名的,困难之处在于,每个比赛项目的运动员报名人数限制和工作人员人数等等都有限制(实际报名规定一两句说不清,此处也不赘述,总之比较复杂)。这些限制都是需要再报名记录进入数据库之前一一进行判断。按照以往的做法,就是流水式写法,根据用户的要求一点一点把报名规定的要求加上。这样的后果就是,代码机构冗余复杂,可读性低,可维护性低。
然后我为同事出的意见就是,采用工厂模式的写法,去把每个比赛项目的规定限制分开判断。即:会有一个工厂接口CheckEvent,里面定义一个“check”的接口。然后把田径、游泳、篮球等项目的校验分别用实现工厂接口的类去实现(TrackAndField implement CheckEvent、Swim implement CheckEvent、Basketball implement CheckEvent)。然后创建一个工厂类(CheckEventFactory),等到报名的时候,通过工厂类去调用每个项目判断类的校验方法。
下面会介绍Spring与工厂模式的关系。但是在说Spring中的工厂模式前,我们先补充一下工厂模式的动态实现。
Shape接口和Rectangle、Square、Cicle这三个产品类保持不变,将工厂类中获取产品实例的方法改为通过类加载器获取类对象,再由类对象获取其实例:
package com.yuxx.factory;
public class DynamicShapeFactoryBean {
//入参className此时须是类的全路径
public static Shape getShapge(String className){
Shape shape = null;
try{
shape = (Shape) Class.forName(className).newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return shape;
}
}
动态工厂调用:
package com.yuxx.factory;
import org.junit.Test;
public class DynamicFactoryPatternDemo {
@Test
public void test() {
Shape rectangle = DynamicShapeFactoryBean.getShapge("com.yuxx.factory.Rectangle");
rectangle.draw();
Shape square = DynamicShapeFactoryBean.getShapge("com.yuxx.factory.Square");
square.draw();
Shape circle = DynamicShapeFactoryBean.getShapge("com.yuxx.factory.Circle");
circle.draw();
}
}
Spring与工厂模式:
说到Spring中的工厂模式,可以说是无处不见了。我们知道,Spring中Bean的管理权交由Spring IOC容器。殊不知,Spring容器就是一个超级大的工厂,它动态管理着所有Bean实例。
由于Spring的DI机制(与IOC是同一个事),我们在Spring框架中使用Bean实例时,不再需要自己去创建Bean的实例,Spring容器就能帮我们创建好,然后注入到我们定义的引用中。虽然我们不用去写创建过程的代码,但是需要通过xml配置文件或者Java配置类去装配Bean:
<bean class="com.yuxx.bean.Person" id="person" scope="singleton">
<property name="age" value="18"></property>
<property name="name" value="张三"></property>
</bean>
@Scope("singleton")
@Bean("person")
public Person person(){
System.out.println("向容器中添加Person...");
return new Person("张三",25);
}
要知道,Spring只是一个框架,框架中封装的东西也都是具有普遍性的,所以Spring其实并不知道我们想要创建的是哪个类的实例。所以一定是通过反射或者类加载器一类的底层逻辑实现。
下班,后续继续补充Spring中工厂模式的痕迹