工厂模式的关键点
是面向接口编程
工厂方法模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
一、简单工厂模式:
实例化对象的时候不再使用 new Object()形式,可以根据用户的选择条件来实例化相关的类。对于客户端来说,去除了具体的类的依赖。只需要给出具体实例的描述给工厂,工厂就会自动返回具体的实例对象。
具体实现:
- 接口
public interface Operation {
public double getResult(double numberA,double numberB) throws Exception;
}
- 实现类
public class Add implements Operation{
// 加法计算
public double getResult(double numberA, double numberB) {
return numberA + numberB;
}
}
public class Sub implements Operation{
// 减法计算
public double getResult(double numberA, double numberB) {
return numberA-numberB;
}
}
public class Mul implements Operation{
// 乘法计算
public double getResult(double numberA, double numberB) {
return numberA * numberB;
}
}
public class Div implements Operation {
// 除法计算
public double getResult(double numberA, double numberB) throws Exception {
if (numberB == 0) {
throw new Exception("除数不能为0!");
}
return numberA / numberB;
}
}
- 定义简单工厂类:
public class EasyFactory {
// 简单工厂,根据字符串创建相应的对象
public static Operation createOperation(String name) {
Operation operationObj = null;
switch (name) {
case "+":
operationObj = new Add();
break;
case "-":
operationObj = new Sub();
break;
case "*":
operationObj = new Mul();
break;
case "/":
operationObj = new Div();
break;
}
return operationObj;
}
}
- 测试代码
public class Client {
public static void main(String[] args) throws Exception {
Operation add = EasyFactory.createOperation("+");
Operation sub = EasyFactory.createOperation("-");
Operation mul = EasyFactory.createOperation("*");
Operation div = EasyFactory.createOperation("/");
System.out.println(add.getResult(1, 1));
System.out.println(sub.getResult(1, 1));
System.out.println(mul.getResult(1, 1));
System.out.println(div.getResult(1, 1));
}
}
我们无需提供具体的子类类名,只需要提供一个字符串即可得到相应的实例对象。这样的话,当子类的类名更换或者增加子类时我们都无需修改客户端代码,只需要在简单工厂类上增加一个分支判断代码即可。
使用这种模式,我们在生成工厂的时候可以加一些业务代码,如日志、判断业务等,这时候可以直接在switch case中加上去就行了,如下:
public class EasyFactory {
private static Operation operationObj = null;
private static Operation add(){
System.out.println("加法运算");
return new Add();
}
private static Operation sub(){
System.out.println("减法运算");
return new Sub();
}
private static Operation mul(){
System.out.println("乘法运算");
return new Mul();
}
private static Operation div(){
System.out.println("除法运算");
return new Div();
}
// 简单工厂,根据字符串创建相应的对象
public static Operation createOperation(String name) {
switch (name) {
case "+":
operationObj = add();
break;
case "-":
operationObj = sub();
break;
case "*":
operationObj = mul();
break;
case "/":
operationObj = div();
break;
}
return operationObj;
}
}
这样做的优点:我们可以对创建的对象进行一些 “加工” ,而且客户端并不知道,因为工厂隐藏了这些细节。如果,没有工厂的话,那我们是不是就得自己在客户端上写这些代码,这就好比本来可以在工厂里生产的东西,拿来自己手工制作,不仅麻烦以后还不好维护。
但是缺点也很明显:如果需要在方法里写很多与对象创建有关的业务代码,而且需要的创建的对象还不少的话,我们要在这个简单工厂类里编写很多个方法,每个方法里都得写很多相应的业务代码,而每次增加子类或者删除子类对象的创建都需要打开这简单工厂类来进行修改。这会导致这个简单工厂类很庞大臃肿、耦合性高,而且增加、删除某个子类对象的创建都需要打开简单工厂类来进行修改代码也违反了开-闭原则。
二、工厂模式
这时候就需要使用工厂模式了。工厂方法模式是对简单工厂模式进一步的解耦,因为在工厂方法模式中是一个子类对应一个工厂类,而这些工厂类都实现于一个抽象接口。这相当于是把原本会因为业务代码而庞大的简单工厂类,拆分成了一个个的工厂类,这样代码就不会都耦合在同一个类里了。
- 首先定义一个工厂接口:
public interface Factory {
public Operation createOperation() ;
}
- 然后是具体的工厂类:
// 加法类工厂
public class AddFactory implements Factory{
public Operation createOperation() {
System.out.println("加法运算");
return new Add();
}
}
// 减法类工厂
public class SubFactory implements Factory{
public Operation createOperation() {
System.out.println("减法运算");
return new Sub();
}
}
........
3.客户端测试
public class TestMain {
public static void main(String[] args) throws Exception {
Factory f1 = (Factory) Class.forName("com.module.test.factory.MultiplyFactory").newInstance();
Factory f2 = (Factory) Class.forName("com.module.test.factory.SubFactory").newInstance();
System.out.println(f1.createOperation().getResult(1, 2));
System.out.println(f2.createOperation().getResult(1, 2));
}
}
比较:
工厂模式中,要增加产品类时也要相应地增加工厂类,客户端的代码也增加了不少。工厂方法把简单工厂的内部逻辑判断转移到了客户端代码来进行。
你想要加功能,本来是改工厂类的,而现在是修改客户端。而且各个不同功能的实例对象的创建代码,也没有耦合在同一个工厂类里,这也是工厂方法模式对简单工厂模式解耦的一个体现。工厂方法模式克服了简单工厂会违背开-闭原则的缺点,又保持了封装对象创建过程的优点。
但工厂方法模式的缺点是每增加一个产品类,就需要增加一个对应的工厂类,增加了额外的开发量。
依赖抽象原则
- 变量不要持有具体类的引用
- 不要让类继承自具体类,要继承自抽象类或接口
- 不要覆盖基类中已实现的方法