首先這一篇文章,不是要討稐物件導向式資料庫(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
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
a.NO=b.NO
那如果我們想用物件導向的方法來設計呢?
1 先思考你要建立的Class(類別)和Property:
這個例子,很明顯的應該是有兩個類別:學生 和 科目,
每個學生有各自的學號、姓名、上課的科目和分數。
科目則會有序號、名稱和分數。
這個例子,很明顯的應該是有兩個類別:學生 和 科目,
每個學生有各自的學號、姓名、上課的科目和分數。
科目則會有序號、名稱和分數。
2 Event (事件)
這個例子的Event是計算每個學生的平均分數。
所以我們在學生類別建立一個 delegate事件,至於詳細的分數算法,可在程式另外指定即可。
這個例子的Event是計算每個學生的平均分數。
所以我們在學生類別建立一個 delegate事件,至於詳細的分數算法,可在程式另外指定即可。
3 開始建立Interface和Class(略)
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 就比較簡單,沒什麼特別的。
ISubject 就比較簡單,沒什麼特別的。
4 有了以上內容,接下來在程式中,我們要開始從資料庫撈資料出來,建立一個學生類別List:List<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)
對! 沒錯,程式爆增許多… 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
那麼,就開始動手寫你的第一支資料物件化程式吧!
可以想想,如果我們未來要做別的計算,例如:列出不及格的科目,所有學生的平均成績…等。
是不是只要拿這些物件來作運算就可以了,而不是重新再下一個SQL到資料庫撈資料。
(當然Performance不在本文的討論範圍 XD
那麼,就開始動手寫你的第一支資料物件化程式吧!
沒有留言:
張貼留言