最近深感项目面对需求改变时需要改动大量代码的烦恼
代码无错不是优,拒绝写bug!!!
于是学习<<大话设计模式>>,致力于写出优秀的代码,在此简单的做一下学习记录
简单工厂模式
定义: 容器究竟需要实例化谁,将来会不会增加实例化对象,都是容易变化的地方,应该考虑用一个独立的类来做这个创建实例的地方,这就是工厂
定义永远只是定义,难于理解,亲自实现一次才是学习正道,下面直接贴代码
需求: 用户输入两个数字和一个符号,做符号的运算并输出结果
改变点:如果新增开根运算,如何处理
为简单工厂做准备
首先需求是对两个数字做一定的操作,那么我们先定义操作类
public class Operation {
// 对应的get,set方法已省略
private double numA = 0;
private double numB = 0;
// 通过该方法获取操作后的结果
public double getRes() throws Exception {
double res = 0;
return res;
}
}
其次针对每一种操作需要写一个子类,并重写计算方法
public class OperateSub extends Operation {
public double getRes() {
double res = getNumA() - getNumB();
return res;
}
}
public class OperateAdd extends Operation {
public double getRes() {
double res = getNumA() + getNumB();
return res;
}
}
public class OperateMul extends Operation {
public double getRes() {
double res = getNumA() * getNumB();
return res;
}
}
public class OperateDiv extends Operation {
public double getRes() throws Exception {
if (getNumB() == 0) {
throw new Exception("分母不能为0");
}
double res = getNumA() / getNumB();
return res;
}
}
最后再写我们的工厂类
public class OperationFactory {
public static Operation createOperation(String operate) {
Operation oper = null;
if ("+".equals(operate)) {
oper = new OperateAdd();
} else if ("-".equals(operate)) {
oper = new OperateSub();
} else if ("*".equals(operate)) {
oper = new OperateMul();
} else if ("/".equals(operate)) {
oper = new OperateDiv();
} else {
// 如果新增开根运算,只需新建OperateRoot类
// 继承Operation并重写getRes()方法
// 并在此加入一个判断
}
return oper;
}
}
测试我们的工厂
public static void main(String[] args) {
try {
Scanner scan = new Scanner(System.in);
System.out.println("请输入数字a:");
String strNumA = scan.nextLine();
System.out.println("请输入运算符:");
String strOperate = scan.nextLine();
System.out.println("请输入数字b:");
String strNumB = scan.nextLine();
Operation oper = null;
oper = OperationFactory.createOperation(strOperate);
oper.setNumA(Double.parseDouble(strNumA));
oper.setNumB(Double.parseDouble(strNumB));
System.out.println(oper.getRes());
} catch (Exception e) {
System.out.println("您的输入有误");
e.printStackTrace();
}
}
优点
通过封装将
我也不知道,自己领悟吧~
大概就是将代码之间的耦合度降低,调用者不再需要关心创建何种实例(这一步交给了工厂去完成),如果再新增别的计算方法,调用者无需改动,而新增的逻辑也不会影响原有的逻辑
策略模式
定义: 它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户
只要在分析过程中听到需要在不同时间 应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性
需求:商场收银软件,根据购买商品的单价和数量,显示收费信息
变化点:后续如果新增打折功能,再增加满减功能该如何实现计算实际收费的信息
策略的设计
目的:使用不同的算法,根据应收总额计算实际需要收取费用
先定义接口,需要有计算现金的功能
public interface CashSuper {
double acceptCash(double money);
}
其次实现各个算法类,实现接口
// 正常收费
public class CashNormal implements CashSuper {
@Override
public double acceptCash(double money) {
return money;
}
}
// 打折
public class CashRebate implements CashSuper {
private double moneyRebate = 0;
public CashRebate(double moneyRebate) {
this.moneyRebate = moneyRebate;
}
@Override
public double acceptCash(double money) {
return money * moneyRebate;
}
}
// 满减
public class CashReturn implements CashSuper {
private double moneyCondition = 0;
private double moneyReturn = 0;
public CashReturn(double moneyCondition, double moneyReturn) {
this.moneyCondition = moneyCondition;
this.moneyReturn = moneyReturn;
}
@Override
public double acceptCash(double money) {
double res = money;
if (money >= moneyCondition) {
res = money - Math.floor(money / moneyCondition) * moneyReturn;
}
return res;
}
}
实现上下文类,维护策略的使用
public class CashContext {
private CashSuper cs;
public CashContext(CashSuper cs) throws Exception {
this.cs = cs;
}
public double getResult(double money) {
return cs.acceptCash(money);
}
}
测试代码
public static void main(String[] args) throws Exception {
double total = 100;
String type = "";
CashContext cc = null;
CashSuper cs = null;
if ("正常收费".equals(type)) {
cs = new CashNormal();
} else if ("满300反100".equals(type)) {
cs = new CashReturn(300, 100);
} else if ("打8折".equals(type)) {
cs = new CashRebate(0.8);
} else {
throw new Exception("没有所选算法");
}
cc = new CashContext(cs);
total = cc.getResult(total);
System.out.println(total);
}
优化:简单工厂模式和策略模式结合
只需要修改两个类: 测试方法和CashContext
public class CashContext {
private CashSuper cs;
public CashContext(String type) throws Exception {
if ("正常收费".equals(type)) {
cs = new CashNormal();
} else if ("满300反100".equals(type)) {
cs = new CashReturn(300, 100);
} else if ("打8折".equals(type)) {
cs = new CashRebate(0.8);
} else {
throw new Exception("没有所选算法");
}
}
public double getResult(double money) {
return cs.acceptCash(money);
}
}
测试方法:
public static void main(String[] args) throws Exception {
double total = 100;
String type = "打8折";
CashContext cc = new CashContext(type);
total = cc.getResult(total);
System.out.println(total);
}
总结
简单工厂模式和策略模式的结合,使得客户端本需要认识CashSuper和CashFactory变成了只需要认识CashContext
耦合度进一步降低
在结构上两个例子很相似,但是:
简单工厂模式的重点在于:使用工厂来封装创建实例的过程,使得客户端不再需要关注于此,直接使用即可
策略模式的重点在于: 将各种算法分开写,然后以相同的方式调用所有的算法,减少了算法的实现与调用者之间的耦合,策略模式封装了变化,其次简化了单元测试,每个算法单独写各自的测试用例