策略模式在票务系统的使用
为什么要选用策略模式
票务系统为用户分等级,普通用户,白银用户,黄金用户; 每种不同的用户都有不同的折扣策略。
同时,为了系统的开放性,我们设计的项目是可以添加新的用户级别同时不修改源代码。
这些就恰恰是策略模式能解决的问题,这就是为什么我要选它。
不懂策略模式的小伙伴可以看我的介绍: 【设计模式】行为型模式其九: 策略模式 - 掘金 (juejin.cn)
项目中书写
策略抽象类
// 定义打折方法
public interface Discount {
BigDecimal discount(BigDecimal price);
}
具体策略类
// 普通用户采取不打折策略
public class NormalDiscount implements Discount{
private final double discount;
public NormalDiscount(){
this.discount = 1.0;
}
@Override
public BigDecimal discount(BigDecimal price) {
return price.multiply(new BigDecimal(discount));
}
}
// 白银用户采取打八折的方式
public class SilverDiscount implements Discount{
private final double discount;
public SilverDiscount(){
this.discount = 0.8;
}
@Override
public BigDecimal discount(BigDecimal price) {
return price.multiply(new BigDecimal(discount));
}
}
// 黄金用户采取打六折的方式
public class GoldDiscount implements Discount{
private final double discount;
public GoldDiscount(){
this.discount = 0.6;
}
@Override
public BigDecimal discount(BigDecimal price) {
return price.multiply(new BigDecimal(discount));
}
}
具体环境类
将打折功能放在一个对象内,公开一个获得价格接口,便于调用和统一处理
public class Price {
private BigDecimal price;
private Discount discount;
public void setPrice(BigDecimal price){
this.price = price;
}
public void setDiscount(Discount discount){
this.discount = discount;
}
public BigDecimal getPrice(){
return discount.discount(this.price);
}
}
XML 文件
<?xml version="1.0" encoding="UTF-8" ?>
<config>
<className>com.example.ticket.service.stratege.SilverDiscount</className>
</config>
XMl文件读取类
public class DiscountXmlUtil {
public static Object getBean(){
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
try {
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.parse("D:\gitee\ticket\ticket-management-system\ticket\src\main" +
"\resources" +
"\discount.xml");
NodeList className = doc.getElementsByTagName("className");
Node firstChild = className.item(0).getFirstChild();
String nodeValue = firstChild.getNodeValue();
Class<?> aClass = Class.forName(nodeValue);
Constructor<?> constructor = aClass.getConstructor();
return Class.forName(nodeValue).getConstructor().newInstance();
} catch (ParserConfigurationException | IOException | SAXException | ClassNotFoundException |
InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
}
具体调用类
我们这算是一个系统,所以我们何时会去使用价格打折机制呢?
没错,就是生成订单的时候,所以我们调用方法写在service层里。
那我们肯定是要根据前端传来的用户信息,读取用户类的身份字段,并为其选择合适的打折策略。
@Override
public SysOrder getOrder() {
// 获得价格对象
Price price = new Price();
// 设置商品价格
price.setPrice(new BigDecimal("10.00"));
// 设置打折策略, 根据XML文件来设置
price.setDiscount((Discount) DiscountXmlUtil.getBean());
// 通过统一接口来获得价格
BigDecimal price1 = price.getPrice();
// 之后就是对Order对象的封装, 省略。。。。。。
System.out.println(price1);
return null;
}
一些细节提示
- 具体类里的discount字段属于赋值一次,永远不变的,所以加上final字段。
- 当你想要添加其它打折策略, 可以直接继承Discount,然后改写方法即可
- 当你还要进行一些特殊操作,比如添加积分,可以在具体打折类进行操作。当然,我更建议在Price对象里进行操作,毕竟这算是一种公共操作,可以在Price里统一进行。
- 根据XML文件来动态生成策略类,这样虽然不会修改源代码,但是需要根据用户身份来修改XML文件,所以我们还得在DiscountXMLUtil类中书写修改XML文件的方法。