软件工程Test1项目总结
1.主要功能
在正确输入所需信息的前提下,本程序提供某地区地铁路线的线路详情与最短线路查询功能
2.实现语言
JAVA
3.实现算法
主要算法为Dijkstra算法
4.类职责划分(将相关类的功能进行描述)
本程序大致划分为3部分:UI类,封装类,控制类(数据生成类)
1).UI类
提供用户界面:包括欢迎界面(功能选择),线路查询界面,线路详情界面,提示窗口等
线路查询界面下,用户输入起始站与终点站,开始查询后,若系统没有检测到起始站或终点站,则弹出提示框;查询完成后,在结果窗口输出所求线路
线路详情界面下,用户选择某一条线路,查询完成后,在结果窗口输出该线路下的所有站点
2).封装类
包括地铁站点类(节点),地铁线路类(边),权重图构造类等
原始信息会经过数据构造后存放到相应的封装类
3).控制类
包含处理算法,对权重图进行初始化与计算
计算任意两个站点间的最短路径并按一定格式输出
5.核心代码(将所有类的代码标注并提交)
控制部分:
DataBuilder类
public class DataBuilder /*throws IOException*/{ public static Set<List<Station>> lineSet = new HashSet<List<Station>>();//所有线集合 public static List<Line> AllLine = new ArrayList<Line>();//所有线集合 public static Map<String, List<Station>> map = new HashMap<String, List<Station>>(); public static List<Station> mapOfStation = new ArrayList<Station>();//存放所有站点 public static int totalStaion = 0;//总的站点数量 public static void setStations(List<Station> linemodel, String str) { String line=str; String[] line1Arr = line.split(" "); int flag=0; String linename = null; Line newline=new Line(); for(String s : line1Arr){ if(flag==0){ linename=s; newline.setName(linename); } else{ Station station=new Station(s); for(List<Station> lineP : DataBuilder.lineSet){ if(lineP.contains(station)){ DataBuilder.totalStaion -=1;//在其他线路出现过就减1站,不然会重复计算 break; } } mapOfStation.add(station); station.setLinename(linename); linemodel.add(station); } flag=1; } for(int i =0;i<linemodel.size();i++){ if(i<linemodel.size()-1){ linemodel.get(i).next = linemodel.get(i+1); linemodel.get(i+1).prev = linemodel.get(i); } } newline.setStations(linemodel); AllLine.add(newline); map.put(linename,linemodel); } static { for(int i=0;i<Subway.lines.size();i++){ List<Station> line = new ArrayList<Station>(); setStations(line,Subway.lines.get(i)); totalStaion += line.size(); lineSet.add(line); } } }
Subway类
public class Subway { public static List<String> lines = new ArrayList<>();//存放所有的线路 public static List<String> route = new ArrayList<>(); private List<Station> outList = new ArrayList<Station>();//记录已经分析过的站点 public void calculate(Station s1, Station s2){//计算从s1站到s2站的最短经过路径 String line="初始线"; if(outList.size() == DataBuilder.totalStaion){ route.add("找到目标站点:"+s2.getName()+",共经过"+(s1.getAllPassedStations(s2).size()-1)+"站\n"); int flag=0; for(Station station : s1.getAllPassedStations(s2)){ if(station.getLinename()==null){//出发站 route.add(station.getName()+"->"); } else if(station.getName().equals(s2.getName())){//最后1站 if(!station.getLinename().equals(line)){ route.add("换乘 "+station.getLinename()+"->"+"到达 "+station.getName()); } else { route.add("到达 "+station.getName()); } } else if(!station.getLinename().equals(line)&&flag==1){//换乘后1站 line=station.getLinename(); route.add("换乘"+station.getLinename()+"->"+station.getName()+"->"); } else if(!station.getLinename().equals(line)&&flag==0){//第2站 line=station.getLinename(); route.add("乘坐"+station.getLinename()+"->"+station.getName()+"->"); flag=1; } else{//其余站 line=station.getLinename(); route.add(station.getName()+"->"); } } return; } if(!outList.contains(s1)) outList.add(s1); //如果起点站的OrderSetMap为空,则第一次用起点站的前后站点初始化之 if(s1.getOrderSetMap().isEmpty()){ List<Station> Linkedstations = getAllLinkedStations(s1); for(Station s : Linkedstations){ s1.getAllPassedStations(s).add(s); } } Station parent = getShortestPath(s1);//获取距离起点站s1最近的一个站(有多个的话,随意取一个) if(parent == s2){ System.out.println("找到目标站点:"+s2+",共经过"+(s1.getAllPassedStations(s2).size()-1)+"站"); for(Station station : s1.getAllPassedStations(s2)){ System.out.print(station.getName()+"->"); } return; } for(Station child : getAllLinkedStations(parent)){ if(outList.contains(child)){ continue; } int shortestPath = (s1.getAllPassedStations(parent).size()-1) + 1;//前面这个1表示计算路径需要去除自身站点,后面这个1表示增加了1站距离 if(s1.getAllPassedStations(child).contains(child)){ //如果s1已经计算过到此child的经过距离,那么比较出最小的距离 if((s1.getAllPassedStations(child).size()-1) > shortestPath){ //重置S1到周围各站的最小路径 s1.getAllPassedStations(child).clear(); s1.getAllPassedStations(child).addAll(s1.getAllPassedStations(parent)); s1.getAllPassedStations(child).add(child); } } else { //如果s1还没有计算过到此child的经过距离 s1.getAllPassedStations(child).addAll(s1.getAllPassedStations(parent)); s1.getAllPassedStations(child).add(child); } } outList.add(parent); calculate(s1,s2);//重复计算,往外面站点扩展 } private Station getShortestPath(Station station){//获取参数station到各个站的最短距离,相隔1站,距离为1,依次类推 int minPatn = Integer.MAX_VALUE; Station rets = null; for(Station s :station.getOrderSetMap().keySet()){ if(outList.contains(s)){ continue; } LinkedHashSet<Station> set = station.getAllPassedStations(s);//参数station到s所经过的所有站点的集合 if(set.size() < minPatn){ minPatn = set.size(); rets = s; } } return rets; } private List<Station> getAllLinkedStations(Station station){//获取参数station直接相连的所有站,包括交叉线上面的站 List<Station> linkedStaions = new ArrayList<Station>(); for(List<Station> line : DataBuilder.lineSet){ if(line.contains(station)){//如果某一条线包含了此站,注意由于重写了hashcode方法,只有name相同,即认为是同一个对象 Station s = line.get(line.indexOf(station)); if(s.prev != null){ linkedStaions.add(s.prev); } if(s.next != null){ linkedStaions.add(s.next); } } } return linkedStaions; } }
UI部分:
FrmQueryLine类
public class FrmQueryLine extends JDialog implements ActionListener { private JPanel toolBar = new JPanel(); private JPanel workPane = new JPanel(); private JButton btnOk = new JButton("查询"); private JLabel labelLineName = new JLabel("请输入线路名称:"); private JTextArea lineinformation=new JTextArea(); List<String> list=new Line().loadAllLineName(); String[] ingName = list.toArray(new String[list.size()]); private JComboBox cmbIngredient= new JComboBox(ingName); public FrmQueryLine(Frame f, String s, boolean b) { super(f, s, b); workPane.setLayout(null); labelLineName.setBounds(10, 9, 129, 19); workPane.add(labelLineName); cmbIngredient.setBounds(146, 7, 96, 23); workPane.add(cmbIngredient); btnOk.setBounds(268, 5, 75, 23); workPane.add(btnOk); lineinformation.setFont(new Font("Monospaced", Font.BOLD, 14)); lineinformation.setLineWrap(true);//激活自动换行功能 lineinformation.setWrapStyleWord(false);//激活断行不断字功能 lineinformation.setBounds(22, 54, 643, 167); workPane.add(lineinformation); this.getContentPane().add(workPane, BorderLayout.CENTER); this.setSize(700, 300); //屏幕居中显示 double width = Toolkit.getDefaultToolkit().getScreenSize().getWidth(); double height = Toolkit.getDefaultToolkit().getScreenSize().getHeight(); this.setLocation((int) (width - this.getWidth()) / 2, (int) (height - this.getHeight()) / 2); this.validate(); this.btnOk.addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource()==this.btnOk){ String ingredientName=this.cmbIngredient.getSelectedItem().toString(); try { List<Station> stations=DataBuilder.map.get(ingredientName); lineinformation.setText(""); for(int i=0;i<stations.size();i++) { Station station=stations.get(i); if(i==stations.size()-1) lineinformation.append(station.getName()); else lineinformation.append(station.getName()+"<->"); } lineinformation.paintImmediately(lineinformation.getBounds()); } catch (Exception e1) { JOptionPane.showMessageDialog(null, e1.getMessage(), "错误",JOptionPane.ERROR_MESSAGE); return; } } } }
FrmQueryRoute类
public class FrmQueryRoute extends JDialog implements ActionListener { private JPanel toolBar = new JPanel(); private JPanel workPane = new JPanel(); private JButton btnOk = new JButton("查询"); private JLabel labelS1 = new JLabel("起始站:"); private JLabel labelS2 = new JLabel("目标站:"); private JTextArea lineinformation=new JTextArea(); private JTextField edtS1 = new JTextField(20); private JTextField edtS2 = new JTextField(20); public FrmQueryRoute(Frame f, String s, boolean b) { super(f, s, b); workPane.setLayout(null); labelS1.setBounds(26, 9, 67, 15); workPane.add(labelS1); edtS1.setBounds(103, 6, 126, 21); workPane.add(edtS1); labelS2.setBounds(265, 9, 67, 15); workPane.add(labelS2); edtS2.setBounds(361, 6, 126, 21); workPane.add(edtS2); btnOk.setBounds(519, 5, 77, 23); workPane.add(btnOk); lineinformation.setFont(new Font("Monospaced", Font.BOLD, 14)); lineinformation.setLineWrap(true);//激活自动换行功能 lineinformation.setWrapStyleWord(false);//激活断行不断字功能 lineinformation.setBounds(36, 33, 908, 204); workPane.add(lineinformation); this.getContentPane().add(workPane, BorderLayout.CENTER); this.setSize(1000, 300); // 屏幕居中显示 double width = Toolkit.getDefaultToolkit().getScreenSize().getWidth(); double height = Toolkit.getDefaultToolkit().getScreenSize().getHeight(); this.setLocation((int) (width - this.getWidth()) / 2, (int) (height - this.getHeight()) / 2); this.validate(); this.btnOk.addActionListener(this); } @Override public void actionPerformed(ActionEvent e) { if(e.getSource()==this.btnOk){ String S1Name=this.edtS1.getText(); String S2Name=this.edtS2.getText(); try { int flag1=0,flag2=0; for(Station s:DataBuilder.mapOfStation) { if(S1Name.equals(s.getName())) { flag1=1; } if(S2Name.equals(s.getName())) { flag2=1; } } if(flag1==0) throw new Exception("起始站不存在"); if(flag2==0) throw new Exception("目标站不存在"); lineinformation.setText("");//清空JTextArea if(S1Name.equals(S2Name)) lineinformation.append("目的站与起始站相同"); else { Subway sw = new Subway(); Subway.route.clear();//再次运行清空路线 sw.calculate(new Station(S1Name), new Station(S2Name)); for(String str:Subway.route) lineinformation.append(str); } lineinformation.paintImmediately(lineinformation.getBounds()); } catch (Exception e1) { JOptionPane.showMessageDialog(null, e1.getMessage(), "错误",JOptionPane.ERROR_MESSAGE); return; } } } }
6.测试用例(将输入输出结果截图提交)




7.总结
部分抄袭代码:https://blog.csdn.net/wangchsh2008/article/details/46288967
感谢UI指导!
git:https://github.com/Non-Exited/31701056/tree/master/%E8%BD%AF%E4%BB%B6%E5%B7%A5%E7%A8%8B
浙公网安备 33010602011771号