1. 程式人生 > 實用技巧 >結對程式設計專案分析與經驗總結

結對程式設計專案分析與經驗總結

結對專案實現


前言

本部落格對在個人程式設計的基礎上進行的結對程式設計進行總結,主要包括程式碼的複用和結對程式設計中學到的經驗教訓

結對程式設計分工:我負責UI介面(登陸介面,出題介面,做題介面,彙報介面,修改密碼,彈窗模組),隊友負責(驗證碼註冊模組,查重模組,儲存賬戶資訊模組,計算模組)


一、結對程式設計要求

1、所有功能通過圖形化介面操作,可以是桌面應用,可以是網站(程式語言和技術不限);
2、使用者註冊功能。使用者提供手機號碼,點選註冊將收到一個註冊碼,使用者可使用該註冊碼完成註冊;
3、使用者完成註冊後,介面提示設定密碼,使用者輸入兩次密碼匹配後設置密碼成功。密碼6-10位,必須含大小寫字母和數字。使用者在登入狀態下可修改密碼,輸入正確的原密碼,再輸入兩次相同的新密碼後修改密碼成功;
4、密碼設定成功後,跳轉到選擇介面,介面顯示小學、初中和高中三個選項,使用者點選其中之一後,提示使用者輸入需要生成的題目數量;
5、使用者輸入題目數量後,生成一張試卷(同一張卷子不能有相同題目,題目全部為選擇題),介面顯示第一題的題乾和四個選項,使用者選擇四個選項中的一個後提交,介面顯示第二題,…,直至最後一題;
6、最後一題提交後,介面顯示分數,分數根據答對的百分比計算;
7、使用者在分數介面可選擇退出或繼續做題;

二、程式碼複用

1.生成試卷

根據使用者選擇的題目型別和數量生成題目(示例):
給函式增加了返回值,函式返回一個字串陣列,每一個字串代表一個題目,傳入的引數分別是題目型別,題目儲存的路徑和題目數量

public static String[] GenerateTest(String type, String path, int number)

2.生成字尾表示式

程式碼如下(示例):

int num = (int) (0 + Math.random() * (4 - 0 + 1));   //獲取0-4個 二元運算子
            ArrayList<String> houzhui = new ArrayList<>();
            int operands = (int) (1 + Math.random() * (100 - 1 + 1)); //運算元的取值範圍是1-100
            houzhui.add(Integer.toString(operands));

隨機生成二元運算子和運算數,然後把他們拼接成字尾表示式。根據題目的型別,隨即插入根號等一元運算子


3.把字尾表示式翻譯成中綴表示式

通過棧來實現,從字尾表示式中取字串,根據字串的內容來決定進行入棧還是出棧的操作

//將字尾表示式翻譯成中綴表示式
            Stack<String> stack = new Stack<>();
            int time=0;
            for (String str:houzhui){
                if (str.equals("sin")||str.equals("cos")||str.equals("tan")||str.equals("√")||str.equals("²")){
                    String popB=stack.pop();
                    if (str.equals("√")){
                        stack.push("√"+"("+popB+")");
                    }
                    if (str.equals("²")){
                        stack.push("("+popB+")"+"²");
                    }
                    if (str.equals("sin")){
                        stack.push("sin"+"("+popB+")");
                    }
                    if (str.equals("cos")){
                        stack.push("cos"+"("+popB+")");
                    }
                    if (str.equals("tan")){
                        stack.push("tan"+"("+popB+")");
                    }
                }
                else if(str.equals("+")||str.equals("-")||str.equals("*")||str.equals("/")){
                    String popB=stack.pop();
                    String popA=stack.pop();
                    if (str.equals("+")){
                        if (time==0){
                            temp = temp + "(" + popA + "+" + popB + ")";
                            time++;
                        }
                        else{
                            temp = "(" + temp + "+" + popB + ")";
                        }
                        stack.push(popA+"+"+popB);
                    }
                    if (str.equals("-")){
                        if (time==0){
                            temp = temp + "(" + popA + "-" + popB + ")";
                            time++;
                        }
                        else{
                            temp = "(" + temp + "-" + popB + ")";
                        }
                        stack.push(popA+"-"+popB);
                    }
                    if (str.equals("*")){
                        if (time==0){
                            temp=temp+popA+"*"+popB;
                            time++;
                        }
                        else{
                            temp=temp+"*"+popB;
                        }
                        stack.push(popA+"*"+popB);
                    }
                    if(str.equals("/")){
                        if (time==0){
                            temp=temp+popA+"/"+popB;
                            time++;
                        }
                        else{
                            temp=temp+"/"+popB;
                        }
                        stack.push(popA+"/"+popB);
                    }
                }
                else{
                    stack.push(str);
                }
            }

4.計算字尾表示式

這個很簡單,只需要利用棧,然後遍歷一次後綴表示式即可。如果遇到運算元,則運算元進棧;如果遇到一元運算子,則取出棧頂元素,運算後重新入棧;如果遇到二元運算子,則從棧中彈出兩個元素,計算後重新入棧

public static String[] Calculate(ArrayList<String> houzhui) {
       //str是一個字尾表示式
        Stack<Double> stack = new Stack<Double>();
        Double result;

        for (String str:houzhui) {
            if(str.equals("√")||str.equals("²")||str.equals("sin")||str.equals("cos")||str.equals("tan")){
                double popB = stack.pop();
                if (str.equals("√")){
                    if (popB!=0)
                        stack.push(Math.sqrt(popB));
                    else
                        stack.push(1.0);
                }
                if (str.equals("²")){
                    stack.push(Math.pow(popB,2));
                }
                if (str.equals("sin")){
                    stack.push(Math.sin(popB));
                }
                if (str.equals("cos")){
                    stack.push(Math.cos(popB));
                }
                if (str.equals("tan")){
                    stack.push(Math.tan(popB));
                }
            }
            else if (str.equals("+")||str.equals("-")||str.equals("*")||str.equals("/")){
                double popB = stack.pop();
                double popA = stack.pop();
                if (str.equals("+")){
                    stack.push(popA+popB);
                }
                else if (str.equals("-")){
                    stack.push(popA-popB);
                }
                else if (str.equals("*")){
                    stack.push(popA*popB);
                }
                else {
                    stack.push(popA/popB);
                }
            }
            else{
                stack.push(Double.parseDouble(str));
            }
        }
        result = stack.pop();
        try {
            result = new BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
        }catch (Exception e){

        }
        System.out.println("結果是: "+result);
        if (result.isNaN()){
            result=1.0;
        }
        if (result.isInfinite()){
            result=2.0;
        }
        String[] finalResult = new String[5];
        finalResult[0]=Double.toString(result);
        for(int j=0;j<3;j++){
            int judge = (int) (0 + Math.random() * (400 - 0 + 1));
            double tempAnswer=0.0;
            try{
                tempAnswer = new BigDecimal(result+judge).setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
            }catch (Exception e){

            }
            if(judge<100){
                finalResult[j+1]=Double.toString(tempAnswer);
            }
            else if (judge<250){
                finalResult[j+1]="√"+Double.toString(tempAnswer);
            }
            else if (judge<300){
                finalResult[j+1]="sin"+Double.toString(tempAnswer);
            }
            else if (judge<350){
                finalResult[j+1]="cos"+Double.toString(tempAnswer);
            }
            else{
                finalResult[j+1]="tan"+Double.toString(tempAnswer);
            }
        }
        for(int j=0;j<3;j++){
            int tempA = (int) (0 + Math.random() * (3 - 0 + 1));
            int tempB = (int) (0 + Math.random() * (3 - 0 + 1));
            String temp = finalResult[tempA];
            finalResult[tempA] = finalResult[tempB];
            finalResult[tempB] = temp;
        }

        String StrResult = Double.toString(result);
        if(finalResult[0].equals(StrResult)){
            finalResult[4]="A";
        }
        else if (finalResult[1].equals(StrResult)){
            finalResult[4]="B";
        }
        else if (finalResult[2].equals(StrResult)){
            finalResult[4]="C";
        }
        else{
            finalResult[4]="D";
        }

        return finalResult;
    }

函式返回值為字串陣列,這是因為函式返回了包括正確答案在內的四個備選答案

5.儲存檔案

複用了個人專案儲存題目為txt檔案的程式碼,將使用者名稱作為路徑,這樣每一個賬戶都有一個檔案來儲存生成的題目

Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy--MM--dd--HH--mm--ss");
        String time = sdf.format(date);

將文字檔案的名字設定為當前時間

public static void WriteToFile(String FilePath, String[] temp) throws IOException {
        FileOutputStream fos = null;
        fos = new FileOutputStream(FilePath, true); //不覆蓋原內容
        for (int i = 0; i < temp.length; i++) {
            String msg = Integer.toString(i + 1) + " " + temp[i] + '\n' + '\n';
            fos.write(msg.getBytes());
        }
        fos.close();
    }

通過檔案的輸入輸出流,將生成的中綴表示式寫入txt文件

三、UI介面

1.初始化介面

包括手機號碼和驗證碼輸入框、獲取驗證碼按鈕和註冊按鈕等,給按鈕新增監聽器,點選按鈕後傳送驗證碼或啟動註冊程式,註冊成功後將賬戶資訊儲存在本地

public void Init(){
        //設定手機號碼標籤
        JPanel jp1=new JPanel();
        JLabel jl1 = new JLabel("手機號碼:");    //用標籤來表示文字
        Font font1 = new Font("宋體", Font.BOLD, 16);
        Dimension dm1=new Dimension(200,30);
        jl1.setFont(font1);
        jp1.add(jl1);
        //設定手機號碼輸入欄
        m_jtf1 = new JTextField();
        m_jtf1.setPreferredSize(dm1);
        jp1.add(m_jtf1);
        //獲取驗證碼 按鈕
        JButton jb1 = new JButton("獲取驗證碼");
        jb1.setFont(font1);
        jp1.add(jb1);

        //設定驗證碼標籤
        JPanel jp2=new JPanel();
        JLabel jL2=new JLabel("驗證碼:");
        jL2.setFont(font1);
        jp2.add(jL2);
        //設定驗證碼輸入欄
        m_jtf2 = new JTextField();
        m_jtf2.setPreferredSize(dm1);
        jp2.add(m_jtf2);

        //設定註冊按鈕
        JPanel jp3=new JPanel();
        JButton jb2=new JButton("註冊");
        jb2.setFont(font1);
        jp3.add(jb2);

        //已有賬戶直接登入
        JPanel jp4=new JPanel();
        JButton jb3=new JButton("已註冊,直接登入");
        jb3.setFont(font1);
        jp4.add(jb3);

        //為按鈕新增監聽器
        jb1.addActionListener(new CheckAction());
        jb2.addActionListener(new RegsAction());
        jb3.addActionListener(new ChangeAction());

        ///設定面板放置的位置
        jp1.setBounds(325,100,600,40);
        jp2.setBounds(325,150,600,40);
        jp3.setBounds(325,200,600,40);

        this.getContentPane().setBackground(Color.decode("#eeeeee"));

        //把面板加入窗體
        this.add(jp1);
        this.add(jp2);
        this.add(jp3);
        this.add(jp4);

        //設定窗體屬性可見
        this.setVisible(true);
        this.setDefaultCloseOperation(3); //視窗關閉時程式自動停止執行
    }

2.登入介面

使用者註冊後,跳轉到設定使用者名稱和密碼介面。介面包括使用者名稱和密碼輸入框以及確認按鈕。有一個按鈕,點選可以切換隱藏密碼和檢視密碼,此功能是通過一個JTextField和一個JPasswordField切換實現的

public void load(){
        Font font1 = new Font("宋體", Font.BOLD, 16);
        Dimension dm1=new Dimension(200,30);

        //使用者名稱面板
        JPanel jp1 = new JPanel();
        JLabel jl1 = new JLabel("使用者名稱:");
        jl1.setFont(font1);
        jp1.add(jl1);
        m_jtf3 =new JTextField();
        m_jtf3.setPreferredSize(dm1);
        jp1.add(m_jtf3);
        jp1.setBounds(325,100,600,40);

        //密碼面板
        JPanel jp2=new JPanel();
        JLabel jl2=new JLabel("密碼");
        jl2.setFont(font1);
        jp2.add(jl2);
        m_jpf1 = new JPasswordField();
        m_jpf1.setPreferredSize(dm1);
        jp2.add(m_jpf1);
        jp2.setBounds(325,150,600,40);

        m_temp =new JTextField();
        m_temp.setPreferredSize(dm1);
        m_temp.setVisible(false);
        jp2.add(m_temp);
        m_vis = new JButton("檢視密碼");
        jp2.add(m_vis);
        m_vis.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (m_vis.getText().equals("檢視密碼")){
                    m_temp.setText(String.valueOf(m_jpf1.getPassword()));
                    m_temp.setVisible(true);
                    m_jpf1.setVisible(false);
                    m_vis.setText("隱藏密碼");
                }
                else {
                    m_temp.setVisible(false);
                    m_jpf1.setVisible(true);
                    m_vis.setText("檢視密碼");
                }
            }
        });


        //再次確認密碼面板
        JPanel jp3=new JPanel();
        JLabel jl3=new JLabel("再次輸入密碼");
        jl3.setFont(font1);
        jp3.add(jl3);
        m_jpf2 = new JPasswordField();
        m_jpf2.setPreferredSize(dm1);
        jp3.add(m_jpf2);
        JButton jb1=new JButton("確認");
        jb1.setFont(font1);
        jp3.add(jb1);
        jp3.setBounds(325,200,600,40);

        //加入使用者名稱和密碼輸入規範的提示
        JPanel jp4=new JPanel();
        JPanel jp5 = new JPanel();
        JLabel jl_id = new JLabel("1.使用者名稱不能以數字開頭,長度為6-10");
        JLabel jl_password = new JLabel("2.密碼至少8個字元,至少1個大寫字母,1個小寫字母和1個數字,不能包含特殊字元");
        jl_id.setFont(font1);
        jl_password.setFont(font1);
        jp4.add(jl_id);
        jp5.add(jl_password);
        jp4.setBounds(325,400,300,40);
        jp5.setBounds(325,500,300,40);

        jb1.addActionListener(new OKAction());

        this.add(jp1);
        this.add(jp2);
        this.add(jp3);
        this.add(jp4);
        this.add(jp5);

        this.setVisible(true);
        System.out.println("請設定您的使用者名稱和密碼");
    }

3.選擇題目介面

包含一個單選框和一個數字輸入框,使用者輸入題目型別和數量。題目型別為在(小學、初中、高中)三個選項中單選,數量為10-30之間的數字

public void ChooseType(){
        Font font1 = new Font("宋體", Font.BOLD, 16);
        Dimension dm1=new Dimension(200,30);

        //題目型別面板
        JPanel jp1=new JPanel();
        JLabel jl1 = new JLabel("題目型別(小學、初中、高中)");
        jl1.setFont(font1);
        jp1.add(jl1);

        m_jr1_Type = new JRadioButton("小學");
        m_jr2_Type = new JRadioButton("初中");
        m_jr3_Type = new JRadioButton("高中");
        jp1.setBounds(325,100,600,40);
        jp1.add(m_jr1_Type);
        jp1.add(m_jr2_Type);
        jp1.add(m_jr3_Type);
        ButtonGroup group = new ButtonGroup();
        group.add(m_jr1_Type);
        group.add(m_jr2_Type);
        group.add(m_jr3_Type);

        //題目數量面板
        JPanel jp2=new JPanel();
        JLabel jl2 = new JLabel("題目數量");
        jl2.setFont(font1);
        jp2.add(jl2);
        m_jtf7 =new JTextField();
        m_jtf7.setPreferredSize(dm1);
        jp2.add(m_jtf7);
        JButton jb1 = new JButton("確定");
        jb1.setFont(font1);
        jp2.add(jb1);
        jp2.setBounds(325,150,600,40);

        JPanel jp3=new JPanel();
        JButton jb2=new JButton("修改密碼");
        jp3.add(jb2);

        jb1.addActionListener(new TypeAction());
        jb2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                LoginUI.this.setVisible(false);
                LoginUI.this.getContentPane().removeAll();
                LoginUI.this.repaint();
                ChangePwd();
            }
        });

        this.add(jp1);
        this.add(jp2);
        this.add(jp3);
        this.setVisible(true);
        System.out.println("請輸入生成的題目型別和題目數量");

    }

4.做題介面

從GenerateTest()函式的返回值得到試卷,然後再用Calculate()函式得到備選答案,把備選答案加入單選按鈕,放置在面板上。使用者可點選”上一題“”下一題“按鈕來切換當前的題目,做過的題目答案會儲存

public void show_test(String[] test){
        Font font1 = new Font("宋體", Font.BOLD, 50);
        Dimension dm1=new Dimension(200,30);
        int len=test.length;
        int i = g_index;
        String temp=test[i];
        //題幹
        JPanel jp1=new JPanel();
        JLabel jl1 = new JLabel("第"+Integer.toString(i+1)+"題: "+temp+" =");
        jl1.setFont(new Font("宋體",Font.BOLD,20));
        jl1.setBackground(Color.white);
        jp1.add(jl1);
        jp1.setBounds(325,200,600,40);

        //四個備選答案和一個按鈕
        //一個函式可以計算答案,並生成四個備選答案
        ArrayList<String> houzhui=Common.map.get(temp);
        result = Common.Calculate(houzhui);        //四個備選答案和正確答案都在這裡
        JPanel jp2 = new JPanel();
        JPanel jp6 = new JPanel();

        m_jrA = new JRadioButton("A "+". "+result[0]);
        m_jrB = new JRadioButton("B "+". "+result[1]);
        m_jrC = new JRadioButton("C "+". "+result[2]);
        m_jrD = new JRadioButton("D "+". "+result[3]);
        m_jrA.setBackground(Color.white);
        m_jrB.setBackground(Color.white);
        m_jrC.setBackground(Color.white);
        m_jrD.setBackground(Color.white);
        JButton jb6 = new JButton("下一題");
        jb6.setBackground(Color.blue);
        if(i==len-1){
            jb6.setText("提交");
        }
        JButton jb7 = new JButton("上一題");
        jb7.setBackground(Color.yellow);
        jp2.add(m_jrA);
        jp2.add(m_jrB);
        jp2.add(m_jrC);
        jp2.add(m_jrD);
        if (i!=0){
            jp6.add(jb7);
        }
        jp6.add(jb6);

        ButtonGroup group = new ButtonGroup();
        group.add(m_jrA);
        group.add(m_jrB);
        group.add(m_jrC);
        group.add(m_jrD);

        jp2.setBounds(325,250,600,40);
        jp6.setBounds(325,500,600,40);

        jb6.addActionListener(new NextAction());
        jb7.addActionListener(new FormerAction());

        String userTemp=userAnswer[g_index];
        if (userTemp.equals("#")){
            System.out.println("此題使用者還沒有做!");
        }
        else if (userTemp.equals(result[0])){
            m_jrA.setSelected(true);
        }
        else if (userTemp.equals(result[1])){
            m_jrB.setSelected(true);
        }
        else if (userTemp.equals(result[2])){
            m_jrC.setSelected(true);
        }
        else {
            m_jrD.setSelected(true);
        }


        this.add(jp1);
        this.add(jp2);
        this.add(jp6);


        this.setVisible(true);
    }

5.彙報介面

在使用者做完全部題目後,介面顯示分數,使用者可選擇繼續做題或者退出

public void show_score(){
        Font font1 = new Font("宋體", Font.BOLD, 50);
        Dimension dm1=new Dimension(200,30);
        int goal = g_score;
        int len = test.length;

        JPanel jp1 = new JPanel();
        JLabel jl1 = new JLabel("您的分數為");
        JLabel jl2 = new JLabel(goal+"/"+len+"!");
        jl2.setFont(font1);
        JButton jb1 =new JButton("繼續做題");
        jb1.setBackground(Color.yellow);
        JButton jb2 = new JButton("退出");
        jb2.setBackground(Color.blue);
        jp1.add(jl1);
        jp1.add(jl2);
        jp1.add(jb1);
        jp1.add(jb2);
        jp1.setBounds(325,150,600,40);

        jb1.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                LoginUI.this.setVisible(false);
                LoginUI.this.getContentPane().removeAll();
                LoginUI.this.repaint();
                g_index=0;
                ChooseType();
            }
        });

        jb2.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });

        try {
            DataBase.SaveToLoc();
        } catch (IOException e) {
            e.printStackTrace();
        }

        this.add(jp1);
        this.setVisible(true);
    }

6.使用者資訊

使用者資訊儲存在名為account的txt檔案裡,在程式開始,程式將account資訊存入一個ArrayList連結串列,在程式結束後,將ArrayList裡的內容寫回account檔案

public static void SaveToLoc()throws IOException {
        FileOutputStream fos = null;
        fos = new FileOutputStream("src/Frame/"   + "account.txt"); //不覆蓋原內容
        for (String s : al) {
            String msg = s + '\n';
            fos.write(msg.getBytes());
        }
        fos.close();
    }
public static void SaveToCur(){
        File file = new File("src/Frame/" + "account.txt");
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        try{
            FileReader fr = new FileReader("src/Frame/"  + "account.txt");
            BufferedReader bf = new BufferedReader(fr);
            String str;
            while ((str = bf.readLine()) != null) {
                al.add(str);
            }
            bf.close();
            fr.close();
        }
        catch (IOException e){
            e.printStackTrace();
        }
    }

總結

以上就是今天要講的內容,本文僅僅簡單介紹了複用的程式碼塊和UI介面的實現,在程式設計過程中總結了幾點經驗
  • 重新整理當前介面時,可以先設定窗體為不可見,然後去除所有的元件,重繪。
  • 為按鈕新增監聽器時,可以寫一個內部類,這樣子內部類可以訪問外部類的成員,從而可以很方便的獲取文字框或密碼框的內容