軟體工程Test1專案總結
阿新 • • 發佈:2020-11-04
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>>();//所有線集合 publicstatic 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