2012年1月6日 星期五

[C#]動手寫第一支資料物件化程式!

首先這一篇文章,不是要討稐物件導向式資料庫(OODB),或是LINQ
而是協助建立一個將資料物件化的概念。

舉例來說,一間小學的資料庫有以下的資料表

TB_STUDENT (學生)
NO(學號)
NAME(姓名)
AGE(年齡)
0001
小明
12
0002
美美
10

TB_SUBJECT (科目)
SN(代號)
SUBJECT(科目)
CH
國文
EN
英文
MA
數學

TB_GRADE_LIST (成績列表)
SN
NO
GRADE
CH
0001
90
EN
0001
50
MA
0001
100
CH
0002
55
EN
0002
95
MA
0002
90

現在我們要取得每個學生及格科目的平均成績,可以下sql如下:(Oracle為例)
SELECT
b.NAME,
ROUND(a.TOTAL_GD/a.CNT,2) as AVG_GD
FROM
(
SELECT
NO,
COUNT(1) AS CNT,
SUM(GRADE) AS TOTAL_GD
FROM TB_GRADE_LIST
WHERE
GRADE>=60
GROUP BY NO
) a,
TB_STUDENT b
WHERE
a.NO=b.NO

那如果我們想用物件導向的方法來設計呢?
1          先思考你要建立的Class(類別)Property

這個例子,很明顯的應該是有兩個類別:學生 和 科目,
每個學生有各自的學號、姓名、上課的科目和分數。
科目則會有序號、名稱和分數。

2          Event (事件)
這個例子的Event是計算每個學生的平均分數。
所以我們在學生類別建立一個 delegate事件,至於詳細的分數算法,可在程式另外指定即可。

3          開始建立InterfaceClass()
3.1          IStudent

   
public delegate int Get_Avg_Score(int cnt, int score);

    interface IStudent
    {
        /* Property */
        //學號
        String NO { get; set; }
        //姓名
        String NAME { get; set; }
        //年紀
        int AGE { get; set; }
        //上課科目
        List<CSubject> LSubject  { get; set; }

        //計算手續費的事件
        event Get_Avg_Score GetAvgScore;
    }

重點在因為要紀錄學生的每個科目的成績,我們必須在IStudent裡面放一個List<CSubject>

3.2          ISubject
    interface ISubject
    {
        /* Property */
        //序號
        String SN { get; set; }
        //科目
        String SUBJECT { get; set; }
        //成績
       int SCORE { get; set; }
    }

    ISubject
就比較簡單,沒什麼特別的。

4          有了以上內容,接下來在程式中,我們要開始從資料庫撈資料出來,建立一個學生類別ListList<CStudent>,過程中當然我們同時把各科目的成績存到類別裡面。

//建立學生物件List
            List<CStudent> LStudent = new List<CStudent>();

            //開始設定每個學生物件的Property
            DataTable dt_student = Get_Student();
            foreach (DataRow dr_student in dt_student.Rows)
            {
                String sNO = dr_student["NO"].ToString();
                String sNAME = dr_student["NAME"].ToString();
                int iAGE = Int16.Parse(dr_student["AGE"].ToString());
                CStudent cStudent = new CStudent(sNO, sNAME, iAGE);
                cStudent.GetAvgScore += Cal_Avg_Score; //指定計算平均分數的方法

                //取得該學生所有學習的科目和成績,把這些資訊放到CSubject物件List
                List<CSubject> LSubject = new List<CSubject>();
                DataTable dt_subject = Get_Subject_Score(sNO);
                foreach (DataRow dr_subject in dt_subject.Rows)
                {
                    String sSN = dr_subject["SN"].ToString();
                    String sSUBJECT = dr_subject["SUBJECT"].ToString();
                    int iScore = Int16.Parse( dr_subject["GRADE"].ToString() );
                    CSubject cSubject = new CSubject(sSN, sSUBJECT, iScore);
                    LSubject.Add(cSubject);
                    cSubject = null;
                }

                //再把CSubject物件List 指定給該學生
                cStudent.LSubject = LSubject;

                //把學生物件加入LStudent
                LStudent.Add(cStudent);

                cStudent = null;
            }

   
! 沒錯,程式爆增許多… XD
    ps1. Get_Student()
我就不列程式了,主要是從資料庫把所有學生和基本資料拿出來。
    ps2. Get_Subject_Score(sNO)
我就不列程式了,主要是從資料庫把該學生學習科目和其分數拿出來。
    ps3.
指定計算平均分數的方法,如下:

    public static int Cal_Avg_Score(int cnt, int score)
    {
        int avg_score = score / cnt;
        return avg_score;
    }


5          計算及格科目的平均分數
//開始計算每個學生及格科目的平均成績
            foreach (CStudent cStudent in LStudent)
            {
                int Cnt = 0;
                int Score = 0;
                foreach(CSubject cSubject in cStudent.LSubject)
                {
                    if (cSubject.SCORE >= 60)
                    {
                        Cnt++;
                        Score += cSubject.SCORE;
                    }
                }

                //呼叫該學生物件下的Get_My_Avg_Score事件,回傳平均分數
                int iAvgScore = cStudent.Get_My_Avg_Score(Cnt, Score);

                Console.WriteLine(String.Format("{0}平均分數: {1}", cStudent.NAME, iAvgScore));
            }
6          原本一次的SQL可解決的事情,為什麼要搞這麼複雜呢???
可以想想,如果我們未來要做別的計算,例如:列出不及格的科目,所有學生的平均成績等。
是不是只要拿這些物件來作運算就可以了,而不是重新再下一個SQL到資料庫撈資料。
(
當然Performance不在本文的討論範圍 XD

那麼,就開始動手寫你的第一支資料物件化程式吧!

沒有留言:

張貼留言