結對項目——四則運算(GUI)
目錄
1.倉庫地址
2.開始前PSP展示
3.接口的設計
4.計算模塊接口的設計與實現過程
5.計算模塊接口部分的性能改進
6.計算模塊部分單元測試展示
7.計算模塊部分異常處理說明
8.界面模塊的詳細設計過程
9.界面模塊與計算模塊的對接
10.結對過程
11.結對編程優缺點
12.實際的PSP
1. 倉庫地址: https://git.coding.net/jiapwy/newfouroperation.git
隊友:胡雅馨
隊友的博客地址:http://www.cnblogs.com/huyaxin/p/8776113.html
這次作業對我而言難度很大,所以十分感謝我的隊友在這次結對編程中對我的幫助。通過這次作業,我也深深地意識到了自己的不足,希望接下來能學到更多知識和技能。
2.開始前PSP展示
PSP |
任務內容 |
計劃共完成需要的時間(min) |
Planning |
計劃 |
30 |
Estimate |
估計這個任務需要多少時間,並規劃大致工作步驟 |
30 |
Development |
開發 |
1100 |
Analysis |
需求分析 (包括學習新技術) |
60*5 |
Design Spec |
生成設計文檔 |
30 |
Design Review |
設計復審 (和同事審核設計文檔) |
10 |
Coding Standard |
代碼規範 (為目前的開發制定合適的規範) |
5 |
Design |
具體設計 |
60 |
Coding |
具體編碼 |
600 |
Code Review |
代碼復審 |
20 |
Test |
測試(自我測試,修改代碼,提交修改) |
30 |
Reporting |
報告 |
40 |
Test Report |
測試報告 |
30 |
Size Measurement |
計算工作量 |
10 |
Postmortem& ProcessImprovement Plan |
事後總結, 並提出過程改進計劃 |
60*5 |
3.接口的設計
基本概念
Information Hiding:信息隱藏指在設計和確定模塊時,使得一個模塊內包含的特定信息(過程或數據),對於不需要這些信息的其他模塊來說,是不可訪問的。
Interface Design:面向接口編程是軟件工程領域常用的設計手段
Loose Coupling:基於面向接口編程,松耦合度是接口設計的目的,接口設計是松耦合度的實現手段
對於面向對象的程序設計而言,信息隱藏是一種重要的軟件開發手段,它與對象的封裝與模塊化密切相關。在我看來,信息隱藏使得一個類把復雜的、敏感的、一旦被外界捕獲可能會引起不良後果的內容封裝在自身內部,這個類以外的代碼得不到此類信息(通過反射等手段可能對得到),以提高程序的安全性與健壯性。
關於interface design與loose coupling
我個人感覺,這兩個概念是相輔相成的,後者是前者的目的,前者是後者的實現手段。
面向接口編程是軟件工程領域常用的設計手段,在這次結對作業中,我們深切體會到它的一大優點:我們只需要面向接口進行編程,代碼就完美的融入了整個程序中。雖然我們剛開始並不清楚整個測試程序的工作原理與架構,但我們專註於實現它的功能,就能夠達到調度的目的。
這對於一個團隊而言,是非常重要的,在做一個團隊項目時,有人可能負責領域模型,有人負責前臺,有人負責業務邏輯,在這種MVC的設計模式驅動下,我們首先想到的就是:定義一套公共的接口,方便各個模塊之間的通訊。面向接口的程序設計思想是一種有效的手段。開發可以並行進行,這樣,不需要等待一個模塊的完成就可以預先“使用”這個模塊,極大的提高了團隊的效率
5個經典的設計原則檢查(SOLID)。其中的SRP(Single Responsibility Principle)要求每個模塊都只有一個明確的職責。因為模塊職責多,就意味著邏輯難以封閉,容易受到外部因素變化而變化,導致該模塊不穩定。在本項目中,我們把涉及到計算數據的生成和求解功能提出來,形成一個獨立的模塊。其他的控制輸入、數據可視化等功能也形成各自的模塊,再通過接口把它們聯系起來,這樣各模塊間就做到了松耦合(即修改一個模塊時不需要更改其他的模塊),同時也實現了不同模塊間的信息隱藏(即每個模塊只訪問自己感興趣的數據來實現自己負責的功能)。
??
4.計算模塊接口的設計與實現過程
我們小組的計算模塊接口由兩個類組成
Main類:模塊的主類,負責接收命令行的參數並啟動程序
test類:生成符合命令行參數要求的題目
test類裏有
num方法:負責隨機產生定制的算式
result方法:輸出文件
calculator方法:負責篩選運算過程中符合要求的式子,並計算答案
nibolan方法:中綴表達式轉後追表達式
5.計算模塊接口部分的性能改進
見下圖
6.計算模塊部分單元測試展示
測試輸入是有錯誤的
測試分母是0
代碼覆蓋率:
public void testMain() { String[] args = {"-n","10","-m","1","100","-o","5","-c","-b"}; String[] args1 = {"-o"}; String[] args2 = {"-n","-m","1","100"}; String[] args3 = {"-n","100000","-m","100"}; String[] args4 = {"-m","1000","44","-n"}; String[] args5 = {"-o","100"}; String[] args6 = {"-m","-3","1"}; new Command(); Command.main(args); Command.main(args1); Command.main(args2); Command.main(args3); Command.main(args4); Command.main(args5); Command.main(args6); }
7.計算模塊部分異常處理說明
我們共設計了四種異常處理,分別是出題數量異常處理,數值左邊界異常處理,數值右邊界異常處理和運算符數量異常處理。
(1)出題數量異常處理
當用戶輸入的出題數量的數值不在範圍內時,提醒用戶當前輸入的數值不合法,要求用戶重新輸入。
try { n1 = Integer.parseInt(n.getText()); if (n1 <= 0 || n1 > 10000) { n.setText("請輸入合法參數"); return; } flag0 = 1; } catch (Exception a) { n.setText("格式不正確,請重新填寫!"); }
(2)數值左邊界異常處理
當用戶輸入的出題數值左邊界不在範圍內時,提醒用戶當前輸入的數值不合法,要求用戶重新輸入。
try { m11 = Integer.parseInt(m1.getText()); if (m11 <= 0 || m11 > 100) { m1.setText("請輸入合法參數"); return; } flag1 = 1; } catch (Exception a) { m1.setText("格式不合法,請重新填寫!"); }
(3)數值右邊界異常處理
當用戶輸入的出題數值右邊界不在範圍內時,提醒用戶當前輸入的數值不合法,要求用戶重新輸入。
try {
m22 = Integer.parseInt(m2.getText());
if (m22 < 50 || m22 > 1000) {
m2.setText("請輸入合法參數");
return;
}
flag2 = 1;
} catch (Exception a) {
m2.setText("格式不合法,請重新填寫!");
}
(4)運算符數量異常處理
當用戶輸入的運算符數量不在範圍內時,提醒用戶當前輸入的數值不合法,要求用戶重新輸入。
try { o1 = Integer.parseInt(o.getText()); if (o1 <= 0 || o1 > 10) { o.setText("請輸入合法參數"); return; } flag3 = 1; } catch (Exception a) { o.setText("格式不合法,請重新填寫!"); }
8.界面模塊的詳細設計過程
(1)歡迎界面
在開始時,我們對GUI的理解十分少,對基本的語句功能實現都非常困難,例如GUI的Button事件都不能實現,設計也十分不美觀,下面是最開始的失敗界面。
在嘗試了很多之後,通過不斷的學習,我們改進了界面,終於做的可以看了,並且實現了一部分功能,下面附上代碼和圖片。
public class Main { public static JTextField jtf =new JTextField(5); //文本框 public Main() { JMenuBar jmb=new JMenuBar(); //菜單欄 JFrame jf=new JFrame(); //窗口 final JLabel jlb =new JLabel(); //標簽 final JButton jb =new JButton(); //按鈕 JPanel jp =new JPanel(); //容器 JPanel jp1 =new JPanel(); jlb.setText("出題數量"); jtf.getText(); jb.setText("生成題目 "); jf.setLayout(new BorderLayout()); //布局 jf.setSize(500, 400); jf.setTitle("歡迎使用四則運算界面"); JMenu jm=new JMenu("語言選擇"); //菜單 JMenuItem mi1=new JMenuItem("中文"); //菜單項 JMenuItem mi2=new JMenuItem(""); JMenuItem mi3=new JMenuItem("英文"); //實現選擇語言功能 jm.add(mi1); jm.add(mi2); jm.add(mi3); jmb.add(jm); jp.add(jlb); jp.add(jtf); jp1.add(jb); ImageIcon img = new ImageIcon("src/test2/2PAA~5DRU_W_ZJG]3$R(9LN銆俻ng"); jf.add(jp,BorderLayout.NORTH); jf.add(jp1,BorderLayout.CENTER); jf.setJMenuBar(jmb); jf.setVisible(true); mi1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jlb.setText("出題數量"); jtf.getText(); jb.setText("生成題目"); } }); mi2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jlb.setText("出題數量"); jtf.getText(); jb.setText("go"); } }); mi3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jlb.setText("please input the number of questions you want"); jtf.getText(); jb.setText("generate tests"); //完成英文版 } }); jb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { methodjb(); } }); } //完成最初界面 public static void methodjb(){ new MainFrame(Integer.parseInt(jtf.getText())); } public static void main(String args[]) { new Main(); //主函數調用 } }
答題頁面
當填寫完出題數量後,再點擊生成題目時,會出現另一個界面,頁面包括所有生成的題目,以及結果的輸入處,包括計時功能的完成,而語言選擇項仍然存在,最後是生成題目按鈕,當你點擊按鈕,運算的結果,時間就會傳遞給後臺,而接下來的頁面會進行相應判斷的顯示。下面附上對應代碼。
最終頁面
當你在題目頁面點擊了提交按鈕,你的題目和答案會一起提交到後臺,後臺的計時器會停止計時,並且後臺會對你的題目進行分析判斷,最後提交給你一份答案頁面,以及你答題所用的時間信息,還有你錯誤的題目的正確答案。下面附上圖片和代碼。
jb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { for(int i=0;i<n;i++) { strarr[3*i+1]=jt[i].getText(); } timer.stop(); JMenuBar jmb=new JMenuBar(); //菜單欄 JFrame jf=new JFrame(); //窗口 JPanel jp =new JPanel(); jp.setLayout(new GridLayout(n+1,2)); jf.setSize(400, 400); jf.setTitle("四則運算"); JMenu jm=new JMenu("Language"); //菜單 JMenuItem mi1=new JMenuItem("中文簡體"); //菜單項 JMenuItem mi2=new JMenuItem("中文繁體"); JMenuItem mi3=new JMenuItem("英文"); jm.add(mi1); jm.add(mi2); jm.add(mi3); jmb.add(jm); jf.setJMenuBar(jmb); jf.setVisible(true); final JButton jbt=new JButton("記錄"); jbt.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { File file = new File("F:/result.txt");//形成文件 String line =null; try { BufferedReader br = new BufferedReader(new FileReader(file)); try { while((line = br.readLine()) != null){ String[] num = line.split("#"); zheng1=Integer.parseInt(num[1]); cuo1=Integer.parseInt(num[3]); }} catch (IOException ex) { Logger.getLogger(MainFrame.class.getName()).log(Level.SEVERE, null, ex); } } catch (FileNotFoundException ex) { keep(); } keep(); } private void keep() { FileOutputStream fop = null; File file; String content = "正確#"+(n-MainFrame.cuowu+zheng1)+"#錯誤#"+(MainFrame.cuowu+cuo1)+"#"; try { file = new File("D:/result.txt"); fop = new FileOutputStream(file); if (!file.exists()) { file.createNewFile(); } byte[] contentInBytes = content.getBytes(); fop.write(contentInBytes); fop.flush(); fop.close(); } catch (IOException e) { e.printStackTrace(); } finally { try { if (fop != null) { fop.close(); } } catch (IOException e) { e.printStackTrace(); } } }//將正確錯誤題目數量都輸入到文件中 });
for(int i=0;i<n;i++) { JLabel jb=new JLabel(); jb2[i]=new JLabel(); jb.setText(strarr[3*i]+strarr[3*i+1]); if(strarr[3*i+1].equals(strarr[3*i+2])) { jb2[i].setText("正確"); mi1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { for(int i=0;i<n;i++) { if(jb2[i].getText().equals("正確")||jb2[i].getText().equals("正確")||jb2[i].getText().equals("right")) jb2[i].setText("正確"); else jb2[i].setText("錯誤,答案是:"+strarr[3*i+2]); } } }); mi2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { for(int i=0;i<n;i++) { if(jb2[i].getText().equals("正確")||jb2[i].getText().equals("正確")||jb2[i].getText().equals("right")) jb2[i].setText("正確"); else jb2[i].setText("錯誤,答案是"+strarr[3*i+2]); } } }); mi3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { for(int i=0;i<n;i++) { if(jb2[i].getText().equals("正確")||jb2[i].getText().equals("正確")||jb2[i].getText().equals("right")) jb2[i].setText("right"); else jb2[i].setText("wrong,the answer is :"+strarr[3*i+2]); } } });//計算正確答案 }
else { jb2[i].setText("錯誤,答案是:"+strarr[3*i+2]); cuowu+=1; } jp.add(jb); jp.add(jb2[i]); }//出現錯誤答案 JPanel jp1=new JPanel(); mi1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jbt.setText("記錄"); } }); mi2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jbt.setText("記錄"); } }); mi3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jbt.setText("record"); } });//產生記錄 jp1.add(jbt); jp.add(jp1); final JLabel lbl1 = new JLabel(); lbl1.setText("用時 :"+lbl.getText()); mi1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { lbl1.setText("用時:"+lbl.getText()); } }); mi2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { lbl1.setText("用時:"+lbl.getText()); } }); mi3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { lbl1.setText("time :"+lbl.getText()); } }); jp.add(lbl1); jf.add(jp); } });//累計用時的方法 jb.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { } }); } public static void main(String args[]) { new Main(); } } //調用函數
整體實現過程
public class MainFrame { Date now = new Date(); JTextField[] jt=new JTextField[100]; String[] strarr=new String[100]; JLabel[] jb2 =new JLabel[100]; public static int cuowu=0; int zheng1=0,cuo1=0; public MainFrame(final int n) { JMenuBar jmb=new JMenuBar(); //菜單欄 JFrame jf=new JFrame(); //窗口 JPanel jp =new JPanel(); jp.setLayout(new GridLayout(n+1,3)); jf.setSize(400, 400); jf.setTitle("四則運算"); JMenu jm=new JMenu("Language"); //菜單 JMenuItem mi1=new JMenuItem("中文簡體"); //菜單項 JMenuItem mi2=new JMenuItem("中文繁體"); JMenuItem mi3=new JMenuItem("英文"); jm.add(mi1); jm.add(mi2); jm.add(mi3); jmb.add(jm); jf.setJMenuBar(jmb); jf.setVisible(true); for(int i=0;i<n;i++) { jt[i]=new JTextField(); jt[i].getText(); Random rand = new Random(); JLabel jbi=new JLabel(); int ra=rand.nextInt(2)+1; zhengshu zsi=new zhengshu(); fenshu fsi=new fenshu(); fsi.main(null); zsi.main(null); if(ra==1) { jbi.setText(zsi.suanshi); strarr[3*i]=zsi.suanshi; strarr[3*i+1]=jt[i].getText(); strarr[3*i+2]=zsi.z; } else { jbi.setText(fsi.suanshi); strarr[3*i]=fsi.suanshi; strarr[3*i+1]=jt[i].getText(); strarr[3*i+2]=fsi.z; } JLabel jb=new JLabel(); jb.setText(" "); jp.add(jbi); jp.add(jt[i]); jp.add(jb); } final JLabel lbl = new JLabel(); now.setHours(0); now.setMinutes(0); now.setSeconds(0); final Timer timer = new Timer(1000, new ActionListener() { public void actionPerformed(ActionEvent e) { Date now2 = new Date(now.getTime() + 1000); now = now2; SimpleDateFormat formatter = new SimpleDateFormat("HH:mm:ss"); lbl.setText(formatter.format(now)); } }); timer.start(); final JButton jb=new JButton("提交"); JPanel jp1=new JPanel(); mi1.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jb.setText("提交"); } }); mi2.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jb.setText("提交"); } }); mi3.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { jb.setText("commit"); } }); jp1.add(jb); jp.add(jp1); jp.add(lbl); jf.add(jp);
9.界面模塊與計算模塊的對接
我們在這次結對項目中,通過設計了GUI類,即使用了圖形用戶界面來進行UI模塊的設計,至於這些各種不同UI模塊之間的對接,則是通過事件監聽器接口來實現的。
參數傳遞功能
String n = JOptionPane.showInputDialog( frame, "輸入題目的數量(1-1000):" ); String m = JOptionPane.showInputDialog( frame, "輸入數值範圍(1-10000):" ); String z = JOptionPane.showInputDialog( frame, "輸入符號數量(1-10):" );
下面附上具體實現功能截圖:
10.結對過程
因為工作量較大,我和我的結對小夥伴一早就定下了分工,雖然中間經歷了一個小假期,但是我們也不時都在為這個作業而思考,在作業過程中我們遇到了很多困難,有時候一籌莫展,但好在我們一直在努力尋找解決方法,而且溝通良好,兩人之間不存在任何嫌隙,最終也都如願以償得到了讓我們比較滿意的結果。下面附上我們的結對照片。
11.結對編程優缺點
優點:在結對編程中,深刻地體會到了1+1>2的含義,與個人單獨編碼相比,結對更加的高效,而代碼變得更加有質量。當你在敲代碼時,有另外一個人在不斷提醒監督著你,這讓自己克制了一些單獨編碼時不免會犯的錯誤,比如惰性和一些基礎性的錯誤,同樣,兩個人的共進退讓我們充滿鬥誌,突破了困難局限,最終完成一項讓我們自己滿意的項目。
缺點:對於結對編程來說,溝通磨合是非常重要的,一旦沒有有效的溝通,那結對編程便很難進行下去,而溝通一直是人與人之間的難題,我想這也算是結對編程所不可避免的缺點吧。另外,每個人編程都有自己的習慣和風格,所以當兩個人在一起工作時,難免會產生矛盾就和摩擦,這也是不太容易解決的問題。
各人優缺點:
|
周紫伊 |
胡雅馨 |
優點 |
認真細心,有耐心
|
積極,上進,善於學習,平易近人
|
缺點 |
基礎知識薄弱,不善於溝通交流 |
缺點:基礎知識不紮實,偶爾懶散 |
12.實際的PSP
PSP
PSP |
任務內容 |
實際完成需要的時間(min) |
Planning |
計劃 |
20 |
Estimate |
估計這個任務需要多少時間,並規劃大致工作步驟 |
50 |
Development |
開發 |
2900 |
Analysis |
需求分析 (包括學習新技術) |
40 |
Design Spec |
生成設計文檔 |
30 |
Design Review |
設計復審 (和同事審核設計文檔) |
2 |
Coding Standard |
代碼規範 (為目前的開發制定合適的規範) |
20 |
Design |
具體設計 |
60 |
Coding |
具體編碼 |
1200 |
Code Review |
代碼復審 |
20 |
Test |
測試(自我測試,修改代碼,提交修改) |
1200 |
Reporting |
報告 |
180 |
Test Report |
測試報告 |
120 |
Size Measurement |
計算工作量 |
25 |
Postmortem& ProcessImprovement Plan |
事後總結, 並提出過程改進計劃 |
60*5 |
結對項目——四則運算(GUI)