简单工厂模式

代码无错就是优?

假如有如下这样一道笔试题:

请用c++ 、Java、C#、或VB.NET 任意一种面向对象语言实现一个计算器控制台程序。要求:输入两个数和运算符号,得到结果。

或许一拿到题。心里就想:“这还不简单?获取控制台的输入参数,后根据if或者switch进行运算符判断。将结果输出。”


顺利成章的得到如下代码:
public static void main(String[] args) throws Exception {
	Scanner sc = new Scanner(System.in);
	System.out.println("输入第一个数字A");
	double numA = sc.nextDouble();
	System.out.println("输入运算符:+、-、*、/");
	String operator = sc.next();
	System.out.println("输入第二个数字B");
	double numB = sc.nextDouble();
	double result = 0d;
	switch (operator) {
	case "+":
		result = numA + numB;
		break;
	case "-":
		result = numA - numB;
		break;
	case "*":
		result = numA * numB;
		break;
	case "/":
		result = numA / numB;
		break;
	default:
		break;
	}
	System.out.println(String.format("结果是:%s", result));
	sc.close();
}

乍一看,似乎没什么问题。但是,会不会出现一些特殊情况呢?比如说,除数为0,运算符并非+-*/呢?因此我们可以将程序做一些细微的调整来预防这些潜在的问题。


经过调整,将 除法 以及default处修改成如下代码片段
case "/":
	if(0 == numB){
		throw new Exception("除数不能为0!");
	}
	result = numA / numB;
	break;
default:
	throw new Exception("运算符错误!");

看起来潜在问题被解决掉了。但是回过头看题目

请用c++ 、Java、C#、或VB.NET 任意一种面向对象语言实现一个计算器控制台程序。要求:输入两个数和运算符号,得到结果。

如果只是这么简单,那你和他人区别在哪呢?现在让我们将代码写的更优秀一些。

解决问题之前,自然要先提出问题:假如现在需要增加一个开方运算。我们在上述基础上应该怎么做呢?

很容易想到在多加一个case分支就可以了。

好的,现在问题来了,你可以增加一个分支。但是这意味着,你需要拿到全部的源码,才可以进行添加。在添加的过程中,如果"不小心"碰到了别的位置的代码,可能会导致1+1=3的情况产生喔。

因此,我们需要将这 “一整坨” 代码,拆分开来,也就是所谓的 "降耦"

我们可以通过如下方式来将程序耦合度降低。

class OperationAdd {
	public double getResult(double numA, double numB) {
		return numA + numB;
	}
}

class OperationSub {
	public double getResult(double numA, double numB) {
		return numA - numB;
	}
}

class OperationMul {
	public double getResult(double numA, double numB) {
		return numA * numB;
	}
}

class OperationDiv {
	public double getResult(double numA, double numB) throws Exception {
		if (0 == numB) {
			throw new Exception("除数不能为0");
		} else {
			return numA / numB;
		}
	}
}

这样我们就可以将上述switch更新成如下写法。

switch(operator){
	case "+":
		result = new OperationAdd().getResult(numA, numB);
		break;
	case "-":
		result = new OperationSub().getResult(numA, numB);
		break;
	case "*":
		result = new OperationMul().getResult(numA, numB);
		break;
	case "/":
		result = new OperationDiv().getResult(numA, numB);
		break;
	default:
	throw new Exception("运算符错误!");
}

这样做完之后,我们便可以在新增一种运算,或者修改某一运算时不会碰到其他代码。

假如有一千个,一万个,运算方式。并且我在别处想要使用这种计算方法时,那么多的类,我究竟应该new哪一种呢?找都要找半天。头大喔

因此,引申出的设计模式是:简单工厂模式
也就是说,到底要new谁,将来不论是否会增加运算,我都可以通过修改工厂来完成对象的实例化。
上述例子中,不论哪种计算方式,均是计算的一种。我们可以将他们全部继承于一个基类,通过多态的方式,返回所需要的对象,并在程序执行期间,进行动态绑定,从而正确调用自己所需的方法。

因此我们建立一下几个类:

  1. Operation 为所有计算类的基类,具有getResult方法。默认返回0
  2. OperationAdd 加法类,通过重新getResult来得到加法的运算方式
  3. OperationSub 减法类…
  4. OperationMul 乘法类…
  5. OperationDiv 除法类…
  6. 以及我们的OperationFactory。负责根据传入参数实例化需要的对象
class Operation {
	public double getResult(double numA, double numB) throws Exception {
		return 0d;
	}
}

class OperationAdd extends Operation {
	public double getResult(double numA, double numB) {
		return numA + numB;
	}
}

class OperationSub extends Operation {
	public double getResult(double numA, double numB) {
		return numA - numB;
	}
}

class OperationMul extends Operation {
	public double getResult(double numA, double numB) {
		return numA * numB;
	}
}

class OperationDiv extends Operation {
	public double getResult(double numA, double numB) throws Exception {
		if (0d == numB) {
			throw new Exception("除数不能为0");
		} else {
			return numA / numB;
		}
	}
}
class OperationFactory {
	public static Operation getOperation(String operator) throws Exception {
		Operation operation;
		try {
			switch (operator) {
			case "+":
				operation = new OperationAdd();
				break;
			case "-":
				operation = new OperationSub();
				break;
			case "*":
				operation = new OperationMul();
				break;
			case "/":
				operation = new OperationDiv();
				break;
			default:
				throw new Exception("运算符错误");
			}
			return operation;
		} catch (Exception e) {
			throw e;
		}
	}
}

这样的工厂建好之后,我们便可以通过OperationFactory.getOperation(String operator)
来获得需要的对象并通过调用其getResult方法,来获得正确的结果。
所以程序调整为:

public static void main(String[] args) throws Exception {
	Scanner sc = new Scanner(System.in);
	try {
		System.out.println("输入第一个数字A");
		double numA = sc.nextDouble();
		System.out.println("输入运算符:+、-、*、/");
		String operator = sc.next();
		System.out.println("输入第二个数字B");
		double numB = sc.nextDouble();

		Operation operation = OperationFactory.getOperation(operator);//通过工厂获得对象
		double result = operation.getResult(numA, numB);//动态绑定来获得正确的计算结果
		System.out.println(String.format("结果是:%s", result));
	} catch (Exception e) {
		throw e;
	} finally {
		sc.close();
	}
}

回忆一下,Java的三大特性是什么?
张口就来系列:继承封装多态

在这个简单的计算器程序中。我们通过将计算子类继承于计算基类,并通过多态来返回父类的方式实现了计算结果。

这样实现的代码,如果某一天,需要修改加法运算方式,我们只需修改OperationAdd就可以了。如果需要增加各种复杂运算,如平方,立方根,对数,sin,cos等等等等。我们就可以通过添加相应的子类,并在工厂中的switch内添加分支就可以。

这也就是所谓的简单工厂模式
相比最开始的代码,显然工厂模式下,更易扩展、复用、维护。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值