Introduction to Computer Science

Download Report

Transcript Introduction to Computer Science

類別與物件 I
(Classes and Objects I)
鄭士康
國立台灣大學
電機工程學系/電信工程研究所/
資訊網路與多媒體研究所
1
結構化程式設計/逐步細分法
• 序列/分支/迴圈
• 虛擬碼
– Magic number 7 加減 2
– 逐層分解工作
• 函式
• 軟體危機
2
物件導向程式設計
(Object-Oriented Programming)
• 軟體IC/程式碼重覆使用( Code Reuse)
• 封裝(Encapsulation)
– 狀態、行為
– 模擬
• 繼承(Inheritance)
• 多型(Polymorphism)
3
程式 DiceSimulation (1/4)
using System;
namespace DiceSimulation
{
/*
* 模擬擲骰子示範類別的宣告與物件的使用
* 3/16/2008
*
* 測試規畫:
* 六個點數出現次數應該大約相同
*/
class Program
{
static void Main(string[] args)
{
4
程式 DiceSimulation (2/4)
int[] count = new int[6];
// 累計點數出現次數
for (int i = 0; i < 6; ++i)
{
count[i] = 0;
}
const int N = 12000; // 總擲骰次數
int faceValue;
Dice dice = new Dice();
// 擲骰N次
for (int i = 0; i < N; ++i)
{
dice.Toss();
faceValue = dice.FaceValue;
++count[faceValue-1];
}
5
程式 DiceSimulation (3/4)
// 印出結果
for (int i = 0; i < 6; ++i)
{
Console.WriteLine(" {0} appears {1} times ",
i + 1, count[i]);
}
}
}
6
程式 DiceSimulation (4/4)
public class Dice
{
int faceValue = 1;
Random rand = new Random();
public int FaceValue
{
get { return faceValue; }
set { faceValue = value; }
}
public void Toss()
{
faceValue = rand.Next() % 6 + 1;
}
}
}
7
函式呼叫流程
static void Main(string[] args)
{
public class Dice
{
. . .
dice.Toss();
public void Toss()
{
}
faceValue = rand.Next() % 6 + 1;
}
. . .
}
public class Random
{
. . .
public int Next()
{
. . .
}
. . .
}
8
類別(Class)觀念
類別 Dice
資料變數(欄位成員 field)
faceValue, rand
方法函式(函式成員 function,
屬性 attribute)
FaceValue
Toss()
9
堆疊(Stack)與堆積(Heap)
Heap
.
.
.
Stack
10
實值型別儲存方式
堆疊(Stack)
int x = 100;
x
100
11
參考型別儲存方式
堆疊(Stack)
堆積(Heap)
string x = “abc”;
x
參考
‘a’
‘b’
‘c’
12
物件(Object)觀念
dice1
dice1.faceValue
記憶體地址1
dice1.rand
函式Toss進入地址
dice2
dice2.fileName
記憶體地址2
dice2.rand
函式Toss進入地址
函式成員Toss進入點
13
物件的設定(Assignment)
dice1
dice1.faceValue
記憶體地址1
dice1.rand
函式Toss進入地址
dice2
記憶體地址2
dice2 = dice1;
dice2.fileName
dice2.rand
函式Toss進入地址
函式成員Toss進入點
14
UML 的類別符號
Dice
faceValue : int
rand: Random
FaceValue
Toss() : void
15
練習1
• 宣告並測試以下類別
Car
speed : int
driver : string
Speed
Driver
Accelerate()
PutOnBrakes()
16
程式 CalculatorTest (1/6)
using System;
namespace CalculatorTest
{
/* 測試類別Calculator以說明函式的使用
* 3/17/2007
*/
class Program
{
static void Main(string[] args)
{
int op = 0;
int operand1;
int operand2;
int result;
Calulator calculator = new Calulator();
17
程式 CalculatorTest (2/6)
do
{
Console.Write(
"指定運算: 0. 結束; 1. 加; 2. 減; 3. 乘; 4. 除: ");
op = int.Parse(Console.ReadLine());
if (op == 0) break;
if (op > 4) continue;
Console.Write("輸入第一個數字: ");
operand1 = int.Parse(Console.ReadLine());
Console.Write("輸入第二個數字: ");
operand2 = int.Parse(Console.ReadLine());
switch (op)
{
18
程式 CalculatorTest (3/6)
case 1:
result = calculator.Add(operand1,
operand2);
Console.WriteLine("{0} + {1} = {2} ",
operand1, operand2, result);
break;
case 2:
result = calculator.Subtract(operand1,
operand2);
Console.WriteLine("{0} - {1} = {2} ",
operand1, operand2, result);
break;
case 3:
result = calculator.Multiply(operand1,
operand2);
19
程式 CalculatorTest (4/6)
Console.WriteLine("{0} * {1} = {2} ",
operand1, operand2, result);
break;
case 4:
result = calculator.Divide(operand1,
operand2);
Console.WriteLine("{0} / {1} = {2} ",
operand1, operand2, result);
break;
default:
Console.WriteLine(
"Should not appear this message. Debug!!!");
break;
}
} while (true);
}
}
20
程式 CalculatorTest (5/6)
/*
* 計算器
* 3/17/2007
*/
public class Calulator
{
public int Add(int a, int b)
{
int result = a + b;
return result;
}
public int Subtract(int a, int b)
{
int result = a - b;
return result;
}
21
程式 CalculatorTest (6/6)
public int Multiply(int a, int b)
{
int result = a * b;
return result;
}
public int Divide(int a, int b)
{
int result = a / b;
return result;
}
}
}
22
函式傳回值
Program.Main()
result
calculator.Add()
result
return value
23
程式SwapIntegers (1/2)
using System;
namespace SwapIntegers
{
/* 交換兩個整數變數
* 以說明傳值呼叫與傳址呼叫
* skj 4/5/2008
*/
class Program
{
static void Main(string[] args)
{
int x = 3;
int y = 5;
Console.WriteLine("x = {0}, y = {1}", x, y);
Swap(ref x, ref y);
Console.WriteLine("x = {0}, y = {1}", x, y);
}
24
程式SwapIntegers (2/2)
static void Swap(ref int x, ref int y)
{
int temp = x;
x = y;
y = temp;
}
}
}
25
程式 PassByReferenceAndOut (1/4)
using System;
namespace PassByReferenceAndOut
{
/*
* 示範傳址參數與out參數的使用方法
* 3/31/2008
*/
class Program
{
static void Main(string[] args)
{
double length = 100.0;
Square s = new Square();
s.Side = length;
26
程式 PassByReferenceAndOut (2/4)
double area1 = 0.0;
double perimeter1 = 0.0;
s.GetAreaAndPerimeter(ref area1,
ref perimeter1);
Console.WriteLine(
"正方形邊長: {0}, 面積: {1}, 周長: {2}",
length, area1, perimeter1);
double area2;
double perimeter2;
s.GetAreaAndPerimeterUsingOut(out area2,
out perimeter2);
Console.WriteLine(
"正方形邊長: {0}, 面積: {1}, 周長: {2}",
length, area2, perimeter2);
Console.ReadLine();
}
}
27
程式 PassByReferenceAndOut (3/4)
/*
* 正方形
* 3/16/2008
*/
public class Square
{
double a;
public double Side
{
get { return a; }
set { a = value; }
}
28
程式 PassByReferenceAndOut (4/4)
public void GetAreaAndPerimeter(ref double area,
ref double perimeter)
{
area = Math.Pow(a, 2);
perimeter = 4.0 * a;
}
public void GetAreaAndPerimeterUsingOut(
out double area, out double perimeter)
{
area = Math.Pow(a, 2);
perimeter = 4.0 * a;
}
}
}
29
傳值, 傳址, out 參數
• 傳值參數傳遞( Pass by value )
• 傳址參數傳遞( Pass by reference )
• out 參數
– 初值設定問題
30
類別Time
Time
hour : int
min : int
sec : int
SetTime()
GetTime()
31
程式 UsingThis (1/4)
using System;
namespace UsingThis
{
/*
* 說明this的使用
* 3/17/2007
*/
class Program
{
static void Main(string[] args)
{
Time t = new Time();
t.SetTime(11, 30, 52);
32
程式 UsingThis (2/4)
int hour;
int min;
int sec;
t.GetTime(out hour, out min, out sec);
Console.WriteLine("現在時間{0} : {1} : {2}",
hour, min, sec);
Console.ReadLine();
}
}
/*
* 以時,分,秒表示時間
*/
33
程式 UsingThis (3/4)
public class Time
{
int hour;
int min;
int sec;
public void SetTime(int hour, int
{
bool error = ( hour < 0 || hour
min < 0 || min >
sec < 0 || sec >
if (!error)
{
this.hour = hour;
this.min = min;
this.sec = sec;
}
min, int sec)
> 24 ||
59 ||
59 );
34
程式 UsingThis (4/4)
else
{
Console.WriteLine(
"Time.SetTime>>參數值不合理");
}
}
public void GetTime(out int hour, out int min,
out int sec)
{
hour = this.hour;
min = this.min;
sec = this.sec;
}
}
35
物件自我參考 this
t
記憶體地址
hour
min
sec
this
SetTime()進入地址
函式成員SetTime進入點
36
極端化程式設計
(Extreme Programming)
1.
2.
3.
4.
5.
撰寫一個簡單的測試程式
編譯執行程式,看它失敗
增添恰能通過測試之程式碼並編譯偵錯
重整及消除冗餘程式碼並編譯偵錯
反覆進行上述步驟
37
練習(1/4)
• 寫主程式
– 宣告陣列 Card[] deck = new Card[2]
– 在deck中放入兩張撲克牌Card物件
– 呼叫函數Swap交換兩張牌位置
• 以傳值參數參照下一頁虛擬碼,寫一個函
式Swap交換兩張牌位置
38
練習(2/4)
• 函式Swap虛擬碼
Procedure Swap( card1, card2 )
1. temp = card1;
2. card1 = card2;
card1
3. card2 = temp;
temp
return
card2
39
練習(3/4)
• 宣告並實作以下類別
Card
suit : char
faceValue : int
SetCard()
GetCard()
40
練習(4/4)
• 根據完成之Card類別修正主程式
• 測試兩張牌能否交換
• 將Swap函式之參數改為傳址參數,再試一
次
• 利用偵錯器找出結果差異之原因
41
程式 OverloadingDemo (1/4)
using System;
namespace OverloadingDemo
{
/*
* 示範多載之應用
* 3/17/2007
*/
class Program
{
static void Main(string[] args)
{
Adder adder = new Adder();
int i = 0;
42
程式 OverloadingDemo (2/4)
do
{
Console.Write(
"指定運算數值型態: 0. 結束; 1. 整數; 2. 浮點數: ");
i = int.Parse(Console.ReadLine());
if (i == 0) break;
if (i > 2) continue;
switch (i)
{
case 1:
Console.Write("輸入第一個整數");
int a = int.Parse(Console.ReadLine());
Console.Write("輸入第二個整數");
int b = int.Parse(Console.ReadLine());
adder.AddAndDisplay(a, b);
break;
43
程式 OverloadingDemo (3/4)
case 2:
Console.Write("輸入第一個浮點數");
double ad =
double.Parse(Console.ReadLine());
Console.Write("輸入第二個浮點數");
double bd =
double.Parse(Console.ReadLine());
adder.AddAndDisplay(ad, bd);
break;
}
} while (true);
}
}
/* 模擬加法器
* 3/17/2007
*/
44
程式 OverloadingDemo (4/4)
public class Adder
{
public void AddAndDisplay(int a,
{
int result = a + b;
Console.WriteLine("{0} + {1} =
result);
}
public void AddAndDisplay(double
{
double result = a + b;
Console.WriteLine("{0} + {1} =
result);
}
}
int b)
{2}", a, b,
a, double b)
{2}", a, b,
}
45
物件導向思維
• 什麼是程式要完成的功能?
• 需要那些物件/類別才能完成程式功
能?
• 物件/類別需要那些行為才能合作完
成程式功能?
• 例: Mission Impossible, RPG
46
範例問題
•
假設有一N_ROW列、N_COL欄的整數表格,
寫一程式計算每列總和、每欄總和、及全部總
和。以如下2列3欄表格為例:
3
4
2
5
7
6
其每列總和為 9、18,每欄總和為8、11、8,
全部總和為27。利用一個二維陣列儲存表格資
料,兩個一維陣列分別儲存每列總和及每欄總
和。
47
演算法主要虛擬碼
1. 取得資料
2. 計算每列總和
3. 計算每欄總和
4. 計算全部總和
5. 輸出答案
48
物件識別
• 表格
– 計算全部總和
• 列
– 計算全列之和
• 欄
– 計算全欄之和
49
類別 Table
Table
content: int[, ]
nRow : int
nCol : int
Content
GetRowSum()
GetColSum()
GetTotalSum()
50
程式 ClassDemo.Program (1/5)
using System;
namespace ClassDemo
{
/*
* 假設有一N_ROW列、N_COL欄的整數表格,
* 計算每列總和、每欄總和、及全部總和
* 利用物件導向
*
* 主要虛擬碼:
* 1. 取得資料
* 2. 計算每列總和
* 3. 計算每欄總和
* 4. 計算全部總和
* 5. 輸出答案
*
51
程式 ClassDemo.Program (2/5)
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
測試: 2 X 3 表格
+---------+---------+---------+
|
3
|
4
|
2
|
+---------+---------+---------+
|
5
|
7
|
6
|
+---------+---------+---------+
每列總和
+---------+---------+
|
9
|
18
|
+---------+---------+
每欄總和
+---------+---------+---------+
|
8
|
11
|
8
|
+---------+---------+---------+
52
程式 ClassDemo.Program (3/5)
* 全部總和
* 27
*
*/
class Program
{
static void Main(string[] args)
{
const int N_ROW = 2;
const int N_COL = 3;
int[,] table = new int[N_ROW, N_COL];
table[0, 0] = 3;
table[0, 1] = 4;
table[0, 2] = 2;
table[1, 0] = 5;
53
程式 ClassDemo.Program (4/5)
table[1, 1] = 7;
table[1, 2] = 6;
Table t = new Table();
t.Content = table;
int[] rowSum = t.GetRowSum();
int[] colSum = t.GetColSum();
int totalSum = t.GetTotalSum();
for (int i = 0; i < N_ROW; ++i)
{
Console.WriteLine("Sum of row " + i +
" is " + rowSum[i]);
}
54
程式 ClassDemo.Program (5/5)
for (int j = 0; j < N_COL; ++j)
{
Console.WriteLine("Sum of column " + j +
" is " + colSum[j]);
}
Console.WriteLine("Total sum is " + totalSum);
Console.ReadLine();
}
}
}
55
程式 ClassDemo.Table (1/5)
using System;
namespace ClassDemo
{
/*
* 表格
* skj 3/31/2008
*/
public class Table
{
private int[,] content;
int nRow;
int nCol;
56
程式 ClassDemo.Table (2/5)
public int[,] Content
{
get { return content; }
set
{
content = value;
nRow = content.GetUpperBound(0) + 1;
nCol = content.GetUpperBound(1) + 1;
}
}
57
程式 ClassDemo.Table (3/5)
public int[] GetRowSum()
{
int[] rowSum = new int[nRow];
for (int i = 0; i < nRow; ++i)
{
rowSum[i] = 0;
for (int j = 0; j < nCol; ++j)
{
rowSum[i] += content[i, j];
}
}
return rowSum;
}
58
程式 ClassDemo.Table (4/5)
public int[] GetColSum()
{
int[] colSum = new int[nCol];
for (int j = 0; j < nCol; ++j)
{
colSum[j] = 0;
for (int i = 0; i < nRow; ++i)
{
colSum[j] += content[i, j];
}
}
return colSum;
}
59
程式 ClassDemo.Table (5/5)
public int GetTotalSum()
{
int totalSum = 0;
for (int i = 0; i < nRow; ++i)
{
for (int j = 0; j < nCol; ++j)
{
totalSum += content[i, j];
}
}
return totalSum;
}
}
}
60
物件/類別之識別
• 程式功能敘述中的名詞、代名詞、名詞
片語可能需要轉換為物件/類別(CRC卡)
• 程式功能敘述中的動詞、動詞片語可能
需要轉換為物件/類別的方法(CRC卡)
• 需要相同方法的物件可以歸納出一個類
別(CRC卡)
• 類別之間的關係可以畫UML類別圖表示
• 主程式及類別的方法可以用虛擬碼進一
步描述
61
物件 CRC 卡
Object Name: withdrawManager Collaborators
Responsibilities
Ask user for amount to withdraw
Verify amount with
AccountManager
accountManager
cashDispenser
Tell cash dispenser to release
cash
62
類別 CRC卡
Class Name: WithdrawManager
Collaborators
Responsibilities
Ask user for amount to withdraw
AccountManager
Verify amount with
AccountManager
CashDispenser
Tell CashDispenser to release
cash
63
UML 類別圖( Class Diagram )
* G. Booch, J. Rumbaugh, and I. Jacobson, The Unified Modeling Language
User Guide, Addison Wesley, 1999.
64
構成(Aggregation)之實作
class Company {
//……
Department[] department;
// created by Company objects
Office[] office;
// created by Company objects
//……
}
65
關連(Association)之實作
class Department {
//……
Person manager; // created outside Department
Person[] member; // created outside Department
//……
}
class Person {
//……
Department department; // created outside Person
//……
}
66
延伸閱讀:系統分析與設計




F. P. Brooks, Jr., The Mythical Man-Month –
Essays on Software Engineering, AddisonWesley, 1975
G. Booch, J. Rumbaugh, and I. Jacobson, The
Unified Modeling Language User Guide,
Addison Wesley, 1999.
I. Jacobson, G. Booch, and J. Rumbaugh, The
Unified Software Development Process,
Addison Wesley, 1999.
R. C. Lee and W. M. Tepfenhart, UML and C++,
A Practical Guide to Object-Oriented
nd
67
練習 (1/2)
• 模擬兩輛車的直線賽道競速比賽, 先擬好測
試規畫
track1
track2
68
練習 (2/2)
• 主程式虛擬碼
while(true)
{
擲骰子決定兩部車的加減速或維持速率;
計算各車前進距離(等於速率乘以時間);
更新兩部車在賽道上的位置;
if 有車衝過終點,
{
記下駕駛員;
break;
}
}
印出勝利者姓名;
69