策略模式与简单工厂模式结合
在上文末尾,我们用策略模式去实现了商场收费程序,编写了CashContext类,虽然在分离了运算逻辑,并且在收费规则发生变化时,Context类也无需修改,但是这又回到了最初的问题,那就是在主程序中,将界面逻辑和判断应该传入什么对象给Context类的业务逻辑混在一起了,这显然无法满足程序易维护的需求。
import java.util.ArrayList;
import java.util.Scanner;
/**
* @Author yirui
* @Date 2024/4/10 10:44
* @Version 1.0
*/
public class AcceptCash2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double totalPrice = 0d;
try {
ArrayList<String> priceList = new ArrayList<>();
String acceptRule;//收费规则:单价,数量,优惠方式。逗号分隔
double price;
int rule, count;
while (true) {
acceptRule = sc.nextLine();
if (acceptRule.equals("end")) {
break;
} else {
priceList.add(acceptRule);
String[] list = acceptRule.split(",");
price = Double.parseDouble(list[0]);
count = Integer.parseInt(list[1]);
rule = Integer.parseInt(list[2]);
CashContext cashContext = null;
switch (rule) {
case 1://正常收费
cashContext = new CashContext(new CashNormal());
break;
case 2://打八折
cashContext = new CashContext(new CashRebate(0.8));
break;
case 3://满300-100
cashContext = new CashContext(new CashReturn(300,100));
break;
}
totalPrice += cashContext.getResult(price,count);
}
}
System.out.println(totalPrice);
System.out.println(priceList);
} catch (Exception e) {
System.out.println(e.toString());
} finally {
sc.close();
}
}
}
简单工厂模式与策略模式的“无效融合”方式
针对上面这个问题,我们在学习简单工厂模式的时候其实就已经知道如何去解决了,那就是创建一个工厂类,专门来负责对象的创建逻辑,在工厂类中我们可以根据用户传入的数据去判断创建什么样类型的对象。
于是我们又去写一个工厂类来负责判断需要什么类型的对象,用工厂类创建出来后把这个对象传到Context中去。如果这样的话,主程序中的代码就变成了下面这样:
import java.util.ArrayList;
import java.util.Scanner;
/**
* @Author yirui
* @Date 2024/4/10 10:44
* @Version 1.0
*/
public class AcceptCash3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double totalPrice = 0d;
try {
ArrayList<String> priceList = new ArrayList<>();
String acceptRule;//收费规则:单价,数量,优惠方式。逗号分隔
double price;
int rule,count;
while (true){
acceptRule = sc.nextLine();
if (acceptRule.equals("end")){
break;
}else {
priceList.add(acceptRule);
String[] list = acceptRule.split(",");
price = Double.parseDouble(list[0]);
count = Integer.parseInt(list[1]);
rule = Integer.parseInt(list[2]);
CashContext cashContext = null;
CashSuper cashAccept = CashFactory.createCashAccept(rule);
cashContext = new CashContext(cashAccept);
totalPrice += cashAccept.acceptPrice(price,count);
}
}
System.out.println(totalPrice);
System.out.println(priceList);
}catch (Exception e){
System.out.println(e.toString());
}finally {
sc.close();
}
}
}
看到上面这段代码大家是不是很别扭?当然别扭。经过这样一搞,原本简单工厂模式只有个工厂类,现在不仅工厂类又回来了,还多了一个Context类,这不是多此一举吗。而且我的主程序相关联的类还更多了,简单工厂模式本来只和工厂类、CashSuper类相关联,现在结合了策略模式后还和CashContext类关联上了。因此作者在文章中说了这么一句话:“难道简单工厂就一定要是一个单独的类吗?难道不可以与策略模式的Context结合?
简单工厂模式与策略模式的正确融合方式
既然工厂类生产出的类最后还是要直接传给Context类,那我们为什么不可以直接把工厂类的业务逻辑放到Context类里呢?话不多说,直接看代码:
public class CashContext1 {
private CashSuper cashSuper;
public CashContext1(int rule) {
switch (rule) {
case 1://正常收费
cashSuper = new CashNormal();
break;
case 2://打八折
cashSuper = new CashRebate(0.8);
break;
case 3://满300-100
cashSuper = new CashReturn(300,100);
break;
}
}
public double getResult(double price, int count) {
return cashSuper.acceptPrice(price, count);
}
}
改成这样之后,我们可以对比比一下他和工厂模式的区别在哪里。
public class CashFactory {
public static CashSuper createCashAccept(int type){
CashSuper cashSuper = null;
switch (type){
case 1://正常收费
cashSuper = new CashNormal();
break;
case 2://打八折
cashSuper = new CashRebate(0.8);
break;
case 3://满300-100
cashSuper = new CashReturn(300,100);
break;
}
return cashSuper;
}
}
- 在工厂类中,用户传入参数,工厂类返回一个CashSuper类型的对象,主程序需要通过返回的对象来自己调用对象的方法,获得最终的结果。
- 而在融合后的CashContext类中,CashContext类不给主程序返回对象,而是直接提供给用户一个可以调用的函数,然后主程序调用CashContext类的方法获得最终的结果。
他们两种方式的主函数如下所示:
import java.util.ArrayList;
import java.util.Scanner;
/**
* @Author yirui
* @Date 2024/4/10 10:44
* @Version 1.0
*/
public class AcceptCash {//简单工厂模式的主程序
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double totalPrice = 0d;
try {
ArrayList<String> priceList = new ArrayList<>();
String acceptRule;//收费规则:单价,数量,优惠方式。逗号分隔
double price;
int rule,count;
while (true){
acceptRule = sc.nextLine();
if (acceptRule.equals("end")){
break;
}else {
priceList.add(acceptRule);
String[] list = acceptRule.split(",");
price = Double.parseDouble(list[0]);
count = Integer.parseInt(list[1]);
rule = Integer.parseInt(list[2]);
CashSuper cashAccept = CashFactory.createCashAccept(rule);//耦合了CashSuper类和CashFactory类,当CashSuper类或者CashFactory类发生变化时有可能会影响到主程序。
totalPrice += cashAccept.acceptPrice(price,count);
}
}
System.out.println(totalPrice);
System.out.println(priceList);
}catch (Exception e){
System.out.println(e.toString());
}finally {
sc.close();
}
}
}
//
import java.util.ArrayList;
import java.util.Scanner;
/**
* @Author yirui
* @Date 2024/4/10 10:44
* @Version 1.0
*/
public class AcceptCash3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double totalPrice = 0d;
try {
ArrayList<String> priceList = new ArrayList<>();
String acceptRule;//收费规则:单价,数量,优惠方式。逗号分隔
double price;
int rule, count;
while (true) {
acceptRule = sc.nextLine();
if (acceptRule.equals("end")) {
break;
} else {
priceList.add(acceptRule);
String[] list = acceptRule.split(",");
price = Double.parseDouble(list[0]);
count = Integer.parseInt(list[1]);
rule = Integer.parseInt(list[2]);
CashContext1 cashContext = new CashContext1(rule);//只耦合了CashContext1类,CashSuper类完全分离出去了
totalPrice += cashContext.getResult(price,count);
}
}
System.out.println(totalPrice);
System.out.println(priceList);
} catch (Exception e) {
System.out.println(e.toString());
} finally {
sc.close();
}
}
}
对比上面两个主程序,虽然没有明显的差别,但是策略模式与简单工厂模式结合之后,主程序关联的类就只有CashContext类了,连CashSuper都从主程序中分离了,这样算法逻辑就彻底与页面逻辑Say goodBye了。无论后面CashSuper或者它的子类怎么变,都不会影响到页面逻辑。这就是策略模式与简单工厂模式结合的优势所在。
总结
简单工厂模式和策略模式的区别
简单工厂模式是创建型模式
,它的作用是创建对象。简单工厂模式的实质是由一个工厂类根据传入的参数,动态决定应该创建并且返回哪一个产品类(这些产品类继承自一个父类或接口)的实例。
策略模式是行为型模式
,作用是在许多行为中选择一种行为,关注的是行为的多样性。策略模式是将生成实例的使用策略放在策略类中配置后才提供调用方使用。在实例化策略模式的时候已经创建好了,我们可以再策略模式中随意的拼接重写方法。
总结一下,两者的区别主要在于:
简单工厂模式关注的是对象的创建,它只负责创建具体对象。
策略模式关注的是行为的选择,它将调用方法的职责交给了Context类。
注:对于设计的学习,可能需要在不断的使用中加深对它们的理解,文章中浅显的举例可能没办法将设计模式的优点全部体现出来。