Download presentation
Presentation is loading. Please wait.
1
鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所
物件導向程式設計 鄭士康 國立台灣大學 電機工程學系/電信工程研究所/ 資訊網路與多媒體研究所 1
2
綱要 物件導向思維 CRC卡會議 UML類別圖 SRP:單一責任原理 物件導向二十一點模擬程式0.0.0版
物件導向二十一點模擬程式0.0.1版 物件導向二十一點模擬程式0.0.2版
3
綱要 物件導向二十一點模擬程式0.0.3版 物件導向二十一點模擬程式0.0.4版
4
綱要 物件導向思維 CRC卡會議 UML類別圖 SRP:單一責任原理 物件導向二十一點模擬程式0.0.0版
物件導向二十一點模擬程式0.0.1版 物件導向二十一點模擬程式0.0.2版
5
物件導向程式設計 利用逐步細分法的函式導向程式設計曾經風行一時
物件和類別的觀念及程式語言普及之後,有必要採用不同的方式思考,以便更直截了當的寫出物件和類別的程式,並且充份發揮物件導向封裝、繼承、多型的特點
6
程式規劃 版本規劃 (80-20定律、要事第一、工程方法) 測試規劃 良好程式設計習慣 測試驅動方法開發
使用研發日誌,與夥伴合作,漸增式與回合式發展程式,維持可持續的發展步調,維護原始碼、註解、重要程式文件 測試驅動方法開發
7
物件導向程式設計之引入 版本規劃及測試規劃完成,要開始以漸增及回合方式,進行各個版本的進一步設計之時
8
物件導向思維 什麼是程式要完成的功能? 需要那些物件/類別才能完成程式功能? 物件/類別需要那些行為才能合作完成程式功能?
例: Mission Impossible, RPG 8
9
綱要 物件導向思維 CRC卡會議 UML類別圖 SRP:單一責任原理 物件導向二十一點模擬程式0.0.0版
物件導向二十一點模擬程式0.0.1版 物件導向二十一點模擬程式0.0.2版
10
CRC卡 Class (類別)、Responsibilities (負責功能)、Collaborators (合作類別)
由Kent Beck與Ward Cunningham發明,用以教導其同儕程式設計師物件導向的觀念 利用CRC卡會議可使初學者更容易了解物件與類別的意義
11
參考書籍 N. M. Wilkinson, Using CRC Cards: An Informal Approach to Object-Oriented Development, New York: SIGS Books, 1995. 11
12
練習 實際演練二十一點遊戲模擬原型的CRC卡會議 12
13
CRC卡會議 定義問題 產生類別 演練場景 整理卡片 13
14
產生類別 腦力激盪 過濾類別 初步建立繼承關係 描述類別 寫出類別名稱及定義 初步寫出負責功能(Responsibilities)
初步寫出資料成員(Attributes) 14
15
產生類別 腦力激盪 過濾物件/類別 建立繼承關係 描述類別
16
物件/類別之識別 流程敘述中的名詞、代名詞、名詞片語可能需要轉換為物件/類別(CRC卡)
類別之間的關係可以畫UML類別圖表示 主程式及類別的方法可以用虛擬碼進一步描述 16
17
資料成員與 類別之區分 資料成員通常表示類別的狀態變數,而非單獨之類別或型別
撲克牌之花色 借書資料之到期日期 但資料成員如可操作,且可用藍圖描述,可能可以構成一個類別,而資料成員變成一個物件 日期 17
18
必也正名乎 物件、類別、狀態、功能之名稱力求精準 使用一致命名規範 物件、類別、狀態:名詞片語 功能:動詞片語 條件判斷:形容詞片語、判斷句
多字連用,除第一字另外考慮外,各字開頭字母大寫 物件、狀態:第一個字小寫起頭 humanPlayer 類別、功能:第一個字大寫起頭 HumanPlayer 18
19
演練場景 列出場景清單 逐一演練場景 寫下類別負責功能、合作類別、資料成員 先演練簡單場景 複雜場景可能包括其他簡單場景 例外場景押後演練
19
20
整理卡片 刪除沒用到的類別、方法成員、資料成員 發現繼承結構 繪出類別圖(Class Diagram) 20
21
CRC卡正面 類別名稱 父類別 負責功能 合作類別 21
22
CRC卡背面 描述 成員變數 22
23
類別Card之CRC卡正面 類別名稱: Card 合作類別 負責功能 知道花色 知道點數 Deck Hand 23
24
類別Card之CRC卡背面 描述:代表用到的52張撲克牌 成員變數 花色(suit) 點數(rank) 24
25
類別Deck之CRC卡正面 類別名稱: Deck 合作類別 負責功能 知道剩下的牌 發一張牌 Game Card 25
26
類別Deck之CRC卡背面 描述:代表牌疊 成員變數 牌(cards) 26
27
類別 HumanPlayer之CRC卡正面
合作類別 負責功能 存入一張牌 知道玩家狀態 決定是否再要一張牌 傳回手牌總點數 Game Hand 27
28
類別HumanPlayer之CRC卡背面
描述:代表人類玩家 成員變數 手牌(hand) 玩家狀態(status) 28
29
類別Hand之 CRC卡正面 類別名稱: Hand 合作類別 負責功能 Card 知道手中的牌 知道手牌總點數 HumanPlayer
ComputerPlayer 29
30
類別Hand之 CRC卡背面 描述:代表人類玩家或電腦玩家手中的牌 . 成員變數 牌(cards) 30
31
類別ComputerPlayer之 CRC卡正面
合作類別 負責功能 存入一張牌 知道玩家狀態 決定是否再要一張牌 傳回手牌總點數 Game Hand 31
32
類別 CRC卡背面 描述:代表電腦玩家. 成員變數 手牌(hand) 玩家狀態(status) 32
33
類別Game之CRC卡正面 類別名稱: Game 合作類別 負責功能 Deck 控制遊戲流程 比較電腦與人類玩家點數 HumanPlayer
大小 Deck HumanPlayer ComputerPlayer 33
34
類別Game之CRC卡背面 描述:代表整個二十一點遊戲. 成員變數 牌疊(deck) 人類玩家(humanPlayer)
電腦玩家(computerPlayer) 34
35
類別關係圖 HumanPlayer Game Deck Hand ComputerPlayer Card
36
綱要 物件導向思維 CRC卡會議 UML類別圖 SRP:單一責任原理 物件導向二十一點模擬程式0.0.0版
物件導向二十一點模擬程式0.0.1版 物件導向二十一點模擬程式0.0.2版
37
UML Unified Modeling Language
由Booch、Rumbaugh、Jacobson三人所創的物件導向軟體圖形表示標準 做為軟體系統的設計文件,使系統維護者容易了解修改程式,另一方面則是軟體工程的工具 背後有一套軟體工程架構,但只適合大規模軟體開發 小範圍程式設計,以較簡易具彈性的Agile Software Development比較實際好用 初學者只須了解幾種簡單的圖形,用來了解或記錄程式架構
38
UML繪製工具 Rational Rose JUDE-Community
可由UML圖形直接產生程式框架,並能保持程式與UML圖形的一致性,但價格相當昂貴 JUDE-Community 免費UML圖形繪製軟體 自2009年年底,JUDE-Community軟體已不再提供下載,改提供較進階的astah-Community 38
39
類別UML符號 39
40
UML 類別圖( Class Diagram )
40
41
不同類別間傳送資料 類別的成員變數通常設為private,不能任意擷取
有人因此乾脆將所有成員變數宣告為public,方便取得,卻失去了使用類別封裝的原意 這種狀況,通常可以畫出類別圖,觀察可能的資料傳送路徑,再於相關類別使用適當的暫存變數或成員變數和成員函式,進行接力傳送資料
42
練習 以大富翁遊戲模擬為目標,規畫版本,進行CRC卡會議,產生CRC卡,繪出UML類別圖 42
43
astah
44
用astah Community畫類別圖 File>New Diagram>Class Diagram
左下角>Base標籤>輸入類別名稱 左下角>Attribute標籤>Add按鈕>輸入資料狀態名稱、型別、可見度 左下角>Operation標籤>Add按鈕>輸入功能函式名稱、傳回值型別、可見度
45
用astah Community畫類別圖 工具列>Association符號旁箭頭>選擇所需Association符號
起點類別圖形按左鍵,轉折點按左鍵 終點類別圖形連按左鍵兩次 按起點、終點類別圖形調整位置 或按Shift鍵點選要對齊的圖形>Alignment>選擇調整方式 Edit>Copy to Clipboard>BMP, PNG File>Save As …
46
綱要 物件導向思維 CRC卡會議 UML類別圖 SRP:單一責任原理 物件導向二十一點模擬程式0.0.0版
物件導向二十一點模擬程式0.0.1版 物件導向二十一點模擬程式0.0.2版
47
單一責任原理 (SRP: Single-Responsibility Principle)
A class should have only one reason to change 若某類別擔負多個責任,某一責任相關的改變會妨礙該類別其他責任的達成 何以不讓Game類別進行某一玩家是否為21點的計算? *Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003 47
48
一個違反SRP的例子 *Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003 48
49
符合SRP的設計 *Robert C. Martin, Agile Software Development: Principles, Patterns, and Practices, Pearson Education, 2003 49
50
綱要 物件導向思維 CRC卡會議 UML類別圖 SRP:單一責任原理 物件導向二十一點模擬程式0.0.0版
物件導向二十一點模擬程式0.0.1版 物件導向二十一點模擬程式0.0.2版
51
BlackJack_0_0_0.Program using System; using System.Diagnostics;
namespace BlackJack_0_0_0 { class Program static void Main(string[] args) Debug.Assert( BlackJackTest.Scenario_1_OK()); }
52
混充類別BlackJack_0_0_0.BlackJackTest
using System; namespace BlackJack_0_0_0 { class BlackJackTest public static bool Scenario_1_OK() return true; }
53
綱要 物件導向思維 CRC卡會議 UML類別圖 SRP:單一責任原理 物件導向二十一點模擬程式0.0.0版
物件導向二十一點模擬程式0.0.1版 物件導向二十一點模擬程式0.0.2版
54
二十一點遊戲模擬v0.1流程 產生牌疊 電腦(莊家)向玩家(一人)及本身派發一張明牌 電腦向玩家及本身派發一張明牌
莊家詢問玩家是否加牌, 直至玩家不加牌或報到 莊家如不足 17點便需加牌直至超過或等於 17點 對未有爆煲或報到的玩家, 比點數大小, 大者勝, 如莊家爆煲, 玩家勝
55
二十一點遊戲模擬原型之測試規畫: 場景1 玩家 莊家 ♠A ♥J ♦10 勝
56
UML 類別圖( Class Diagram )
56
57
Scenario 1: Sequence Diagram
58
BlackJack_0_0_1.BlackJackTest片段
public static bool Scenario_1_OK() { Deck deck = new Deck(); HumanPlayer player = new HumanPlayer(); ComputerPlayer computer = new ComputerPlayer(); player.SaveACard(deck.DealACard()); computer.SaveACard(deck.DealACard()); return (player.IsBlackJack()); }
59
BlackJack_0_0_1.Deck 片段 private Card[] cards; private int top;
public Deck() { cards = new Card[3]; cards[0] = new Card(Suit.SPADE, 1); cards[1] = new Card(Suit.HEART, 11); cards[2] = new Card(Suit.DIAMOND, 10); top = 0; } public Card DealACard() { return cards[top++];
60
BlackJack_0_0_1.Card 片段 public enum Suit { CLUB = 0, DIAMOND = 1,
HEART = 2, SPADE = 3 } public struct Card { public Suit suit; public int rank; public Card(Suit suit, int rank) { this.suit = suit; this.rank = rank;
61
BlackJack_0_0_1.HumanPlayer 片段(1/3)
private Card[] hand = new Card[2]; private int nCards = 0; public HumanPlayer() {} public void SaveACard(Card card) { hand[nCards++] = card; }
62
BlackJack_0_0_1.HumanPlayer片段 (2/3)
public bool IsBlackJack() { int point1 = Points( hand[0].rank ); int point2 = Points( hand[1].rank ); bool isBlackJack = (point1 + point2 == 21); if (!isBlackJack && point1 == 1) { point1 = 11; isBlackJack = (point1 + point2 == 21); }
63
BlackJack_0_0_1.HumanPlayer片段 (3/3)
if (!isBlackJack && point2 == 1) { point2 = 11; isBlackJack = (point1 + point2 == 21); } return isBlackJack; private int Points(int rank) { int points = rank; if (rank > 10) points = 10; return points;
64
BlackJack_0_0_1.ComputerPlayer 片段
Card[] hand = new Card[1]; int nCards = 0; public ComputerPlayer() { } public void SaveACard(Card card) { hand[nCards++] = card; }
65
綱要 物件導向思維 CRC卡會議 UML類別圖 SRP:單一責任原理 物件導向二十一點模擬程式0.0.0版
物件導向二十一點模擬程式0.0.1版 物件導向二十一點模擬程式0.0.2版
66
UML 類別圖
67
BlackJack_0_0_2.BlackJackTest. Scenario_1_OK 片段
Card[] cards = { new Card(Suit.SPADE, 1), new Card(Suit.HEART, 11), new Card(Suit.DIAMOND, 10) }; Deck deck = new Deck(cards); HumanPlayer player = new HumanPlayer(); ComputerPlayer computer = new ComputerPlayer(); player.SaveACard(deck.DealACard()); computer.SaveACard(deck.DealACard()) return (player.GetStatus() == Status.BLACK_JACK && computer.GetStatus() == Status.PASS);
68
BlackJack_0_0_2.Deck 片段 private Card[] cards; private int top = 0;
public Deck(Card[] card) { int nCards = card.Length; this.cards = new Card[nCards]; int i; for (i = 0; i < nCards; ++i) { this.cards[i] = card[i]; } public Card DealACard() { return cards[top++];
69
BlackJack_0_0_2.Status public enum Status { PASS = 0, BLACK_JACK = 1,
BURST = 2 }
70
BlackJack_0_0_2.HumanPlayer片段 (1/6)
private Card[] hand = new Card[3]; private int nCards; private Status status; private int totalPoints; public HumanPlayer() { nCards = 0; } public void SaveACard(Card card) { hand[nCards++] = card; SetStatus();
71
BlackJack_0_0_2.HumanPlayer片段 (2/6)
public Status GetStatus() { return status; } public int GetTotalPoints() { return totalPoints; private int Points(int rank) { int points = rank; if (rank > 10) points = 10; return points;
72
BlackJack_0_0_2.HumanPlayer片段 (3/6)
private void SetStatus() { int[] point = new int[nCards]; int i; int sum = 0; for (i = 0; i < nCards; ++i) { point[i] = Points(hand[i].rank); sum += point[i]; } status = JudgeStatus(sum); totalPoints = sum; if (status != Status.PASS) return; bool isWithAce = false;
73
BlackJack_0_0_2.HumanPlayer片段 (4/6)
for (i = 0; i < nCards; ++i) { if (point[i] == 1) { isWithAce = true; break; } if (isWithAce) { sum += 10; if (sum == 21) { status = Status.BLACK_JACK;
74
BlackJack_0_0_2.HumanPlayer片段 (5/6)
if (sum <= 21) { totalPoints = sum; }
75
BlackJack_0_0_2.HumanPlayer片段 (6/6)
private Status JudgeStatus(int sum) { Status status; if (sum == 21) { status = Status.BLACK_JACK; } else if (sum > 21) { status = Status.BURST; } else { status = Status.PASS; } return status;
76
綱要 物件導向二十一點模擬程式0.0.3版 物件導向二十一點模擬程式0.0.4版
77
UML 類別圖
78
BlackJack_0_0_3.Program.Main 片段
Debug.Assert(BlackJackTest.DeckRandom_OK(), "Deck random mode test failed"); Debug.Assert(BlackJackTest.Game_OK(), "Game test failed"); Console.WriteLine(); Console.WriteLine("21點遊戲開始"); Game game = new Game(); game.Run();
79
BlackJack_0_0_3.BlackJackTest. DeckRandom_OK 片段 (1/3)
Deck deck = new Deck(123); Card[] cards = new Card[52]; int i; for (i = 0; i < 52; ++i) { cards[i] = deck.DealACard(); } int[] nSuit = new int[4]; int s; for (s = 0; s < 4; ++s) { nSuit[s] = 0; int[] nRank = new int[13];
80
BlackJack_0_0_3.BlackJackTest. DeckRandom_OK 片段 (2/3)
int r; for (r = 0; r < 13; ++r) { nRank[r] = 0; } for (i = 0; i < 52; ++i) { switch (cards[i].suit) { case Suit.CLUB: nSuit[0]++; break; nRank[cards[i].rank - 1]++;
81
BlackJack_0_0_3.BlackJackTest. DeckRandom_OK 片段 (3/3)
bool suit_OK = true; for (s = 0; s < 4; s++) { if (nSuit[s] != 13) { suit_OK = false; break; } bool rank_OK = true; for (r = 0; r < 13; r++) { if (nRank[r] != 4) { rank_OK = false; return suit_OK && rank_OK;
82
BlackJack_0_0_3.BlackJackTest. Game_OK
public static bool Game_OK() { Console.WriteLine("Game 測試"); Game game = new Game(); game.Run(123); return true; }
83
BlackJack_0_0_3.Game 片段 private HumanPlayer player = new HumanPlayer(); private ComputerPlayer computer = new ComputerPlayer(); public void Run() { deck = new Deck(); Play(); } public void Run(int seed) { deck = new Deck(seed);
84
BlackJack_0_0_3.Game.Play 片段 (1/3)
player.SaveACard(deck.DealACard()); player.Dump(); computer.SaveACard(deck.DealACard()); computer.Dump(); if (IsBlackJackOrBurst()) return; while (player.GetStatus() == Status.PASS && player.WantOneMoreCard() && deck.HasMoreCard()) {
85
BlackJack_0_0_3.Game.Play 片段 (2/3)
player.SaveACard(deck.DealACard()); player.Dump(); } if (IsBlackJackOrBurst()) return; while ( computer.GetStatus() == Status.PASS && computer.WantOneMoreCard() && deck.HasMoreCard()) { computer.SaveACard(deck.DealACard()); computer.Dump(); Console.WriteLine("比大小分勝負");
86
BlackJack_0_0_3.Game.Play 片段 (3/3)
if (IsBlackJackOrBurst()) return; if (computer.GetTotalPoints() >= player.GetTotalPoints()) { Console.WriteLine("電腦勝"); } else { Console.WriteLine("玩家勝"); }
87
BlackJack_0_0_3.Game. IsBlackJackOrBurst 片段 (1/2)
bool isBlackJack = false; if (player.GetStatus() == Status.BLACK_JACK) { isBlackJack = true; Console.WriteLine("玩家 BlackJack!!!"); } if(computer.GetStatus()== Status.BLACK_JACK) Console.WriteLine("電腦 BlackJack!!!"); bool isBurst = false;
88
BlackJack_0_0_3.Game. IsBlackJackOrBurst 片段 (2/2)
if (player.GetStatus() == Status.BURST) { isBurst = true; Console.WriteLine("玩家爆!!!"); } if (computer.GetStatus() == Status.BURST) Console.WriteLine("電腦爆!!!"); return (isBlackJack || isBurst);
89
BlackJack_0_0_3.Deck 片段 private Card[] cards = new Card[52];
private int top = 0; public Deck() { Random rand = new Random(); PrepareDeck(rand); } public Deck(int seed) { Random rand = new Random(seed);
90
BlackJack_0_0_3.Deck. PrepareDeck 片段 (1/2)
bool[] used = new bool[52]; for (i = 0; i < 52; ++i) { used[i] = false; } pos = rand.Next() % 52; while (used[pos]) { ++pos; pos = pos % 52; s = pos / 13;
91
BlackJack_0_0_3.Deck. PrepareDeck 片段 (2/2)
switch (s) { case 0: cards[i].suit = Suit.CLUB; break; default: } cards[i].rank = pos % ; used[pos] = true;
92
BlackJack_0_0_3.Deck. DealACard & HasMoreCard
public Card DealACard() { return cards[top++]; } public bool HasMoreCard() { return (top < 52);
93
BlackJack_0_0_3.HumanPlayer. WantOneMoreCard
public bool WantOneMoreCard() { Console.Write("要再一張牌嗎? (y/n) "); string answer = Console.ReadLine(); return (answer == "Y" || answer == "y"); }
94
BlackJack_0_0_3.HumanPlayer. Dump 片段
Console.Write("玩家牌: "); for (i = 0; i < nCards; ++i) { hand[i].Dump(); Console.Write("\t"); if ((i + 1) % 5 == 0) Console.WriteLine(); } Console.WriteLine(); Console.WriteLine("玩家總點數: " + totalPoints);
95
BlackJack_0_0_3.ComputerPlayer. WantOneMoreCard
public bool WantOneMoreCard() { return (totalPoints < 17); }
96
綱要 物件導向二十一點模擬程式0.0.3版 物件導向二十一點模擬程式0.0.4版
97
重構(Refactoring) 不好的味道(Bad program flavor) 重構 重複出現的程式碼 不妥的名稱 擷取方法 重新命名
建立新類別 使用繼承
98
BlackJack_0_0_3重構: BlackJack_0_0_4
HumanPlayer與ComputerPlayer中有關判斷status的函式SetStaus、 JudgeStatus、Points完全相同 建立新類別StatusChecker,函式SetStaus, JudgeStatus, Points改成此一類別的靜態函式 SetStatus改名DetermineStatusAndTotalPoints,並修改參數 將列舉型別Status的宣告移到StatusChecker.cs
99
BlackJack_0_0_4.Status public enum Status { PASS = 0, BLACK_JACK = 1,
BURST = 2 }
100
BlackJack_0_0_4.StatusChecker. DetermineStatusAndTotalPoints (1/2)
public static void DetermineStatusAndTotalPoints( Card[] hand, int nCards, out Status status, out int totalPoints) { int[] point = new int[nCards]; int sum = 0; for (i = 0; i < nCards; ++i) { point[i] = Points(hand[i].rank); sum += point[i]; } status = JudgeStatus(sum); totalPoints = sum; if (status != Status.PASS) return; bool isWithAce = false;
101
BlackJack_0_0_4.StatusChecker. DetermineStatusAndTotalPoints (2/2)
for (i = 0; i < nCards; ++i) { if (point[i] == 1) { isWithAce = true; break; } if (isWithAce) { sum += 10; if (sum == 21) { status = Status.BLACK_JACK; } if (sum <= 21) { totalPoints = sum; }
102
BlackJack_0_0_4.HumanPlayer. SaveACard
public void SaveACard(Card card) { hand[nCards++] = card; StatusChecker. DetermineStatusAndTotalPoints( hand, nCards, out status, out totalPoints); }
Similar presentations