好的,所以你有很多问题,但是,让我们首先关注“文件写入”问题。
一个非常重要的起点是Basic I/O 课程,这样您就可以了解基础知识。
任何解决方案的一个重要目标应该是减少耦合(类之间)。其中一部分是减少解决方案的其他部分可能拥有的系统“知识”(或实施细节)数量的过程。他们只是不应该关心。
所以,我们要做的第一件事就是管理“预订”,让我们从这里开始吧。您已经确定了预订的基本要求,我们可以这样描述...
public interface Booking {
public String getName();
public String getPhoneNumber();
public String getAddress();
public String getDescription();
public LocalDate getDateOfBirth();
}
现在,我们需要某种方式让系统的其他部分与这些数据进行交互,从而将它们与底层实现分离,我的意思是,为什么预订视图会关心预订的存储方式,它只是关心它可以使它们:/
所以,让我们添加一个PatientRepository
public interface PatientRepository {
public Booking createBooking(String name, String phoneNumber, String address, String description, LocalDate dob) throws IOException;
}
好的,所以,这“描述”了系统的各个部分,提供了有关如何创建某些元素的信息,我们接下来需要的是某种实现。
预订是非常基本的,它只是一个 POJO,封装了捕获的信息......
public class DefaultBooking implements Booking {
private String name;
private String phoneNumber;
private String address;
private String description;
private LocalDate dateOfBirth;
public DefaultBooking(String name, String phoneNumber, String address, String description, LocalDate dateOfBirth) {
this.name = name;
this.phoneNumber = phoneNumber;
this.address = address;
this.description = description;
this.dateOfBirth = dateOfBirth;
}
public String getName() {
return name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getAddress() {
return address;
}
public String getDescription() {
return description;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
}
PatientRepository 有点复杂。
滚动您自己的文件格式总是一项繁重的工作,我会考虑使用JAXB/XML、JSON、CSV 之类的东西(使用OpenCSV 或Apache Commons CSV 之类的东西),但最终,您的最终目标应该是使用某种数据库并使用JDBC(可能使用H2 或HSQL)(恕我直言)
对于这个例子,我们只是滚动一个简单的基于行的文件,使用; 作为属性分隔符(或分隔符)
public class DefaultPatientRepository implements PatientRepository {
private List<Booking> bookings;
public DefaultPatientRepository() throws IOException {
bookings = new ArrayList<>(32);
File sourceFile = new File("Clinic.txt");
if (sourceFile.exists()) {
try (BufferedReader br = new BufferedReader(new FileReader(sourceFile))) {
String line = null;
while ((line = br.readLine()) != null) {
String parts[] = line.split(";");
String name = parts[0];
String phoneNumber = parts[1];
String address = parts[2];
String description = parts[3];
String dob = parts[4];
LocalDate dateOfBirth = LocalDate.parse(dob, DateTimeFormatter.ISO_LOCAL_DATE);
Booking booking = new DefaultBooking(name, phoneNumber, address, description, dateOfBirth);
bookings.add(booking);
}
}
System.out.println("!! Loaded " + bookings.size() + " bookings");
}
}
@Override
public Booking createBooking(String name, String phoneNumber, String address, String description, LocalDate dob) throws IOException {
// Rolling your own file format is just a complete pain in the ... code
// To my mind, I'd explore XML, JSON and possibly even CSV if you're
// not ready for a SQL based database solution, but I'm old and I'm lazy :P
try (BufferedWriter bw = new BufferedWriter(new FileWriter(new File("Clinic.txt"), true))) {
StringJoiner joiner = new StringJoiner(";");
joiner.add(name);
joiner.add(phoneNumber);
joiner.add(address);
joiner.add(description);
joiner.add(DateTimeFormatter.ISO_LOCAL_DATE.format(dob));
bw.write(joiner.toString());
bw.newLine();
}
return new DefaultBooking(name, phoneNumber, address, description, dob);
}
}
所以,不用做任何其他事情,我们现在有了一个基本的解决方案。您可以对其进行测试并验证其功能,而无需包含系统的任何其他部分。
但请注意,此解决方案会将Clinic.txt 存储在当前工作目录中,这可能会因多种原因造成问题。
- 工作目录不是静态的,而是与程序启动位置相关的上下文
- 当前工作目录可能被写保护(这种情况发生的频率比您想象的要多)
为此,我想看看我对java- reading the properties file outside of jar file 或Get current path of executed file 的回答,它描述了基于平台使用“众所周知的位置”
现在,我们已经到了可以引入 UI 的地步了。
我强烈建议您查看A Visual Guide to Layout Managers 和How to Use CardLayout
用户界面通常非常复杂,涉及多种不同的视图和交互。您希望将时间花在将视图彼此分离并管理职责范围上。
例如,在您的基本示例中,您有三个不同的视图。
- 菜单
- 预订视图
- 患者视图(可能涉及某种搜索,这将添加另一个视图)
菜单不关心其他两个,也没有责任在用户请求时显示它们之外的管理它们。
同样,其他视图也不关心菜单。他们真正需要做的就是告诉“某个机构”用户不再需要他们。
以下示例试图演示基于CardLayout 使用的“可能”解决方案,它利用了诸如
您应该注意的一个特殊功能是更改 PatientRepository 的底层实现的容易程度(使用不同的文件格式、本地数据库甚至 Web 服务),因为实现是与系统的其余部分没有紧密耦合
import java.awt.CardLayout;
import java.awt.EventQueue;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.EventListener;
import java.util.EventObject;
import java.util.List;
import java.util.StringJoiner;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
PatientRepository patientRepository = new DefaultPatientRepository();
JFrame frame = new JFrame();
frame.add(new ClinicPane(patientRepository));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(null, "Failed to load patient repository", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
}
public class ClinicPane extends JPanel {
private CardLayout cardLayout;
private PatientRepository patientRepository;
private DismissablePaneListener dismissablePaneListener = new DismissablePaneListener() {
@Override
public void didDismissView(DismissablePaneEvent evt) {
// If you really wanted to you could consider having some kind
// of idenitifier for the view and then have some custom logic
// in place to determine which view to show next, but in this
// case, we just want to show the menu again
cardLayout.show(ClinicPane.this, "MainMenu");
}
};
public ClinicPane(PatientRepository patientRepository) {
this.patientRepository = patientRepository;
cardLayout = new CardLayout();
setLayout(cardLayout);
add(new MenuPane(new MenuListener() {
@Override
public void didSelectMenuOption(MenuEvent evt) {
switch (evt.getOption()) {
case MAKE_A_BOOKING:
cardLayout.show(ClinicPane.this, "Booking");
break;
case VIEW_PATIENT_RECORD:
cardLayout.show(ClinicPane.this, "Patient");
break;
}
}
}), "MainMenu");
BookingPane bookingPane = new BookingPane(patientRepository);
configure(bookingPane);
add(bookingPane, "Booking");
PatientPane patientPane = new PatientPane(patientRepository);
configure(patientPane);
add(patientPane, "Patient");
}
protected DismissablePaneListener getDismissablePaneListener() {
return dismissablePaneListener;
}
protected void configure(DismissablePane dismissablePane) {
dismissablePane.addDismissablePaneListener(getDismissablePaneListener());
}
public PatientRepository getPatientRepository() {
return patientRepository;
}
}
public enum MenuOption {
MAKE_A_BOOKING,
VIEW_PATIENT_RECORD,
}
public class MenuEvent extends EventObject {
private MenuOption option;
public MenuEvent(Object source, MenuOption option) {
super(source);
this.option = option;
}
public MenuOption getOption() {
return option;
}
}
public interface MenuListener extends EventListener {
public void didSelectMenuOption(MenuEvent evt);
}
public class MenuPane extends JPanel {
public MenuPane() {
this(null);
}
public MenuPane(MenuListener listener) {
if (listener != null) {
addMenuListener(listener);
}
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(8, 8, 8, 8);
add(new JLabel("Please select one Option"), gbc);
add(makeButtonFor(MenuOption.MAKE_A_BOOKING), gbc);
add(makeButtonFor(MenuOption.VIEW_PATIENT_RECORD), gbc);
}
public void addMenuListener(MenuListener listener) {
listenerList.add(MenuListener.class, listener);
}
public void removeMenuListener(MenuListener listener) {
listenerList.remove(MenuListener.class, listener);
}
protected JButton makeButtonFor(MenuOption action) {
JButton btn = new JButton("Unknown action");
switch (action) {
case MAKE_A_BOOKING:
btn.setText("Make a booking");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireDidSelectMenuOption(action);
}
});
break;
case VIEW_PATIENT_RECORD:
btn.setText("View patient record");
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireDidSelectMenuOption(action);
}
});
break;
}
return btn;
}
protected void fireDidSelectMenuOption(MenuOption option) {
MenuListener[] listeners = listenerList.getListeners(MenuListener.class);
if (listeners.length == 0) {
return;
}
MenuEvent evt = new MenuEvent(this, option);
for (MenuListener listener : listeners) {
listener.didSelectMenuOption(evt);
}
}
}
public class DismissablePaneEvent extends EventObject {
public DismissablePaneEvent(DismissablePane source) {
super(source);
}
public DismissablePane getDismissablePane() {
return (DismissablePane) getSource();
}
}
public interface DismissablePaneListener extends EventListener {
public void didDismissView(DismissablePaneEvent evt);
}
public interface DismissablePane {
public JPanel getPane();
public void addDismissablePaneListener(DismissablePaneListener listener);
public void removeDismissablePaneListener(DismissablePaneListener listener);
}
public abstract class AbstractDismissablePane extends JPanel implements DismissablePane {
@Override
public JPanel getPane() {
return this;
}
@Override
public void addDismissablePaneListener(DismissablePaneListener listener) {
listenerList.add(DismissablePaneListener.class, listener);
}
@Override
public void removeDismissablePaneListener(DismissablePaneListener listener) {
listenerList.remove(DismissablePaneListener.class, listener);
}
protected void fireDidDismissPane() {
DismissablePaneListener[] listeners = listenerList.getListeners(DismissablePaneListener.class);
if (listeners.length == 0) {
return;
}
DismissablePaneEvent evt = new DismissablePaneEvent(this);
for (DismissablePaneListener listener : listeners) {
listener.didDismissView(evt);
}
}
}
public class BookingPane extends AbstractDismissablePane {
private PatientRepository patientRepository;
public BookingPane(PatientRepository patientRepository) {
this.patientRepository = patientRepository;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(8, 8, 8, 8);
add(new JLabel("Please make a booking"), gbc);
add(new JLabel("This is where you'd collect all the booking details"), gbc);
JButton save = new JButton("Save");
JButton cancel = new JButton("Cancel");
save.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
// Create an instance of Booking
Booking booking = getPatientRepository().createBooking("name", "phone number", "address", "description", LocalDate.now());
// Dismiss the view
fireDidDismissPane();
} catch (IOException ex) {
ex.printStackTrace();
JOptionPane.showMessageDialog(BookingPane.this, "Failed to create booking", "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
cancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireDidDismissPane();
}
});
JPanel buttonPane = new JPanel(new GridLayout(1, 2));
buttonPane.add(cancel);
buttonPane.add(save);
add(buttonPane, gbc);
}
public PatientRepository getPatientRepository() {
return patientRepository;
}
}
public class PatientPane extends AbstractDismissablePane {
private PatientRepository patientRepository;
public PatientPane(PatientRepository patientRepository) {
this.patientRepository = patientRepository;
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(8, 8, 8, 8);
add(new JLabel("Patient Record"), gbc);
add(new JLabel("All your record belong to us"), gbc);
JButton okay = new JButton("Okay");
okay.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
fireDidDismissPane();
}
});
add(okay, gbc);
}
public PatientRepository getPatientRepository() {
return patientRepository;
}
}
public interface Booking {
public String getName();
public String getPhoneNumber();
public String getAddress();
public String getDescription();
public LocalDate getDateOfBirth();
}
public interface PatientRepository {
public Booking createBooking(String name, String phoneNumber, String address, String description, LocalDate dob) throws IOException;
}
public class DefaultBooking implements Booking {
private String name;
private String phoneNumber;
private String address;
private String description;
private LocalDate dateOfBirth;
public DefaultBooking(String name, String phoneNumber, String address, String description, LocalDate dateOfBirth) {
this.name = name;
this.phoneNumber = phoneNumber;
this.address = address;
this.description = description;
this.dateOfBirth = dateOfBirth;
}
public String getName() {
return name;
}
public String getPhoneNumber() {
return phoneNumber;
}
public String getAddress() {
return address;
}
public String getDescription() {
return description;
}
public LocalDate getDateOfBirth() {
return dateOfBirth;
}
}
public class DefaultPatientRepository implements PatientRepository {
private List<Booking> bookings;
public DefaultPatientRepository() throws IOException {
bookings = new ArrayList<>(32);
File sourceFile = new File("Clinic.txt");
if (sourceFile.exists()) {
try (BufferedReader br = new BufferedReader(new FileReader(sourceFile))) {
String line = null;
while ((line = br.readLine()) != null) {
String parts[] = line.split(";");
String name = parts[0];
String phoneNumber = parts[1];
String address = parts[2];
String description = parts[3];
String dob = parts[4];
LocalDate dateOfBirth = LocalDate.parse(dob, DateTimeFormatter.ISO_LOCAL_DATE);
Booking booking = new DefaultBooking(name, phoneNumber, address, description, dateOfBirth);
bookings.add(booking);
}
}
System.out.println("!! Loaded " + bookings.size() + " bookings");
}
}
@Override
public Booking createBooking(String name, String phoneNumber, String address, String description, LocalDate dob) throws IOException {
// Rolling your own file format is just a complete pain in the ... code
// To my mind, I'd explore XML, JSON and possibly even CSV if you're
// not ready for a SQL based database solution, but I'm old and I'm lazy :P
try (BufferedWriter bw = new BufferedWriter(new FileWriter(new File("Clinic.txt"), true))) {
StringJoiner joiner = new StringJoiner(";");
joiner.add(name);
joiner.add(phoneNumber);
joiner.add(address);
joiner.add(description);
joiner.add(DateTimeFormatter.ISO_LOCAL_DATE.format(dob));
bw.write(joiner.toString());
bw.newLine();
}
return new DefaultBooking(name, phoneNumber, address, description, dob);
}
}
}
我敢打赌,你现在连问了都不好意思;)