1. 程式人生 > >結對項目——四則運算(GUI)

結對項目——四則運算(GUI)

Coding NPU log != 架構 stream 答案 思考 mage

目錄

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)