gitee 地址:cash_system_use_design_pattern
代码结构如上,建议下载,结合文章进行分析
01_不使用设计模式
不使用设计模式,完成收银系统
输入商品销售模式:1 原价 2 打折(8折) 3 满减(300 - 100)
请输入商品的销售模式
while() {
请输入商品的单价
请输入商品的数量
每次输出结果:
单价 xx 元,数量 xx 个,本次合计 xx 元
总计 xxx 元
}
public class CashSystem {
private Double totalPrices = 0d;
public void cashSystemMethod() {
System.out.println("(1)正常销售\n" + "(2)打折\n" + "(3)满减(300 - 100)");
System.out.println("请输入商品销售模式:");
int mode = Integer.parseInt(new Scanner(System.in).nextLine());
double goodPrice;
int goodCount;
do {
System.out.println("请输入商品的单价:");
goodPrice = Double.parseDouble(new Scanner(System.in).nextLine());
System.out.println("请输入商品的数量:");
goodCount = Integer.parseInt(new Scanner(System.in).nextLine());
// -----------------------------------------------------------
double curPrices = goodPrice * goodCount;
switch (mode) {
case 1:
break;
case 2:
curPrices = curPrices * 0.8;
break;
case 3:
int subTimes = (int) Math.floor(curPrices / 300);
if (subTimes > 0) {
curPrices = curPrices - 100 * subTimes;
}
break;
default:
break;
}
// -----------------------------------------------------------
totalPrices += curPrices;
System.out.println("单价 " + goodPrice + " 元,数量 " + goodCount +
" 个,本次合计 " + curPrices + " 元");
System.out.println("总计" + totalPrices + " 元");
} while (goodPrice > 0 && goodCount > 0);
}
public static void main(String[] args) {
new CashSystem().cashSystemMethod();
}
}
02_简单工厂模式
简单工厂与工厂模式的区别:
- 简单工厂:工厂类 => 实例
- 工厂模式:工厂类 => 具体工厂 => 实例
将注释 ----- 中的逻辑修改为
// -----------------------------------------------------------
CashCalFactory cashCalFactory = new CashCalFactory(mode);
// 从工厂中获取`收银计算的逻辑`(缺点:需知道工厂生成的`对象类型,也就是`CashSuper)
CashSuper cashCalculate = cashCalFactory.getCashCalculate();
// 进行计算
Double curPrices = cashCalculate.calculateMoney(goodPrice, goodCount);
// -----------------------------------------------------------
工厂定义
public class CashCalFactory {
private CashSuper cashSuper;
public CashCalFactory(Integer mode) {
switch (mode) {
case 1:
cashSuper = new Normal();
break;
case 2:
cashSuper = new Discount();
break;
case 3:
cashSuper = new FullSub();
break;
default:
break;
}
}
public CashSuper getCashCalculate() {
return cashSuper;
}
}
收银方式接口的定义
/**
* 收银模式的接口
*/
public interface CashSuper {
/**
* 具体的收银方法
*/
Double calculateMoney(double price, int num);
}
实现类的定义
public class Normal implements CashSuper {
@Override
public Double calculateMoney(double price, int num) {
return price * num;
}
}
public class Discount implements CashSuper {
private double discountRate = 0.8d;
@Override
public Double calculateMoney(double price, int num) {
return price * num * getDiscountRate();
}
public double getDiscountRate() {
return discountRate;
}
public void setDiscountRate(double discountRate) {
this.discountRate = discountRate;
}
}
public class FullSub implements CashSuper {
private double fullCondition = 300d;
private double subMoney = 100d;
@Override
public Double calculateMoney(double price, int num) {
double curPrices = price * num;
int subTimes = (int) Math.floor(curPrices / fullCondition);
if (subTimes > 0) {
return curPrices - subMoney * subTimes;
}
return curPrices;
}
public void setFullCondition(double fullCondition) {
this.fullCondition = fullCondition;
}
public void setSubMoney(double subMoney) {
this.subMoney = subMoney;
}
}
03_策略 + 工厂
策略模式,其本质在于替换:定义一个字段,通过 set 方法,对这个字段进行更新(策略上的更新)
使用策略模式,可解决工厂模式所遗留下来的问题(需要知道工厂返回的类型 SuperCash)
// -----------------------------------------------------------
CashContext cachContext = new CashContext(mode);
// new CashContext(mode) 之后,可直接获取结果,不需要使用者关心调用逻辑
Double curPrices = cachContext.getResult(goodPrice, goodCount);
// -----------------------------------------------------------
如下,之后使用者可通过 setCashSuper 方法对 cashSuper 进行替换
public class CashContext {
private CashSuper cashSuper;
public CashSuper getCashCalculate() {
return cashSuper;
}
public CashContext(int mode) {
CashCalFactory cashCalFactory = new CashCalFactory(mode);
// 由上下文获取工厂生成的`对象类型`
cashSuper = cashCalFactory.getCashCalculate();
}
public Double getResult(double goodPrice, int goodCount) {
// 使用者可直接使用
return this.cashSuper.calculateMoney(goodPrice, goodCount);
}
public CashSuper getCashSuper() {
return cashSuper;
}
public void setCashSuper(CashSuper cashSuper) {
this.cashSuper = cashSuper;
}
}
04_新需求,打折后,再满减
优惠大酬宾:全场8折,打折之后再满减(满 500 - 100)
public CashCalFactory(Integer mode) {
switch (mode) {
case CashMode.NORMAL:
cashSuper = new Normal();
break;
case CashMode.DISCOUNT:
cashSuper = new Discount();
break;
case CashMode.FULLSUB:
cashSuper = new FullSub();
break;
case CashMode.GREATEDISCOUNT:
// 缺点:该功能组合了 Discount 与 FullSub,存在逻辑的重复
cashSuper = new GreateDiscount();
break;
default:
break;
}
}
新需求实现逻辑
public class GreateDiscount implements CashSuper {
private double discountRate = 0.8d;
private double fullCondition = 500d;
private double subMoney = 100d;
public Double calculateMoney(double price, int num) {
// 打折
double curPrices = price * num * getDiscountRate();
// 满减
int subTimes = (int) Math.floor(curPrices / fullCondition);
if (subTimes > 0) {
return curPrices - subMoney * subTimes;
}
return curPrices;
}
public double getDiscountRate() {
return discountRate;
}
public void setDiscountRate(double discountRate) {
this.discountRate = discountRate;
}
public void setFullCondition(double fullCondition) {
this.fullCondition = fullCondition;
}
public void setSubMoney(double subMoney) {
this.subMoney = subMoney;
}
}
05_装饰器
利用装饰器,解决逻辑重复的问题
装饰的过程,相当于穿衣服(入栈操作),执行的过程,相当于脱衣服(出栈操作)
public CashCalFactory(Integer mode) {
switch (mode) {
// ......
case CashMode.GREATEDISCOUNT:
Discount discount = new Discount();
FullSub fullSub = new FullSub();
// 设置满 500 - 100 的参数
fullSub.setFullCondition(500d);
fullSub.setSubMoney(100d);
// 调用完折扣,再调用装饰的类(满减)
discount.decorator(fullSub);
cashSuper = discount;
break;
default:
break;
}
}
public interface ICash {
Double calculateMoney(double price, int num);
}
将原来的 CashSuper 接口 变为 class,使得其子类可以进行 decorator
public class CashSuper implements ICash {
protected ICash component;
public void decorator(ICash component) {
this.component = component;
}
@Override
public Double calculateMoney(double price, int num) {
// 如果其子类实现了 decorator,调用
if (component != null) {
return component.calculateMoney(price, num);
}
return price;
}
}
public class Normal extends CashSuper {
@Override
public Double calculateMoney(double price, int num) {
return super.calculateMoney(price * num, 1);
}
}
public class Discount extends CashSuper {
private double discountRate = 0.8d;
@Override
public Double calculateMoney(double price, int num) {
double curPrices = price * num * getDiscountRate();
return super.calculateMoney(curPrices, 1);
}
public double getDiscountRate() {
return discountRate;
}
public void setDiscountRate(double discountRate) {
this.discountRate = discountRate;
}
}
public class FullSub extends CashSuper {
private double fullCondition = 300d;
private double subMoney = 100d;
@Override
public Double calculateMoney(double price, int num) {
double curPrices = price * num;
int subTimes = (int) Math.floor(curPrices / fullCondition);
if (subTimes > 0) {
return super.calculateMoney(curPrices - subMoney * subTimes, 1);
}
return super.calculateMoney(curPrices, 1);
}
public void setFullCondition(double fullCondition) {
this.fullCondition = fullCondition;
}
public void setSubMoney(double subMoney) {
this.subMoney = subMoney;
}
}
06_Switch 优化
利用反射的方式,对 switch 进行优化
同时,对 newInstance 的结果进行缓存
public class CashCalFactory {
private final CashSuper cashSuper;
static Map<Integer, Class<? extends CashSuper>> operationMap = new HashMap<>();
static Map<Class<? extends CashSuper>, CashSuper> operation_cache_map = new HashMap<>();
static {
operationMap.put(CashMode.NORMAL, Normal.class);
operationMap.put(CashMode.DISCOUNT, Discount.class);
operationMap.put(CashMode.FULLSUB, FullSub.class);
operationMap.put(CashMode.GREATEDISCOUNT, GreateDiscount.class);
}
public CashCalFactory(Integer mode) {
Class<? extends CashSuper> clazz = operationMap.get(mode);
CashSuper operationObject = operation_cache_map.get(clazz);
if (operationObject != null) {
cashSuper = operationObject;
} else {
try {
cashSuper = clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
public CashSuper getCashCalculate() {
return cashSuper;
}
}
07_新需求,满减后,再打折
变换所引起的变更,要尽可能地小
目前缺点:新增功能,需添加
(1) CashMode.FULLSUBANDDISCOUNT
(2) 添加新类:FullSubAndDiscount
(3) operationMap.put
优化方式:
- Normal,Discount、FullSub,可统一为:
先满减,后打折
以及先打折,后满减
- 可将逻辑提取到 data.properties 中,之后通过文件读取的方式,读取,加载即可
先满减,后打折
public class FullSubAndDiscount extends CashSuper {
private double discountRate = 0.8d;
private double fullCondition = 500d;
private double subMoney = 100d;
public FullSubAndDiscount(double discountRate, double fullCondition, double subMoney) {
this.discountRate = discountRate;
this.fullCondition = fullCondition;
this.subMoney = subMoney;
}
@Override
public Double calculateMoney(double price, int num) {
Discount discount = new Discount();
// 设置折扣
discount.setDiscountRate(getDiscountRate());
FullSub fullSub = new FullSub();
// 设置满减参数
fullSub.setFullCondition(getFullCondition());
fullSub.setSubMoney(getSubMoney());
// 调用完满减,再调用所装饰的类(折扣)
fullSub.decorator(discount);
Double result = fullSub.calculateMoney(price, num);
return super.calculateMoney(result, 1);
}
// getter、setter .......
}
先打折,后满减
public class DisCountAndFullSub extends CashSuper {
private double discountRate = 0.8d;
private double fullCondition = 500d;
private double subMoney = 100d;
public DisCountAndFullSub(double discountRate, double fullCondition, double subMoney) {
this.discountRate = discountRate;
this.fullCondition = fullCondition;
this.subMoney = subMoney;
}
@Override
public Double calculateMoney(double price, int num) {
Discount discount = new Discount();
// 设置折扣
discount.setDiscountRate(getDiscountRate());
FullSub fullSub = new FullSub();
// 设置满减参数
fullSub.setFullCondition(getFullCondition());
fullSub.setSubMoney(getSubMoney());
// 调用完折扣,再调用所装饰的类(满减)
discount.decorator(fullSub);
Double result = discount.calculateMoney(price, num);
return super.calculateMoney(result, 1);
}
// getter、setter .......
}
提取
# 正常销售
strategy1=DisCountAndFullSub,1d,0d,0d
# 打折
strategy2=DisCountAndFullSub,0.8d,0d,0d
# 满减(300 - 100)
strategy3=DisCountAndFullSub,1d,300d,100d
# 先打 8 折,再满减(500 - 100)
strategy4=DisCountAndFullSub,0.8d,500d,100d
# 先满减(500 - 100),再打 8 折
strategy5=FullSubAndDiscount,0.8d,500d,100d
08_CashCalculateFactory 中读取参数,利用反射进行动态加载
public class CashCalculateFactory {
private final CashSuper cashSuper;
private static final String BASE_PATH = "algorithm.impl.advance";
static Map<String, CashSuper> operation_cache_map = new HashMap<>();
static List<List<String>> collect;
static {
Path path = Paths.get("target/classes/data.properties");
// 字符串处理,如:strategy1=DisCountAndFullSub,1d,0d,0d
Function<String, List<String>> getPropertiesFromMap = s -> {
String value = s.split("=")[1];
String[] functionAndArgs = value.split(",");
ArrayList<String> functionAndArgsList = new ArrayList<>();
functionAndArgsList.add(functionAndArgs[0]);
functionAndArgsList.add(functionAndArgs[1]);
functionAndArgsList.add(functionAndArgs[2]);
functionAndArgsList.add(functionAndArgs[3]);
return functionAndArgsList;
};
try {
collect = Files.readAllLines(path).stream()
// 过滤 # 的注释
.filter(v -> !"#".equals(v.substring(0, 1)))
.map(getPropertiesFromMap)
.collect(Collectors.toList());
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public CashCalculateFactory(Integer mode) {
List<String> config = collect.get(mode - 1);
String functionName = config.get(0).trim();
String className = "cn.zhang.cash_system.fff_file_end.factory" +
"." +
BASE_PATH +
"." +
functionName;
if (operation_cache_map.get(className) != null) {
cashSuper = operation_cache_map.get(className);
} else {
try {
cashSuper = (CashSuper) Class.forName(className)
.getDeclaredConstructor(new Class[]{double.class, double.class, double.class})
.newInstance(new Object[]{
Double.parseDouble(config.get(1).trim()),
Double.parseDouble(config.get(2).trim()),
Double.parseDouble(config.get(3).trim())
});
operation_cache_map.put(className, cashSuper);
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException | NoSuchMethodException |
InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
public CashSuper getCashCalculate() {
return cashSuper;
}
}
- 参考
- 《大话设计模式 Java》