设计模式之工厂模式
本文我们说一下创建型设计模式中的工厂模式,可细分为三种:分别是简单工厂模式,工厂方法模式,抽象工厂模式
下面一一讲解:
一.简单工厂模式
简单工厂模式属于类的创建型模式,又叫静态工厂方法模式。通过专门定义一个工厂类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
简单工厂模式包含三种角色:
1.工厂角色(Creator)
这是简单工厂模式的核心,它用来负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
2.抽象角色(Product)
这是简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。该类可以是接口,也可以是抽象类。
3.具体产品角色(Concrete Product)
简单工厂模式所创建的具体的实例对象。
以上面的UML为例,表示的是一个用简单工厂方法模式实现的计算器程序。
其中Operator是一个抽象类,其中包含属性numberA及numberB,还有一个方法getResult()用于返回计算的结果,它的角色是抽象角色(Product)。
下面的AddOperator,SubOperator,MulOperator,DivOperator是Operator的子类,分别代表加减乘除四种运算,他们的角色是具体产品角色(Concrete Product)。
OperatorFactory是工厂类,其中的createOperator()方法用于创建计算器对象。
下面看代码:
1 /** 2 * 计算器抽象类 3 * Created by lixiuyu 4 */ 5 public abstract class Operator { 6 7 private double numberA = 0.0; 8 private double numberB = 0.0; 9 10 protected abstract double getResult() throws Exception; 11 12 public double getNumberA() { 13 return numberA; 14 } 15 16 public void setNumberA(double numberA) { 17 this.numberA = numberA; 18 } 19 20 public double getNumberB() { 21 return numberB; 22 } 23 24 public void setNumberB(double numberB) { 25 this.numberB = numberB; 26 } 27 }
1 /** 2 * 加法类 3 * Created by lixiuyu 4 */ 5 public class AddOperator extends Operator { 6 7 protected double getResult() { 8 return getNumberA() + getNumberB(); 9 } 10 }
/** * 减法类 * Created by lixiuyu. */ public class SubOperation extends Operator { @Override protected double getResult() { return getNumberA() - getNumberB(); } }
/** * 乘法类 * Created by lixiuyu. */ public class MulOperation extends Operator { @Override protected double getResult() { return getNumberA() * getNumberB(); } }
1 /** 2 * 除法类 3 * Created by lixiuyu. 4 */ 5 public class DivOperation extends Operator { 6 @Override 7 protected double getResult() throws Exception { 8 if(getNumberB() == 0.0){ 9 throw new Exception("除数不能为0"); 10 }else{ 11 return getNumberA() / getNumberB(); 12 } 13 } 14 }
1 /** 2 * 工厂类 3 * Created by lixiuyu. 4 */ 5 public class OperatorFactory { 6 public static Operator createOperator(String operation){ 7 Operator operator = null; 8 switch (operation){ 9 case "+": 10 operator = new AddOperator(); 11 break; 12 case "-": 13 operator = new SubOperator(); 14 break; 15 case "*": 16 operator = new MulOperator(); 17 break; 18 case "/": 19 operator = new DivOperator(); 20 break; 21 } 22 return operator; 23 } 24 }
测试类:
1 /** 2 * Created by lixiuyu. 3 */ 4 public class OperatorTest { 5 public static void main(String[] args) { 6 Operator operator = OperatorFactory.createOperator("+"); 7 operator.setNumberA(10); 8 operator.setNumberB(5); 9 try { 10 System.out.println(operator.getResult()); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } 14 } 15 }
最后输出15
这样写的计算器是不是比if()...else()...写出来的好呢?
下面我们分析一下简单工厂模式的优缺点:
在简单工厂模式中,工厂类是整个模式的关键所在。它包含了必要的判断逻辑,能够根据外界给定的条件去判断应该创建哪个具体类的实例。用户在使用时可以直接根据工厂类去创建所需的实例,而无需关系这些对象是如何组织并创建的,从这一点上来说,这有利于整个软件体系结构的优化。
但是,简单工厂模式的缺点也正体现在工厂类上,由于工厂类中集中了所有实例的创建逻辑,当我们增加了一个新的具体类时,需要同时修改工厂类(多加一个if),这违反了“开闭原则”。
二.工厂方法模式
工厂方法模式是对普通工厂方法模式的改进,在普通工厂方法模式中,如果传递的字符串出错,则不能正确创建对象,而工厂方法模式是提供多个工厂方法,分别创建对象。
依旧以上面的计算器为例,进行讲解:
我们对OperatorFactory类更改如下:
1 /** 2 * Created by lixiuyu. 3 */ 4 public class OperatorFactory { 5 6 public static Operator createAddOperator(){ 7 return new AddOperator(); 8 } 9 10 public static Operator createSubOperator(){ 11 return new SubOperator(); 12 } 13 14 public static Operator createMulOperator(){ 15 return new MulOperator(); 16 } 17 18 public static Operator createDivOperator(){ 19 return new DivOperator(); 20 } 21 }
此时将不会出现简单工厂模式因为字符串传错而不能正常创建对象的问题。
1 /** 2 * Created by lixiuyu. 3 */ 4 public class OperatorTest { 5 public static void main(String[] args) { 6 Operator operator = OperatorFactory.createAddOperator(); 7 operator.setNumberA(10); 8 operator.setNumberB(5); 9 try { 10 System.out.println(operator.getResult()); 11 } catch (Exception e) { 12 e.printStackTrace(); 13 } 14 } 15 }
执行结果:15.0
总结:
与简单工厂模式相比,工厂方法模式避免了因为传入字符串错误导致无法正常创建对象的问题,但需要添加新的具体类时,仍然需要修改工厂类,这仍然违背“开闭原则”。
三.抽象工厂模式
工厂方法模式有一个问题就是,类的创建依赖工厂类,也就是说,如果想要拓展程序,必须对工厂类进行修改,这违背了开闭原则,所以,从设计角度考虑,有一定的问题,如何解决?就用到抽象工厂模式。抽象工厂模式就是将对象工厂的创建抽象到一个接口中。抽象工厂类不在负责产品的创建,仅仅负责定义具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得可以再不修改具体工厂角色的情况下引进新的产品。
这样一旦需要增加新的功能,直接增加新的工厂类就可以了,不需要修改之前的代码,符合开闭原则。
抽象工厂模式中包含的角色及职责:
1.抽象工厂角色(Creator)
这是抽象工厂模式的核心,任何工厂类必须实现这个接口。
2.具体工厂角色(Concrete Creator)
它是抽象工厂的一个实现,负责实例化产品对象。
3.抽象角色(Product)
抽象工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
3.具体产品角色(Concrete Product)
抽象工厂模式所创建的具体的实例对象。
下面结合UML图理解一下:
在抽象工厂中定义了各个实现类需要实现的接口createOperator(),各个实现类按照各自的规则实例化Operator对象。代码如下:
1 /** 2 * Created by lixiuyu. 3 */ 4 public interface OperatorFactory { 5 public Operator createOperator(); 6 }
接下来定义OperatorFactory的具体实现类,即具体工厂类。
1 /** 2 * Created by lixiuyu. 3 */ 4 public class AddOperatorFactory implements OperatorFactory { 5 6 @Override 7 public Operator createOperator() { 8 return new AddOperator(); 9 } 10 }
1 /** 2 * Created by lixiuyu. 3 */ 4 public class SubOperatorFactory implements OperatorFactory { 5 @Override 6 public Operator createOperator() { 7 return new SubOperator(); 8 } 9 }
1 /** 2 * Created by lixiuyu. 3 */ 4 public class MulOperatorFactory implements OperatorFactory { 5 @Override 6 public Operator createOperator() { 7 return new MulOperator(); 8 } 9 }
1 /** 2 * Created by lixiuyu. 3 */ 4 public class DivOperatorFactory implements OperatorFactory { 5 @Override 6 public Operator createOperator() { 7 return new DivOperator(); 8 } 9 }
下面是测试类:
1 /** 2 * Created by lixiuyu. 3 */ 4 public class OperatorFactoryTest { 5 6 public static void main(String[] args) { 7 OperatorFactory factory = new AddOperatorFactory(); 8 Operator operator = factory.createOperator(); 9 operator.setNumberA(10.0); 10 operator.setNumberB(5.0); 11 try { 12 System.out.println(operator.getResult()); 13 } catch (Exception e) { 14 e.printStackTrace(); 15 } 16 } 17 }
运行结果:15.0
由此可见,当我们需要增加一种运算时,只需要增加一个具体运算及具体工厂类,原有的具体工厂类不需要做任何改动,即做到了"对扩展开放,对修改关闭",很好的符合了开闭原则。