运用设计模式设计一个票务系统(策略模式使用)

544 阅读3分钟

策略模式在票务系统的使用

为什么要选用策略模式

票务系统为用户分等级,普通用户,白银用户,黄金用户; 每种不同的用户都有不同的折扣策略。

同时,为了系统的开放性,我们设计的项目是可以添加新的用户级别同时不修改源代码。

这些就恰恰是策略模式能解决的问题,这就是为什么我要选它。

不懂策略模式的小伙伴可以看我的介绍: 【设计模式】行为型模式其九: 策略模式 - 掘金 (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文件的方法。