《大话设计模式》的java翻译版。
在正式介绍工厂模式之前,有一点我想提一下,我觉得《大话设计模式》对面向对象的比喻非常生动,面向过程就比如雕版印刷,而面向对象则是活字印刷术。面向对象有很多优点,比如可维护、可复用、可扩展、灵活性好,而这些都可以在活字印刷术中展现。
当我们使用活字印刷术时:第一,用过一次的字模完全可以使用第二次第三次,此为可复用;第二,如果我们刻错了字,则只需修改该字即可,此为可维护;第三,若需要加入新字,只需另刻字加入即可,此为可扩展;第四,字的顺序方向可以任意排列,横向竖向皆可,此为灵活性好;而这些都是雕版印刷(面向过程)所没有的优点。而这些优点正是因为面向对象所具有的三个基本特征:封装、继承与多态才能很好的实现,这也是我想分享给大家的。
好了下面进入正文。面试题:用任意一种语言实现一个计算器控制台程序,输入两个数和运算符号,得到结果。
下面为面向过程的代码:
public class Calculator {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.print("请输入数字A:");
double a = sc.nextDouble();
System.out.print("请选择运算符号(+、-、*、/):");
String symbol = sc.next();
System.out.print("请输入数字B:");
double b = sc.nextDouble();
double result = 0d;
switch (symbol) {
case "+":
result = a - b;
break;
case "-":
result = a - b;
break;
case "*":
result = a * b;
break;
case "/":
result = a / b;
break;
}
System.out.println("A " + symbol + " B = " + result);
}
}
让我们来分析一下,第一,如果我希望在windows界面或iphone上输入操作,那我必须复制整个方法,并改动其中的一部分代码,才能实现,因为他们的控制台输入部分并不一致,不可复用;第二,显而易见,上面的加法写错了,但是当我想要修改它时,却发现还要编译其余全部的算法,维护风险相当高,而当我们把这一段代码复制到成千上万个平台之后,已经完全不可维护了;第三,当我想要再加入一个开平方的算法,参考第二点,也已经完全不可扩展了。
下面是简单工厂模式的代码:
首先我们抽象(算不算面向对象的特征存在争议)出计算类:
public class Calculator {
private double num1;
private double num2;
double getResult(double num1, double num2, String symbol) {
return 0;
}
public double getNum1() {
return num1;
}
public void setNum1(double num1) {
this.num1 = num1;
}
public double getNum2() {
return num2;
}
public void setNum2(double num2) {
this.num2 = num2;
}
}
然后我们加减乘除类来继承计算类:
class Add extends Calculator {
@Override
double getResult() {
return this.getNum1() + this.getNum2();
}
}
class Sub extends Calculator {
@Override
double getResult() {
return this.getNum1() + this.getNum2();
}
}
class Mul extends Calculator {
@Override
double getResult() {
return this.getNum1() * this.getNum2();
}
}
class Div extends Calculator {
@Override
double getResult() throws Exception {
if (this.getNum2() == 0) {
throw new Exception("除数不能为0");
}
return this.getNum1() + this.getNum2();
}
}
至此,我们的业务逻辑已经全部实现,与界面逻辑完全分开,我们称此为封装,即我们将业务逻辑封装好了。
当然我们也可以这样写:
public interface ICalculator {
double getResult(double num1, double num2) throws Exception;
}
class Add implements ICalculator {
@Override
public double getResult(double num1, double num2) {
return num1 + num2;
}
}
class Sub implements ICalculator {
@Override
public double getResult(double num1, double num2) {
return num1 - num2;
}
}
class Mul implements ICalculator {
@Override
public double getResult(double num1, double num2) {
return num1 * num2;
}
}
class Div implements ICalculator {
@Override
public double getResult(double num1, double num2) throws Exception {
if (num2 == 0) {
throw new Exception("除数不能为0");
}
return num1 / num2;
}
}
工厂类:
public class CalculatorFactory {
public static ICalculator create(String symbol) {
ICalculator calculator = null;
switch (symbol) {
case "+":
calculator = new Add();
break;
case "-":
calculator = new Sub();
break;
case "*":
calculator = new Mul();
break;
case "/":
calculator = new Div();
break;
}
return calculator;
}
}
接口的多种不同的实现方式即为多态。
在简单工厂模式中,我们可以很清晰的体会到面向对象的4个基本特征;
客户端代码:
public class Client {
public static void main(String[] args) {
ICalculator calculator1 = CalculatorFactory.create("+");
ICalculator calculator2 = CalculatorFactory.create("-");
ICalculator calculator3 = CalculatorFactory.create("*");
ICalculator calculator4 = CalculatorFactory.create("/");
try {
double result1 = calculator1.getResult(6, 2);
System.out.println(result1);
double result2 = calculator2.getResult(result1, 2);
System.out.println(result2);
double result3 = calculator3.getResult(result2, 2);
System.out.println(result3);
double result4 = calculator4.getResult(result3, 2);
System.out.println(result4);
} catch (Exception e) {
e.printStackTrace();
}
}
}
那么我们再来分析一下:第一,无论我再任何平台界面操作,我都可以直接使用封装好的业务代码,只需要稍稍修改客户端代码即可,此为可复用;第二,如果我想要修改加法运算,我只需要修改Add类即可,因为所有平台的业务代码都是同一套,所以可维护;第三,如果我想要新增一个开平方的运方法,那我只需要添加一个开平方类实现ICalculator,并修改工程类,同理第二条,可扩展。