运用设计模式设计一个票务系统(工厂模式使用)

231 阅读5分钟

工厂模式在票务系统的使用

工厂模式回顾:【设计模式】创建型模式其二: 工厂方法模式(Java版) - 掘金 (juejin.cn)

工厂模式使用

因为有三种类型的票,每种票之间价格与信息不同。因此,我选择工厂模式去设计这三种票。

选择工厂模式的原因

  • 保证扩展性,万一我又想卖其它票,可以直接继承抽象工厂类。
  • 票的种类没有特别多,所以占用的内存也不会很大

那为什么不选用静态工厂和抽象工厂呢

  • 静态工厂不符合开闭性原则,当我票的种类增加必须修改源代码
  • 抽象工厂适合于生产一族产品, 但是开闭性也不好,添加删除票也需要修改源代码

具体的代码编写

我们现在正在对票进行管理,客户要买票,必须先录入票。因为票的种类目前有三个,所以我打算使用工厂模式.

小伙伴可能会问? 为啥不直接把前端传来的对象直接存在数据库

在一个票务系统中,使用工厂模式可以帮助您更好地组织和管理代码。工厂模式可以将对象的创建和具体实现分离开来,从而使代码更加灵活和可维护。如果您直接将票的对象存储在数据库中,虽然可能会更快一些,但这样做可能会导致以下问题:

  1. 难以管理和扩展:如果您需要增加新的票种类或者修改现有的票种类,那么直接将票的对象存储在数据库中可能会使代码变得混乱和难以维护。使用工厂模式,您可以将票的对象的创建和具体实现分离开来,从而使代码更加灵活和易于管理和扩展。
  2. 难以控制票的对象的状态:如果您直接将票的对象存储在数据库中,那么您可能需要在不同的地方修改票的对象的状态。这样会使代码变得复杂,难以维护。使用工厂模式,您可以将票的对象的状态控制在工厂类中,从而使代码更加简单和易于维护。
  3. 难以实现业务逻辑:如果您直接将票的对象存储在数据库中,那么您可能需要在代码中编写大量的业务逻辑来实现票务系统的各种功能。使用工厂模式,您可以将业务逻辑和票的对象的创建和具体实现分离开来,从而使代码更加清晰和易于实现。

抽象票类(拥有基本的属性和get,set方法)

// 这里其实就是ticket在数据库中的对应字段

@Getter
@Setter
@TableName("sys_ticket")
public class SysTicket implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    private String type;

    private String origin;

    private String destination;

    private LocalDateTime departureTime;

    private LocalDateTime arrivalTime;

    private BigDecimal price;
}

具体票类

通过set方法设置特有属性

// bus类, 公交车票

public class BusTicket extends SysTicket {
    // 设置自己独有的属性
    public BusTicket(){
        setType("BusTicket");
        setPrice(new BigDecimal("20.00"));
    }
}

// plane类,飞机票

public class PlaneTicket extends SysTicket {
    // 设置自己独有的属性
    public PlaneTicket(){
        setType("PlaneTicket");
        setPrice(new BigDecimal("30.00"));
    }
}

// train类,火车票

public class TrainTicket extends SysTicket {
    // 设置自己独有的属性
    public TrainTicket(){
        setType("TrainTicket");
        setPrice(new BigDecimal("50.00"));
    }
}

抽象工厂

// 返回票的抽象类(这里的抽象类不是指真的抽象类,而是一个抽象的概念)

public abstract class AbstractTicketFactory {
    public abstract SysTicket createTicketFactory();
}

具体工厂

实现抽象工厂的方法, 返回具体的票类

//Bus票工厂

public class BusFactory extends AbstractTicketFactory{
    @Override
    public SysTicket createTicketFactory() {
        return new BusTicket();
    }
}

// Plane票工厂

public class PlaneFactory extends AbstractTicketFactory{
    @Override
    public SysTicket createTicketFactory() {
        return new PlaneTicket();
    }
}

// Train票工厂

public class TrainFactory extends AbstractTicketFactory{
    @Override
    public SysTicket createTicketFactory() {
        return new TrainTicket();
    }
}

读取配置文件动态生成具体类

我既写了读取XML文件,又写了修改XML文件的代码,毕竟我们生成不同的对象需要修改XML文件,所以我写了update方法。我也写了返回当前XML文件内容的方法(用于比对需要生成的对象,避免重复修改配置文件)

知识点温习

小伙伴们忘记XML文件的读取和修改可以参照我之前的文章【常见配置文件】XML文件(Java) - 掘金 (juejin.cn)

public class TicketXmlUtil {
    // 返回具体的类
    public static Object getBean(){
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        try {
            DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
            Document document = documentBuilder.parse("D:\gitee\ticket\ticket-management-system\ticket\src\main" +
                    "\resources\ticket.xml");
            NodeList classname = document.getElementsByTagName("classname");
            Node item = classname.item(0).getFirstChild();
            String nodeValue = item.getNodeValue();
            Class<?> aClass = Class.forName(nodeValue);
            return aClass.getConstructor().newInstance();
        } catch (ParserConfigurationException | IOException | SAXException | ClassNotFoundException |
                 InvocationTargetException | InstantiationException | IllegalAccessException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
    // 更新XML文件
    public static boolean updateXml(String type){
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = null;
        try {
            documentBuilder = dbf.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        }
        Document document = null;
        try {
            document = documentBuilder.parse("D:\gitee\ticket\ticket-management-system\ticket\src\main" +
                    "\resources\ticket.xml");
        } catch (SAXException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        NodeList classname = document.getElementsByTagName("classname");
        Node item = classname.item(0).getFirstChild();
        item.setNodeValue("com.example.ticket.entity.concreteTicket." + type);
        // 保存修改后的XML文档
        TransformerFactory transformerFactory = TransformerFactory.newInstance();
        Transformer transformer = null;
        try {
            transformer = transformerFactory.newTransformer();
        } catch (TransformerConfigurationException e) {
            throw new RuntimeException(e);
        }
        DOMSource source = new DOMSource(document);
        StreamResult result = new StreamResult(new File("D:\gitee\ticket\ticket-management-system\ticket\src\main" +
        "\resources\ticket.xml"));
        try {
            transformer.transform(source, result);
        } catch (TransformerException e) {
            throw new RuntimeException(e);
        }
        return true;
    }
    // 返回当前类名
    public static String getClassName(){
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder documentBuilder = null;
        try {
            documentBuilder = dbf.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            throw new RuntimeException(e);
        }
        Document document = null;
        try {
            document = documentBuilder.parse("D:\gitee\ticket\ticket-management-system\ticket\src\main" +
                        "\resources\ticket.xml");
        } catch (SAXException | IOException e) {
            throw new RuntimeException(e);
        }
        NodeList classname = document.getElementsByTagName("classname");
        Node item = classname.item(0).getFirstChild();
        String nodeValue = item.getNodeValue();
        String[] split = nodeValue.split("\.");
        return split[split.length-1];
    }
}

代码的调用

我们的代码写在业务层service,所以生成具体类的代码也在此编写

先读取前端传来的ticket对象,读取其中的type类型,根据type类型去比对XML文件中的类型, 如果不一致就修改配置文件,否则生成具体类,最后将具体对象放入数据库存储。

@Service
public class SysTicketServiceImpl extends ServiceImpl<SysTicketMapper, SysTicket> implements ISysTicketService {
    public boolean addTicket(SysTicket sysTicket) {
        String type = sysTicket.getType();
        SysTicket ticket = null;
        // 比对当前文件的类名
        if (!type.equals(TicketXmlUtil.getClassName())) {
            TicketXmlUtil.updateXml(type);
        }
        ticket =(SysTicket) TicketXmlUtil.getBean();
        // 设置目的地
        ticket.setDestination(sysTicket.getDestination());
        // 设置出发地
        ticket.setOrigin(sysTicket.getOrigin());
        // 设置出发时间
        ticket.setDepartureTime(sysTicket.getDepartureTime());
        // 设置到达时间
        ticket.setArrivalTime(sysTicket.getArrivalTime());
        // 添加到数据库
        return baseMapper.insert(ticket) == 1;
    }
}

这样就完成了不同票的存储,如果之后还想添加其它种类的票,直接添加具体工厂类和具体产品类即可,不需要修改源代码。

下一篇:策略模式的使用