工厂模式介绍
工厂模式定义
工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。
看下 GOF为工厂模式的定义:
“Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.”(在基类中定义创建对象的一个接口,让子类决定实例化哪个类。工厂方法让一个类的实例化延迟到子类中进行。)
工厂模式分类
- 简单工厂模式 (Simple Factory),又称静态工厂方法模式 (Static Factory Method Pattern)。
- 工厂方法模式(Factory Method)。
- 抽象工厂模式(Abstract Factory)。
使用场景
- 生产一个发送消息的 产品对象,比如通过邮件、短信、微信公众号等产品发送消息给用户。
- Spring 中 FactoryBean 的 getObject();spring 中 各种各样的 bean。就可以通过工厂模式创建并且实现了依赖解耦。
工厂模式的优点
- 解耦:把对象的创建和使用分开。
- 降低代码复杂度:如果某个对象的创建比较复杂,或者其过程比较多的步骤。多个地方都会使用就会产生很多重复代码
- 降低维护成本:创建过程有工厂统一管理,当业务发生变化,不需要去找代码中创建对象 A 的地方组个修改,只要在工厂里面修改即可。开闭原则。
简单工厂模式
其实这个并不算设计模式,适合创建简单对象,创建的对象较少。客户端不关心对象的创建过程。
简单工厂模式角色
- 工厂角色(Factory):简单工厂模式的核心,负责创建所有实例的内部逻辑,提供外部使用创建所需要的产品。
- 抽象产品角色(Product):简单工厂所创建的类型。
- 具体产品(Concrete Product)角色:简单工厂模式的创建目标,所有创建的对象都是充当这个角色的某个具体类的实例。
简单工厂代码实现
创建发送器接口,也就是产品角色
代码语言:javascript复制public interface Sender {
/**
* 发送信息
* @param to 收件人
* @param msg 消息
* @return
*/
boolean send(String to, String msg);
}
创建具体产品,分别是邮件发送器、短信发送器。
代码语言:javascript复制public class MailSender implements Sender {
@Override
public boolean send(String to, String msg) {
System.out.println("MailSender:收件人:" to ",消息为:" msg);
return true;
}
}
public class SmsSender implements SenderService {
@Override
public boolean send(String to, String msg) {
System.out.println("SmsSender:收件人:" to ",消息为:" msg);
return true;
}
}
创建简单工厂
代码语言:javascript复制/**
* 工厂类,创建实例对象。缺点当字符串输错则得不到对象
* @author unique
*
*/
public class SendFactory {
public Sender getObject(String type) {
Sender sender = null;
switch (type) {
case "mail":
senderService = new MailSender();
break;
case "sms":
senderService = new SmsSender();
break;
default:
System.out.println("请输入正确类型");
break;
}
return sender;
}
}
单元测试
代码语言:javascript复制public class SimpleFactoryTest {
public static void main(String[] args) {
SendFactory factory = new SendFactory();
Sender sender = factory.getObject("mail");
sender.send("大兄弟", "你是最棒的!");
}
}
缺点
当我们新增产品类的时候,就需要修改工厂类中的 getObject() 方法,不符合 开放-封闭原则。
工厂方法模式
工厂模式中使用最多的一种。
与简单公差个模式最大的区别就是我们不再提供一个统一的工厂来创建所有的产品,二十针对不同的产品提供不同的工厂。也就是每个产品都有一个与之对应的工厂。
适用场景
- 一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
- 一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏原则。
- 创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无需关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。
工厂方法模式角色
- 抽象工厂(Abstract Factory)角色:是工厂方法模式的核心,与应用程序无关。任何在模式中创建的对象的工厂类必须实现这个接口。
- 具体工厂(Concrete Factory)角色 :这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建某一种产品对象。
- 抽象产品(AbstractProduct)角色 :工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。
- 具体产品(Concrete Product)角色 :这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应
代码示例
基于上面的简单工厂,我们改造下。新建一个抽象工厂角色
代码语言:javascript复制public interface SenderFactory {
/**
* 生成对象
* @return
*/
public Sender getObject();
}
增加短信、邮件工厂类,实现抽象工厂接口。
代码语言:javascript复制public class SendMailFactory implements SenderFactory {
@Override
public Sender getObject() {
return new MailSender();
}
}
public class SendSmsFactory implements SenderFactory {
@Override
public Sender getObject() {
return new SmsSenderServiceImpl();
}
}
测试代码
代码语言:javascript复制public class Test {
public static void main(String[] args) {
SenderFactory senderFactory = new SendMailFactory();
Sender sender = senderFactory.getObject();
sender.send("大兄弟", "你是最棒的!");
}
}
抽象工厂模式
在工厂方法模式中,其实我们有一个潜在意识的意识。那就是我们生产的都是同一类产品。抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一种产品,而是可以创建一组产品。这个产品会依赖多个合成一个。比如我们的电脑厂商 有因特尔和AMD 生产的主板与CPU。这个产品族有CPU跟主板。
抽象工厂模式和工厂方法模式一样,都符合开放-封闭原则。但是不同的是,工厂方法模式在增加一个具体产品的时候,都要增加对应的工厂。但是抽象工厂模式只有在新增一个类型的具体产品时才需要新增工厂。也就是说,工厂方法模式的一个工厂只能创建一个具体产品。而抽象工厂模式的一个工厂可以创建属于一类类型的多种具体产品。工厂创建产品的个数介于简单工厂模式和工厂方法模式之间。
适用场景
- 和工厂方法一样客户端不需要知道它所创建的对象的类。
- 需要一组对象共同完成某种功能时,并且可能存在多组对象完成不同功能的情况。(同属于同一个产品族的产品)
- 系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)
抽象工厂方法模式的角色与工厂方法模式一致
我们的电脑有主板、CPU、内存…组成。这些都是产品,现在提供一个工厂,直接同时生产出 CPU与对应的主板,避免单独去创建。也就是所谓的一套,一个系列。
比如 有因特尔工厂出品的 CPU 与 主板,AMD 工厂出品的 CPU 与主板。
代码示例
定义CPU 与主板两个产品
代码语言:javascript复制public interface CPU {
void calculate();
}
public interface Mainboard {
void installCPU();
}
以及产品的具体实现,比如 AMD 公司出品的、因特尔公司出品的 CPU 与主板
代码语言:javascript复制public class AmdCpu implements CPU {
/**
* CPU的针脚数
*/
private int pins = 0;
public AmdCpu(int pins) {
this.pins = pins;
}
@Override
public void calculate() {
System.out.println("AMD CPU的针脚数:" pins);
}
}
public class IntelCPU implements CPU {
/**
* CPU的针脚数
*/
private int pins = 0;
public IntelCPU(int pins) {
this.pins = pins;
}
@Override
public void calculate() {
System.out.println("Intel CPU的针脚数:" pins);
}
}
public class AmdMainboard implements Mainboard {
/**
* CPU插槽的孔数
*/
private int cpuHoles = 0;
/**
* 构造方法,传入CPU插槽的孔数
*
* @param cpuHoles
*/
public AmdMainboard(int cpuHoles) {
this.cpuHoles = cpuHoles;
}
@Override
public void installCPU() {
// TODO Auto-generated method stub
System.out.println("AMD主板的CPU插槽孔数是:" cpuHoles);
}
}
public class IntelMainboard implements Mainboard {
/**
* CPU插槽的孔数
*/
private int cpuHoles = 0;
/**
* 构造方法,传入CPU插槽的孔数
*
* @param cpuHoles
*/
public IntelMainboard(int cpuHoles) {
this.cpuHoles = cpuHoles;
}
@Override
public void installCPU() {
System.out.println("Intel主板的CPU插槽孔数是:" cpuHoles);
}
}
接着我们先创建一个抽象工厂角色,能生产 CPU 与主板系列产品的工厂方法定义。
代码语言:javascript复制public interface MainboardCPUFactory {
/**
* 创建CPU对象
*
* @return CPU对象
*/
public CPU createCpu();
/**
* 创建主板对象
*
* @return 主板对象
*/
public Mainboard createMainboard();
}
因特尔工厂定义
代码语言:javascript复制public class IntelFactory implements MainboardCPUFactory {
@Override
public CPU createCpu() {
return new IntelCPU(755);
}
@Override
public Mainboard createMainboard() {
return new IntelMainboard(755);
}
}
AMD 工厂
代码语言:javascript复制public class AmdFactory implements MainboardCPUFactory {
@Override
public CPU createCpu() {
return new AmdCpu(938);
}
@Override
public Mainboard createMainboard() {
return new AmdMainboard(938);
}
}
最后我们来测试
代码语言:javascript复制public class AbstactFactoryTest {
public static void main(String[] args) {
//使用因特尔工厂生产
MainboardCPUFactory intelFactory = new IntelFactory();
CPU cpu = intelFactory.createCpu();
Mainboard mainboard = intelFactory.createMainboard();
mainboard.installCPU();
cpu.calculate();
}
}
打印
代码语言:javascript复制Intel主板的CPU插槽孔数是:755
Intel CPU的针脚数:755