0.抛出问题
在软件构建过程中,某些对象使用的算法可能多种多样,经常改变。如果将这些算法都编码在一个对象中,会使得对象变得异常复杂,而且支持不使用的算法也是一个性能负担。如何在运行时候根据需要更改对象的算法,将算法和对象本身解耦?
1.具体场景
假设我们现在有一家跨国公司需要计算每个国家的关税(目前业务涉及中国、美国、法国),设计软件
第一版代码
//枚举类型定义三个参数
public enum TaxBase {
CN_TAX,
US_TAX,
FR_TAX
}
public class SalesOrder {
TaxBase taxBase;
public void CalculateTax(){
switch (taxBase){
case CN_TAX:
System.out.println("假设这是一堆很复杂的算法");
System.out.println("中国关税");
break;
case FR_TAX:
System.out.println("假设这是一堆很复杂的算法");
System.out.println("法国关税");
break;
case US_TAX:
System.out.println("假设这是一堆很复杂的算法");
System.out.println("美国关税");
break;
}
}
public void setTaxBase(TaxBase taxBase) {
this.taxBase = taxBase;
}
}
public class Main {
public static void main(String[] args)
{
SalesOrder salesOrder = new SalesOrder();
salesOrder.setTaxBase(TaxBase.CN_TAX);
salesOrder.CalculateTax();
}
}
//运行结果
假设这是一堆很复杂的算法
中国关税
Process finished with exit code 0
可以说上面的代码是非常非常的简单了,经过前几篇的文章的总结,这里面的设计缺陷已经被重复无数次了,代码复用性不够,要是业务有扩展添加英国关税后,需要修改SalesOrder类源代码,而且将所有的算法都写在同一个类里面,如果哪天公司业务遍布全球232个国家和地区,那这个类可太臃肿了。必须改改改!!!
2.改进
类图分析,定义一个Strategy接口(所有关税算法的抽象接口),然后在Context类中根据需要来创建具体的算法对象
//公共接口
public interface TaxStrategy {
double calculate();
}
public class FRTaxStrategy implements TaxStrategy {
@Override
public double calculate() {
System.out.println("假设这是一堆很复杂的算法");
System.out.println("法国关税");
return 0;
}
}
public class CNTaxStrategy implements TaxStrategy {
@Override
public double calculate() {
System.out.println("假设这是一堆很复杂的算法");
System.out.println("中国关税");
return 0;
}
}
public class USTaxStrategy implements TaxStrategy {
@Override
public double calculate() {
System.out.println("假设这是一堆很复杂的算法");
System.out.println("美国关税");
return 0;
}
}
//上下文
public class Context {
private TaxStrategy taxStrategy;
//根据传过来的字节码去反射创建相应对象
public Context(Class<TaxStrategy> taxStrategyClass) {
try {
taxStrategy=taxStrategyClass.newInstance();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
public void execute(){
taxStrategy.calculate();
}
}
public class Main {
public static void main(String[] args)
{
Context context = new Context(CNTaxStrategy.class);
context.execute();
}
}
//运行结果
假设这是一堆很复杂的算法
中国关税
Process finished with exit code 0
这样写的话我们在需要添加新关税算法只需要用一个类去实现TaxStrategy接口就好了,问题被解决。
2.总结
上面的代码其实很像简单工厂模式,笔者在刚接触的时候觉得基本一模一样,现在细说一下他们的区别。
简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建并且返回哪一 个产品类(这些产品类继承自一个父类或接口)的实例。
那么也就是说:
1、有已知的产品类
2、你无法准确的知道编译哪个产品类
3、需要在运行时决定创建哪个产品类
4、产品类不多
很明显看出,在创建对象上的灵活性高
策略模式它定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。在一段代码里,使用了逻辑控制(if-else,swich-case)来决定算法,算法有相似的方法和函数,就可以选择策略模式。
那么也就是说:
1、某方法里有多个条件语句,条件语句代码块里有许多行为过程。
2、其算法能封装到策略类
3、算法随意切换
4、算法与客户端隔离
这样一来,通过选择对应的策略类,作为参数传到Content类里,在运行时配置对应的算法。
可以看出,两者解决的问题不同。简单工厂模式是解决产品类的统一分发、策略模式是为了解决策略的切换和扩展。