工厂模式(Factory Pattern)是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
工厂模式介绍(来自菜鸟教程)
意图:
定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。
主要解决:
主要解决接口选择的问题。
何时使用:
我们明确地计划不同条件下创建不同实例时。
如何解决:
让其子类实现工厂接口,返回的也是一个抽象的产品。
关键代码:
创建过程在其子类执行。
**应用实例: **
1、您需要一辆汽车,可以直接从工厂里面提货,而不用去管这辆汽车是怎么做出来的,以及这个汽车里面的具体实现。
2、Hibernate 换数据库只需换方言和驱动就可以。
优点:
1、一个调用者想创建一个对象,只要知道其名称就可以了。
2、扩展性高,如果想增加一个产品,只要扩展一个工厂类就可以。
3、屏蔽产品的具体实现,调用者只关心产品的接口。
缺点:
每次增加一个产品时,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。这并不是什么好事。
使用场景:
1、日志记录器:记录可能记录到本地硬盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。
2、数据库访问,当用户不知道最后系统采用哪一类数据库,以及数据库可能有变化时。 3、设计一个连接服务器的框架,需要三个协议,"POP3"、"IMAP"、"HTTP",可以把这三个作为产品类,共同实现一个接口。
注意事项:
作为一种创建类模式,在任何需要生成复杂对象的地方,都可以使用工厂方法模式。有一点需要注意的地方就是复杂对象适合使用工厂模式,而简单对象,特别是只需要通过 new 就可以完成创建的对象,无需使用工厂模式。如果使用工厂模式,就需要引入一个工厂类,会增加系统的复杂度。
工厂模式实战
老夫在参加校招的时候,忘了是哪个公司,先参加笔试,笔试内容是 Java/C 任意选择一个开发语言作为我们笔试的基本语言。因为老夫对 C 向来没兴趣,就选择了 Java . 在笔试题中 , 有一道题目是使用 面向对象语言 Java 编写一个可实现 加 / 减 / 乘 / 除 基本功能的简易计算器。这看起来还是很简单的。所以老夫就随手写了,按照顺序写的,就跟 C 一样。
public class Calculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in) ;
while (true){
System.out.println("请输入数字 A : ");
int numA = scanner.nextInt();
System.out.println("输入操作符: ");
String operator = scanner.next();
System.out.println("输入数字 B : ");
int numB = scanner.nextInt();
int result = operate(numA, operator, numB);
System.out.println("计算后的结果为: " + result);
}
}
public static int operate(int numA , String operator , int numB){
switch (operator){
case "+" :
return (numA + numB) ;
case "-":
return (numA - numB) ;
case "*":
return (numA * numB) ;
case "/":
if (numB == 0){
throw new IllegalArgumentException("除数不可为 0") ;
}else {
return (numA / numB) ;
}
default:
throw new IllegalArgumentException("nothing ... ") ;
}
}
}
后来,面试官拿到我的笔试题,前面的都没看,看到我的程序,就问我,你觉得你的程序有什么问题?然后我说,功能都没问题呀,好像没啥别的毛病。面试官跟我说:“你的功能是实现了,但是你有没有看到要求。用面向对象语言 Java 来实现,你实现了这个功能,但面向对象没有体现出来。你觉得怎么去改它。”后来面试官应该看不下去了,就跟我说:“简单点,你把这个业务代码和这个界面分离,这个你能做到吧。”后来我改成了下面的这个:
public class Calculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in) ;
while (true){
System.out.println("请输入数字 A : ");
int numA = scanner.nextInt();
System.out.println("输入操作符: ");
String operator = scanner.next();
System.out.println("输入数字 B : ");
int numB = scanner.nextInt();
int result = Operator.operate(numA, operator, numB);
System.out.println("计算后的结果为: " + result);
}
}
}
class Operator{
public static int operate(int numA , String operator , int numB){
switch (operator){
case "+" :
return (numA + numB) ;
case "-":
return (numA - numB) ;
case "*":
return (numA * numB) ;
case "/":
if (numB == 0){
throw new IllegalArgumentException("除数不可为 0") ;
}else {
return (numA / numB) ;
}
default:
throw new IllegalArgumentException("nothing ... ") ;
}
}
}
然后他跟我说:“你这样写,不就分开了吗。”后来我们就聊了下入职实习等一些情况。讲真的,当时我是真没觉得这有啥子区别,我就换个位置而已。后来才知道,分开之后相当于解耦的过程。上述的过程其实相当于一个封装的过程。将操作部分封装在一个类里面。
工厂模式下的调整
首先,我们先将上面的程序在重构一下,上面的耦合性太高,再解耦。
这是新的 Operator.java
public abstract class Operator {
public int numA ;
public int numB ;
public int getNumA() {
return numA;
}
public void setNumA(int numA) {
this.numA = numA;
}
public int getNumB() {
return numB;
}
public void setNumB(int numB) {
this.numB = numB;
}
public abstract int getResult();
}
OperatorAdd.java
public class OperatorAdd extends Operator {
@Override
public int getResult() {
return numA + numB;
}
}
OperatorFactory.java
public class OperatorFactory {
public static Operator createOperatorFactory(String operate){
switch (operate){
case "+" :
return new OperatorAdd() ;
case "-":
return new OperatorSub() ;
case "*":
return new OperatorMul() ;
case "/":
return new OperatorDiv() ;
default:
throw new IllegalArgumentException("Are you kidding ? ") ;
}
}
}
在工厂类中,OperatorSub.java , OperatorMul.java , OperatorDiv.java 与 OperatorAdd.java 相似。
现在再看主类:
public class Calculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in) ;
System.out.println("请输入数字 A : ");
int numA = scanner.nextInt();
System.out.println("输入操作符: ");
String operate = scanner.next();
System.out.println("输入数字 B : ");
int numB = scanner.nextInt();
Operator operator = OperatorFactory.createOperatorFactory(operate) ;
operator.setNumA(numA);
operator.setNumB(numB);
int result = operator.getResult();
System.out.println(result);
}
}
以上便是一个很简单的工厂模式。