一文带你学习简单工厂模式
场景:
请用任意一种面向对象语言实现一个计算器控制台程序,要求输入两个数和运算符号,得到结果。
解析:
就单纯实现一个简单计算器控制程序而言,这是一个很简单的功能,相信大家听到这个问题的时候,基本就已经有了思路了。
- 用户输入需要计算的值和计算方式。
- 程序根据用户输入内容进行计算。
- 输出答案。
实现:
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); //获取用户输入对象
System.out.println("请输入数字A:");
String a = scanner.nextLine(); //接收用户输入值
System.out.println("请输入运算符号(+,-,*,/)");
String b = scanner.nextLine();
System.out.println("请输入数字B");
String c = scanner.nextLine();
Double d = 0d;
if(b.equals("+")){
d = Double.parseDouble(a) + Double.parseDouble(c);
}
if(b.equals("-")){
d = Double.parseDouble(a) - Double.parseDouble(c);
}
if(b.equals("*")){
d = Double.parseDouble(a) * Double.parseDouble(c);
}
if(b.equals("/")){
d = Double.parseDouble(a) / Double.parseDouble(c);
}
System.out.println("结果是:" + d);
}
思考
- 其中代码规范咱就不说了,基本功能是已经实现了。
- 但是其中问题点也挺多的,代码不规范咱就不说了。
- 相信不少人已经开出来了,这段代码虽然简单实现了功能,
但是程序却不容易维护,不容易扩展,不容易复用。
show time
- 1 业务的封装
让业务逻辑与界面逻辑分开,让它们之间的耦合度降低,只有分离开,才可以达到更容易维护和扩展。
Operation运算类:
将运算的逻辑单独封装起来
public class Operation {
public static double getResult(double numberA,double numberB,String operate){
double result = 0d;
switch (operate){
case "+":
result = numberA + numberB;
break;
case "-":
result = numberA - numberB;
break;
case "*":
result = numberA * numberB;
break;
case "/":
result = numberA / numberB;
break;
}
return result;
}
}
此时客户端的代码也要进行相应的修改:
可以直接使用封装的运算方法了
public class run02 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); //获取用户输入对象
System.out.println("请输入数字A:");
double numberA = Double.parseDouble(scanner.nextLine()); //接收用户输入值
System.out.println("请输入运算符号(+,-,*,/)");
String operate = scanner.nextLine();
System.out.println("请输入数字B");
double numberB = Double.parseDouble(scanner.nextLine());
double result = 0d;
result = Operation.getResult(numberA,numberB,operate);
System.out.println("结果是:" + result);
}
}
1.2 问题升级
假如我想再增加一个运算方式该怎么办呢?比如说增加一个指数的运算。
相信不少的小伙伴都会想到直接到封装的运算方法中进行增加一个case就行了,但是我们想,这样自然是可以实现方法,但是你增加了一个运算方法,是不是相当于整个运算的逻辑,例如 operation中的getResult方法整个的都需要进行编译,是不是就相当于影响到其他运算方法了。
所以我们要让程序变成这样,我们新增一个算法逻辑,并不能影响其他的运算逻辑,最好就是将每个逻辑都分开,这个自己管自己谁也不管誰,这就是松耦合。
1.3 继承和多态
1.3.1
我新建一个抽象类
将运算方法定义为一个抽象方法
public abstract class Operation03 {
/**
* 创建抽象方法
* @param numberA
* @param numberB
* @return
*/
public double getResult(double numberA, double numberB) { //这是一个抽象方法
return 0d;
}
}
1.3.2
然后将具体的运算方法继承这个抽象方法。
public class Add extends Operation03{
public double getResult(double numberA,double numberB){
return numberA + numberB;
}
}
public class Sub extends Operation03{
public double getResult(double numberA,double numberB){
return numberA - numberB;
}
}
public class Mul extends Operation03{
public double getResult(double numberA,double numberB){
return numberA * numberB;
}
}
public class Div extends Operation03{
public double getResult(double numberA,double numberB){
if(numberB == 0){
System.out.println("除数不能为0");
throw new ArithmeticException();
}
return numberA / numberB;
}
}
我们将所有的运算方式都使用子类集成抽象方法,具体的实现方式都在子类进行实现,但是貌似我们程序并不能直接的知道使用什么运算方法。
不知道如何去实例化对象。
ok,接下来就是正正的工厂了。
到底要实例化谁,将来会不会增加实例化的对象,比如增加指数运算,这是很容易变化的地方,应该用一个单独的类做这个创造示例的过程,这就是工厂
客户端代码变成了:
public class run03 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); //获取用户输入对象
System.out.println("请输入数字A:");
double numberA = Double.parseDouble(scanner.nextLine()); //接收用户输入值
System.out.println("请输入运算符号(+,-,*,/)");
String operate = scanner.nextLine();
System.out.println("请输入数字B");
double numberB = Double.parseDouble(scanner.nextLine());
double result = 0d;
Operation03 oper = OperationFactory.createOperate(operate);
result = oper.getResult(numberA,numberB);
System.out.println("结果是:" + result);
}
}
总结
这样一修改之后的有点就很显而易见了。
- 修改和维护运算方法的代码就很简单了。
- 他们之间的联系也更少了,耦合度没这么高了。
- 之后新增运算方式,直接新增一个运算子类就好了。
- 这个就是结构类图了。
最后
本文代码在gitee仓库中已经有了,各位小伙伴需要的可以自取
链接: gitee仓库连接