工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。 在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
一、介绍
意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行
主要解决:主要解决接口选择的问题
何时使用:我们明确地计划不同条件下创建不同实例时
如何解决:让其子类实现工厂接口,返回的也是一个抽象的产品
关键代码:创建过程在其子类执行
应用实例:
- 您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现
- Hibernate 换数据库只需换方言和驱动就可以
优点:
- 一个调用者想创建一个对象,只要知道其名称就可以了
- 扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以
- 屏蔽产品的具体实现,调用者只关心产品的接口
缺点:每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。
使用场景:
- 日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方
- 数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时
- 设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口
注意事项:作为一种创建型模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
二、工厂模式的三种形式
- 简单工厂(Simple Factory)模式:又称静态工厂方法模式(Static Factory Method Pattern)
- 工厂方法(Factory Method)模式:又称多态性工厂(Polymorphic Factory)模式或虚拟构造子(Virtual Constructor)模式
- 抽象工厂(Abstract Factory)模式:又称工具箱(Kit 或 ToolKit)模式
三、简单工厂模式
简单工厂模式(又叫作静态工厂方法模式), 其属于创建型设计模式,但并不属于23种Gof设计模式之一。
定义:简单工厂模式属于创建型设计模式,又叫作静态工厂方法模式,这是由一个工厂对象决定创建出哪一种产品类的实例。
实现
场景描述:比如说有个农场公司,专门向市场销售各类水果。在这个系统里需要描述下列的水果
- 葡萄 Grape
- 草莓 Strawberry
- 苹果 Apple
/**
* 定义一个适用于所有水果的接口,定义出所有水果必须具备的方法:
* 种植plant(),生长grow(),收获harvest()
*/
public interface Fruit {
//种植
void plant();
//生长
void grow();
//收获
void harvest();
default void log(String message){
System.out.println(message);
}
}
/**
* 苹果类
*/
public class Apple implements Fruit {
//树龄
private Integer treeAge;
@Override
public void plant() {
log("Apple开始种植...");
}
@Override
public void grow() {
log("Apple开始生长...");
}
@Override
public void harvest() {
log("Apple开始收货...");
}
public Integer getTreeAge() {
return treeAge;
}
public void setTreeAge(Integer treeAge) {
this.treeAge = treeAge;
}
}
/**
* 工厂类,负责生产水果
*/
public class FruitFactory {
public static Fruit getFruit(String type) {
switch (type) {
case "apple":
return new Apple();
default:
throw new RuntimeException("农场没有这种水果");
}
}
}
测试:
输出结果:
特点:它是一个具体的类,非接口、抽象类。有一个重要的
getFruit()方法,利用switch创建产品并返回
优点:
- 解耦,将需求类与实现类分离了,通过工厂类进行交互,实现了责任的分割
- 无论是添加,修改,删除子类,都十分的容易,不会影响到其他的类
- 复用,子类可以多次复用
缺点:
- 扩展性差(我想增加一种水果,除了新增一个水果产品类,还需要修改工厂类方法)
- 不同的产品需要不同额外参数的时候不支持
- 不符合:对扩展开放,对修改关闭
四、工厂方法模式
提供一个用于创建对象的接口(工厂接口),让其实现类(工厂实现类)决定实例化哪一个类(产品类),并且由该实现类创建对应类的实例
作用 可以一定程度上解耦,消费者和产品实现类隔离开,只依赖产品接口(抽象产品),产品实现类如何改动与消费者完全无关。
可以一定程度增加扩展性,若增加一个产品实现,只需要实现产品接口,修改工厂创建产品的方法,消费者可以无感知(若消费者不关心具体产品是什么的情况)。 可以一定程度增加代码的封装性、可读性。清楚的代码结构,对于消费者来说很少的代码量就可以完成很多工作。 另外,抽象工厂才是实际意义的工厂模式,工厂方法只是抽象工厂的一个比较常见的情况。
适用场景
消费者不关心它所要创建对象的类(产品类)的时候。
消费者知道它所要创建对象的类(产品类),但不关心如何创建的时候。
实现
提供一个产品类的接口。产品类均要实现这个接口(也可以是abstract类,即抽象产品)。 提供一个工厂类的接口。工厂类均要实现这个接口(即抽象工厂)。 由工厂实现类创建产品类的实例。工厂实现类应有一个方法,用来实例化产品类。
/**
* 工厂接口
*/
public interface IMessageFactory {
public IMessage createMessage(String messageType);
}
/**
* 产品接口
*/
public interface IMessage {
public Map<String, Object> getMessageParam();
public void setMessageParam(Map<String, Object> messageParam);
public void sendMesage() throws Exception;
}
/**
* 虚拟产品类
*/
public abstract class AbstractMessage implements IMessage {
private Map<String, Object> messageParam;
@Override
public Map<String, Object> getMessageParam() {
return messageParam;
}
@Override
public void setMessageParam(Map<String, Object> messageParam) {
this.messageParam = messageParam;
}
}
/**
* 邮件产品类
*/
public class EmailMessage extends AbstractMessage {
@Override
public void sendMesage() throws Exception {
// TODO Auto-generated method stub
if (null == getMessageParam() || null == getMessageParam().get("EMAIL")
|| "".equals(getMessageParam().get("EMAIL"))) {
throw new Exception("发送短信,需要传入EMAIL参数");
}
System.out.println("我是邮件,发送通知给" + getMessageParam().get("EMAIL"));
}
}
/**
* 短信产品类
*/
public class SmsMessage extends AbstractMessage {
@Override
public void sendMesage() throws Exception {
if (null == getMessageParam()
|| null == getMessageParam().get("PHONENUM")
|| "".equals(getMessageParam().get("PHONENUM"))) {
throw new Exception("发送短信,需要传入PHONENUM参数");
}
System.out.println("我是短信,发送通知给" + getMessageParam().get("PHONENUM"));
}
}
/**
* 工厂实现
*/
public class MessageFactory implements IMessageFactory {
@Override
public IMessage createMessage(String messageType) {
// 这里的方式是:消费者知道自己想要什么产品;若生产何种产品完全由工厂决定,则这里不应该传入控制生产的参数。
IMessage message;
Map<String, Object> messageParam = new HashMap<String, Object>();
// 根据某些条件去选择究竟创建哪一个具体的实现对象,条件可以传入的,也可以从其它途径获取。
// sms
if ("SMS".equals(messageType)) {
message = new SmsMessage();
messageParam.put("PHONENUM", "123456789");
} else {
message = new EmailMessage();
messageParam.put("EMAIL", "123456@qq.com");
}
message.setMessageParam(messageParam);
return message;
}
}
测试:
输出结果:
五、抽象工厂模式
为创建一组相关或相互依赖的对象提供一个接口,而且无需指定他们的具体类。
与工厂方法模式的区别:
抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。
- 工厂方法模式针对的是一个产品等级结构;而抽象工厂模式则是针对的多个产品等级结构。在编程中,通常一个产品结构,表现为一个接口或者抽象类,也就是说,工厂方法模式提供的所有产品都是衍生自同一个接口或抽象类,而抽象工厂模式所提供的产品则是衍生自不同的接口或抽象类。
- 在抽象工厂模式中,有一个产品族的概念:所谓的产品族,是指位于不同产品等级结构中功能相关联的产品组成的家族。抽象工厂模式所提供的一系列产品就组成一个产品族;而工厂方法提供的一系列产品称为一个等级结构。
适用场景
- 客户端(应用层)不依赖于产品类实例如何被创建、实现等细节
- 强调一系列相关的产品对象(属于同一产品族)一起使用创建对象需要大量重复的代码
- 提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于具体实现
为了更好理解产品等级结构与产品族,我们看两张图
横着一行是一个产品族,竖着一列是一个产品等级结构。
实现
业务场景:现在需要录视频还要手记,如Java手记、Python手记等,要是按照工厂模式,需要创建Java手记类、python手记类、Java手记工厂、python 手记工厂、手记工厂类,很容易发生类爆炸情况。其中Java视频、python 视频是一个产品等级都是视频,Java手记、python手记又是一个产品等级,Java视频和Java手记是同一产品族。
//手记工厂
public abstract class Article {
public abstract void produce();
}
//视频工厂
public abstract class Video {
public abstract void produce();
}
//Java手记工厂
public class JavaArticle extends Article {
@Override
public void produce() {
System.out.println("编写Java课程手记");
}
}
//Java视频工厂
public class JavaVedio extends Video {
@Override
public void produce() {
System.out.println("录制Java课程视频");
}
}
//Python手记工厂
public class PythonArticle extends Article {
@Override
public void produce() {
System.out.println("编写Python课程手记");
}
}
//Python视频工厂
public class PythonVideo extends Video{
@Override
public void produce() {
System.out.println("录制Python课程视频");
}
}
//抽象工厂
public interface CourseFactory {
Video getVideo();
Article getArticle();
}
测试:
输出结果:
优点:
- 具体产品在应用层代码隔离,无须关心创建细节
- 将一系列的产品族统一到一起创建
- 最主要的优点就是可以在类的内部对产品族进行约束,所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理
缺点:
- 规定了所有可能被创建的产品集合,产品族中扩展新的产品困难,需要修改抽象工厂的接口
- 增加了系统的抽象性和理解难度
- 产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改,所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的。