最近正在学习设计模式,第一个接触到的就是非常经典的工场模式。由于之前没有多少设计模式相关的知识积累,所以我选择了据说对菜鸟非常友好的《大话设计模式》。不得不说大话设计模式这本书,内容非常接地气,我个人非常喜欢,本文就是将大话设计模式中的简单工厂模式例子用JAVA实现。
场景:使用面向对象语言设计一个简单计算器,要求输入两个数以及操作符,输出计算结果。
项目结构图如下:
项目分为了四个部分,MainClass作为客户端,负责用户输入、工程方法调用和结果输出;CalculatorFactory作为工厂类,向用户端提供运算类的对象;CalculatoryName是枚举,由于switch/case在jdk7之前都不支持String,所以这里用枚举作为switch的选择变量;ICalculator是抽象的运算类接口,AddCalculator、SubCalculator、MulCalculator、ExcCalculator分别为加、减、乘、除四种运算类的实现。
运算类接口ICalculator:
package com.jswang.calculator;
public interface ICalculator {
public double getResult(double num1, double num2);
}
加法运算类AddCalculator:
package com.jswang.calculator;
public class AddCalculator implements ICalculator{
@Override
public double getResult(double num1, double num2) {
return num1+num2;
}
}
减法运算类SubCalculator:
package com.jswang.calculator;
public class SubCalculator implements ICalculator{
@Override
public double getResult(double num1, double num2) {
return num1-num2;
}
}
乘法运算类MulCalculator:
package com.jswang.calculator;
public class MulCalculator implements ICalculator{
@Override
public double getResult(double num1, double num2) {
return num1*num2;
}
}
除法运算类ExcCalculator:
package com.jswang.calculator;
public class ExcCalculator implements ICalculator{
@Override
public double getResult(double num1, double num2) {
if(num2!=0){
return num1/num2;
}
else{
return Double.MAX_VALUE;
}
}
}
运算类工厂CalculatorFactory:
package com.jswang.factory;
import com.jswang.calculator.AddCalculator;
import com.jswang.calculator.ExcCalculator;
import com.jswang.calculator.ICalculator;
import com.jswang.calculator.MulCalculator;
import com.jswang.calculator.SubCalculator;
import com.jswang.calculatorenum.CalculatorName;
public class CalculatorFactory {
public static ICalculator getCalculator(String calc) {
ICalculator calculator = null;
switch (CalculatorName.getCalcName(calc)) {
case ADD:
calculator = new AddCalculator();break;
case SUB:
calculator = new SubCalculator();break;
case MUL:
calculator = new MulCalculator();break;
case EXC:
calculator = new ExcCalculator();break;
case UNKNOW:
break;
}
return calculator;
}
}
运算类名称枚举:CalculatorName:
package com.jswang.calculatorenum;
public enum CalculatorName {
ADD, SUB, MUL, EXC, UNKNOW;
public static CalculatorName getCalcName(String calc) {
try {
return valueOf(calc);
}
catch (Exception ex) {
return UNKNOW;
}
}
}
客户端MainClass:
package com.jswang.main;
import java.util.Scanner;
import com.jswang.calculator.ICalculator;
import com.jswang.factory.CalculatorFactory;
public class MainClass {
public static void main(String[] args) {
double num1 = 0.0;
double num2 = 0.0;
String opt = "";
Scanner scanner = new Scanner(System.in);
System.out.println("输入 num1:");
num1 = scanner.nextDouble();
System.out.println("输入 num2:");
num2 = scanner.nextDouble();
System.out.println("输入 opt:");
opt = scanner.next();
scanner.close();
// 简单工厂模式
ICalculator calculator = CalculatorFactory.getCalculator(opt);
if (null != calculator) {
double res = calculator.getResult(num1, num2);
System.out.println(res);
} else {
System.out.println("暂不支持 " + opt + " 运算符!");
}
}
}
执行结果:
两数相加(ADD):
两数相减(SUB):
两数相乘(MUL):
两数相除(EXC):
可能看完上述代码,会很困惑一点,就是明明只为了实现一个加减乘除运算,为什么要额外付出这么大的代价绕了好几个弯。其实这就要谈到设计模式里很重要的一个原则:开放–封闭原则。良好的面向对象设计,应该对扩展开放,而对修改尽量拒绝。也就是说一个软件系统,不应该在新的需求发生时,去大量的改动源代码,而是扩展新的代码,这样的系统才是健壮的,可维护的。
可能这么说还是假大空了,那就考虑上述的例子,如果将上面工厂类中的逻辑代码switch/case选择运算类的代码,放在MainClass中去,这意味着不再需要工程模式,也不需要抽象出运算类。业务逻辑全都放在客户端,当新的需求比如需要支持num1的num2次方时,需要在客户端进行改动,增加一个新的case分支。这就导致当不断的有新需求来临,客户端日益臃肿,而且客户端中任何一个算法出问题,都可能导致整个系统崩溃。
简单工厂模式,就是为了避免客户端和业务逻辑过于耦合而设计的。将业务逻辑都封装在相关的业务逻辑类中,工厂模式负责根据用户需求进行调度,产生相应的业务对象以供客户使用,这样当有了新的运算需求,仅仅需要新增一个运算类集成抽象运算类接口,并在工厂类中加一个分支即可,客户端无需知道细节,只需要按需要调用工厂产生所需的运算对象即可。各个算法类之间分离,即使出了问题也不会相互影响,这也是解耦的好处。