设计模式之简单工厂模式与策略模式(通过两种模式设计的计算器/java)
前言
在前段时间制作的图书馆管理系统的mysql重构版时,通过使用三层架构和面向对象的思维让我对这方面的知识有所提升。但在结束之后经过点拨发现在整个项目重构的过程中没有使用到设计模式里的思想,导致整个项目不太符合低耦合高内聚设计原则。所以决定在学习设计模式后,进行一个简单的小项目来练习使用设计模式。本次选择了简单工厂模式、策略模式以及两个模式混合使用来完成计算器的功能。
简单工厂模式
在百度百科我们可以直接搜索到简单工厂模式的简介:
由此我们可以得知,使用简单工厂模式设计的项目中,客户端只需要输入相应的内容,就可以通过工厂类来决定需要实例化的对象,通过多态,返回父类的方式实现结果。
通过这样的设计,不管最后使用的是何种程序,都可以通过这串代码实现已经写好的需要的功能。
简单工厂模式设计的计算器
Operation类:
package SimpleFactory.Operation;
public class Operation {
private double numA = 0;
private double numB = 0;
public double getNumA() {
return numA;
}
public void setNumA(double numA) {
this.numA = numA;
}
public double getNumB() {
return numB;
}
public void setNumB(double numB) {
this.numB = numB;
}
public double getResult() throws Exception {
return 0.0;
}
}
加法类:
package SimpleFactory.Operation;
//加法运算类继承父类Operation类
public class OperationAdd extends Operation{
@Override
public double getResult() {
return getNumA()+getNumB();
}
}
减法类:
package SimpleFactory.Operation;
//减法运算类继承父类Operation类
public class OperationSub extends Operation{
@Override
public double getResult() {
return getNumA()-getNumB();
}
}
乘法类:
package SimpleFactory.Operation;
//乘法运算类继承父类Operation类
public class OperationMul extends Operation{
@Override
public double getResult() {
return getNumA()*getNumB();
}
}
除法类:
package SimpleFactory.Operation;
//除法运算类继承父类Operation类
public class OperationDiv extends Operation{
@Override
public double getResult() throws Exception {
if (getNumB() == 0){
throw new Exception("除数不能为0!");
}
return getNumA()/getNumB();
}
}
工厂类:
package SimpleFactory.Operation;
//简单工厂类
//只要输入运算符号,工厂就实例化出合适的对象,通过多态,返回父类的方式实现了计算器的结果
public class OperationFactory{
private Operation operation;
public Operation simpleFactory (String type){
switch (type){
case "+":
operation = new OperationAdd();
break;
case "-":
operation = new OperationSub();
break;
case "*":
operation = new OperationMul();
break;
case "/":
operation = new OperationDiv();
break;
}
return operation;
}
}
最后到了客户端的代码类(Calculator类):
import java.util.Scanner;
public class Calculator {
public static void main(String[] args) throws Exception {
Operation operation;
OperationFactory operationFactory = new OperationFactory();
Scanner scanner = new Scanner(System.in);
System.out.println("请输入第一个数字:");
double numA = scanner.nextDouble();
System.out.println("请输入运算符号:");
String type = scanner.next();
System.out.println("请输入第二个数字:");
double numB = scanner.nextDouble();
operation = operationFactory.simpleFactory(type);
operation.setNumA(numA);
operation.setNumB(numB);
double result = operation.getResult();
System.out.println("结果 = "+result);
/**
* 以下部分为实现重复运算的代码,做测试使用
*/
// boolean flag = true;
// while (flag){
// System.out.println("请输入运算符号(+、-、*、/;输入其他字符退出运算):");
// String type1 = scanner.next();
// if (!type1.equals("+")&&!type1.equals("-")&&!type1.equals("*")&&!type1.equals("/")){
// flag=false;
// }else {
// System.out.println("请输入第二个数字:");
// double num2 = scanner.nextDouble();
// operation = operationFactory.simpleFactory(type1);
// operation.setNumA(result);
// operation.setNumB(num2);
// double result1 = operation.getResult();
// System.out.println("结果 = "+result1);
// result = result1;
// }
// }
}
}
运算结果:
当工厂类负责创建的对象比较少,却客户只知道传入工厂类的参数 ,对于如何创建对象的逻辑不关心时,简单工厂模式使用会非常的方便。
但当系统中具体产品类不断增多的时候,可能会出现要求工厂类更具不同条件创建不同实例的需求,这种条件的判断和对具体产品类型的判断交错在一起的时候,简单工厂类就很难避免模块功能的蔓延,对系统的维护和扩展非常不利。这时候就需要我们用到其他的一些设计模式了。
策略模式
在百度百科上,我们同样可以很快的找到策略模式的简介:
简单来说,策略模式定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
通过策略模式来实现计算器
Strategy类(策略类):
package Strategy;
//定义所有支持的算法的公共接口
//定义的抽象类或接口Strategy,每一个算法类都要实现这个抽象类下的抽象方法(或接口)
public abstract class Strategy {
public abstract double strategy(double numA,double numB) throws Exception;
}
加法类:
package Strategy;
//加法类,继承自策略类Strategy,实现了抽象方法,实现加法运算
public class AddStrategy extends Strategy{
@Override
public double strategy(double numA, double numB) throws Exception {
return numA+numB;
}
}
减法类:
package Strategy;
//减法类,继承自策略类Strategy,实现了抽象方法,实现减法运算
public class SubStrategy extends Strategy{
@Override
public double strategy(double numA, double numB) throws Exception {
return numA-numB;
}
}
乘法类:
package Strategy;
//乘法类,继承自策略类Strategy,实现了抽象方法,实现乘法运算
public class MulStrategy extends Strategy{
@Override
public double strategy(double numA, double numB) throws Exception{
return numA*numB;
}
}
除法类:
package Strategy;
//除法类,继承自策略类Strategy,实现了抽象方法,实现除法运算
public class DivStrategy extends Strategy{
@Override
public double strategy(double numA, double numB) throws Exception {
if (numB == 0) {
throw new Exception("除数不能为0!");
}
return numA/numB;
}
}
Context类:
package Strategy;
//Context上下文,用一个ConcreteStrategy来配置,维护一个队Strategy对象的引用
public class Context {
private Strategy strategy;
public Context(Strategy strategy){
this.strategy = strategy;
}
public double getResult(double numA, double numB) throws Exception {
return strategy.strategy(numA,numB);
}
}
客户端代码(Client类):
package Strategy;
import java.util.Scanner;
public class Client {
public static void main(String[] args) throws Exception {
Context context = null;
Scanner scanner = new Scanner(System.in);
double result;
String type;
double numA;
double numB;
System.out.println("请输入第一个数字:");
numA = scanner.nextDouble();
System.out.println("请输入运算符号:");
type = scanner.next();
System.out.println("请输入第二个数字:");
numB = scanner.nextDouble();
// context.setStrategy(new AddStrategy());
switch (type) {
case "+":
context = new Context(new AddStrategy());
break;
case "-":
context = new Context(new SubStrategy());
break;
case "*":
context = new Context(new MulStrategy());
break;
case "/":
context = new Context(new DivStrategy());
break;
}
result = context.getResult(numA, numB);
System.out.println("结果=" + result);
}
}
运行结果:
当多个类之区别在表现的行为是不同的时候,可以使用策略模式,在运行时动态的选择要具体执行的行为。策略模式可以对客户隐藏具体算法的实现细节,彼此完全独立。
但是,客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。
这时候,我们可以使用策略加简单工厂的方式来实现
策略模式加简单工厂模式
通过上面对简单工厂模式和策略模式的运用,我们可以得知:
- 使用简单工厂模式的时候,客户只知道传入工厂类的参数,对于如何创建对象无需关心;
- 策略模式需要客户端知道所有的算法或行为。
那么,我们为什么不将两种模式结合一下,让用户不需要了解如何创建对象,就可以选择恰当的算法呢?
策略模式加简单工厂模式设计的计算器
基于以上信息,我们只需要在前面策略模式设计的计算器上再加上一个简单工厂就可以解决这个问题。
我们将Context类中的构造方法设计成一个工厂,具体代码如下:
public Context(String type){
switch (type){
case "+":
AddStrategy add = new AddStrategy();
strategy = add;
break;
case "-":
SubStrategy sub = new SubStrategy();
strategy = sub;
break;
case "*":
MulStrategy mul = new MulStrategy();
strategy = mul;
break;
case "/":
DivStrategy div = new DivStrategy();
strategy = div;
break;
}
}
完成后的Context类:
package Strategy;
//Context上下文,用一个ConcreteStrategy来配置,维护一个队Strategy对象的引用
public class Context {
private Strategy strategy;
public Context(String type){
switch (type){
case "+":
AddStrategy add = new AddStrategy();
strategy = add;
break;
case "-":
SubStrategy sub = new SubStrategy();
strategy = sub;
break;
case "*":
MulStrategy mul = new MulStrategy();
strategy = mul;
break;
case "/":
DivStrategy div = new DivStrategy();
strategy = div;
break;
}
}
public double getResult(double numA, double numB) throws Exception {
return strategy.strategy(numA,numB);
}
}
客户端代码(StrategyAndFactoryClient类):
package Strategy;
import java.util.Scanner;
public class StrategyAndFactoryClient {
public static void main(String[] args) throws Exception {
Context context = null;
double result;
Scanner scanner = new Scanner(System.in);
String type;
double numA;
double numB;
System.out.println("请输入第一个数字:");
numA = scanner.nextDouble();
System.out.println("请输入运算符号:");
type = scanner.next();
System.out.println("请输入第二个数字:");
numB = scanner.nextDouble();
context = new Context(type);
result = context.getResult(numA,numB);
System.out.println(result);
}
}
运行结果:
这时我们可以看到,我们只需要在客户端输入相应的运算符号,就可以在构造方法(工厂)中进行判断,根据传入的参数,动态决定应该创建哪一个对象,解决了使用策略模式时客户需要自己来决定创建哪一个对象的问题。
总结
学习设计模式是一个很漫长的过程,往往我们需要花费大量的时间才能将其融会贯通,在使用的时候才能得心应手,要想达到这样的境界就需要在这个过程中进行大量的练习,才能在使用的时候得心应手。路漫漫其修远兮,吾将上下而求索。