本系列编号基本对应stanford CS193P的课程编号,可能有一两节课的误差:比如我标(1)就对应Lecture 1,但有时我做得快了就变成(1)对应lecture 1的全部和lecture 2的一部分。
正文:
本文包括三个类,PlayingCard和PlayingDeck,两个单元测试类,和两个单元测试的用到的工具类。
写完之后的感觉:只要写完view controller这个程序就能在iphone上有个用户界面可以点了,刚做出来的时候还是挺有成就感的。在看swift的电子书只看了一两章的时候我就想把这个做出来,但是没写对。
后来在网上一搜发现别人也有在做这个,而且也在问这个怎么写。然后我借鉴他贴的代码把这个写出来了。再后面把书又读了几章,才决定再次重新开始继续看这个CS193P的课程,这次总算能跟上进度了。
具体值得记录的点有:
1.每个类都可以选TargetMembership,单元测试类应该只选CardGameTest,待测类应该同时选CardGame和CardGameTest。CardGame是我的应用名字。
选对之后我就不用一直用默认提供的CardGameTests这个类了,我后面写了两个分开的单元测试类
2.PlayingCard类里,suit和rank做了合法值校验,比如rank不能大于13,suit不能取四种花色以外的值。
这点在CS193P里,OC下用的做法是重载了这两个变量的setter,我的做法是用了swift的did set功能来校验。
3.PlayingCardTests是我写来测试PlayingCard的。原CS193P的老外是不写单元测试的。但我现在主职业是做测试的,所以我写了单元测试。
思路是一点一点校验PlayingCard类提供的所有东西,包括suit和rank的默认值或最大值,单个变量成功的设置、不成功的设置时的校验、
4.PlayingCardDeckTests里我通过检查deck里所有元素来检查待测的init方法。
并且在其后做了2次抽牌,抽牌之前先记录当前卡组,做随机抽牌,抽牌之后先用正则表达式证明抽到的牌在抽牌之前在卡组中,再用正则表达式判断被抽出的牌在抽牌之后不再在卡组中。
5.DeckPrintHelper只是用来打印当前卡组中的所有牌和打印随机抽出的牌,为了减少重复代码用。Regex是我从网上找来的swift正则表达式封装好的工具类
代码:
首先是ViewController,里面只有一个IBOutlet和一个IBAction
1 // 2 // ViewController.swift 3 // CardGame 4 // 5 // Created by colin.zt on 14-6-6. 6 // Copyright (c) 2014 Ting. All rights reserved. 7 // 8 9 import UIKit 10 11 class ViewController: UIViewController { 12 13 14 @IBOutlet var flipCount : UILabel 15 16 var flipCountNum: Int = 0 17 override func viewDidLoad() { 18 super.viewDidLoad() 19 // Do any additional setup after loading the view, typically from a nib. 20 } 21 22 override func didReceiveMemoryWarning() { 23 super.didReceiveMemoryWarning() 24 25 // Dispose of any resources that can be recreated. 26 } 27 28 @IBAction func touchButton(sender : UIButton) { 29 //check the card title to know which side is upside 30 //then flip it by change the background image and title 31 if (sender.currentTitle.isEmpty){ 32 sender.setTitle("A♣︎", forState: UIControlState.Normal) 33 sender.setBackgroundImage(UIImage(named:"CardFront"),forState: UIControlState.Normal) 34 } else { 35 sender.setTitle("", forState: UIControlState.Normal) 36 sender.setBackgroundImage(UIImage(named:"CardBack"),forState: UIControlState.Normal) 37 } 38 //increse the flip count number 39 flipCountNum++ 40 //update the flip count on screen 41 setFlipCount() 42 } 43 44 func setFlipCount(){ 45 // update the flip count by reset the text of the label 46 flipCount.text = "Flips:" + String(flipCountNum) 47 48 } 49 50 51 }
然后是PlayingCard类,他是Card类的子类
1 // 2 // PlayingCard.swift 3 // CardGame 4 // 5 // Created by colin.zt on 14-10-18. 6 // Copyright (c) 2014 Ting. All rights reserved. 7 // 8 9 import UIKit 10 11 class PlayingCard: Card { 12 //in did set, do a check and make it can't set to invalid value 13 var suit: String = "?" { 14 didSet { 15 //#####could improve or not: is there a simpler way to check if an element is in an array? 16 for it in PlayingCard.valid_suits{ 17 if it == suit{ 18 return 19 } 20 } 21 suit = oldValue 22 } 23 } 24 //in did set, do a check and make it can't set to invalid value 25 var rank: Int = 0 { 26 didSet { 27 if rank > PlayingCard.max_rank{ 28 rank = oldValue 29 } 30 31 } 32 } 33 34 //all valid ranks in this game,"?" means unset 35 class var rank_strings:String[] { 36 return ["?","A","2","3","4","5","6","7","8","9","10","J","Q","K"] 37 } 38 39 //define the max rank 40 class var max_rank:Int{ 41 return rank_strings.count - 1 42 } 43 44 //all valid suits 45 class var valid_suits:String[]{ 46 return ["♥︎","♦︎","♠︎","♣︎"] 47 } 48 49 init() {} 50 51 //just pirnt the rank and suit, and rank 11 will be rank "J",and so n 52 func contents() -> String{ 53 return PlayingCard.rank_strings[self.rank]+self.suit 54 } 55 56 57 }
接着是PlayingCardDeck类,他是Deck类的子类
1 // 2 // PlayingCardDeck.swift 3 // CardGame 4 // 5 // Created by colin.zt on 14-10-20. 6 // Copyright (c) 2014 Ting. All rights reserved. 7 // 8 9 import UIKit 10 11 class PlayingCardDeck: Deck { 12 //just init the deck with 52 cards 13 init(){ 14 super.init() 15 for suit in PlayingCard.valid_suits{ 16 for (var rank = 1; rank <= PlayingCard.max_rank; rank++){ 17 var card: PlayingCard = PlayingCard() 18 card.rank = rank 19 card.suit = suit 20 cards.append(card) 21 } 22 } 23 24 } 25 26 }
为了测试这两个类,写了两个单元测试:
PlayingCardTests
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
PlayingCardDeckTests
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
其中有写打印所有card的方法写在了工具类里
DeckPrintHelper
![](https://i-blog.csdnimg.cn/blog_migrate/cdec0645add3fc3c328197dda5c76203.gif)
还有一个正则表达式的工具类
Regex
![](https://i-blog.csdnimg.cn/blog_migrate/81178cc93a2a3bb5048d90d76e7ec935.gif)
1 // 2 // Regec.swift 3 // CardGame 4 // 5 // Created by colin.zt on 14-10-20. 6 // Copyright (c) 2014年 Ting. All rights reserved. 7 // 8 9 import Foundation 10 11 class Regex: NSObject { 12 let internalExpression: NSRegularExpression 13 let pattern: String 14 15 init(_ pattern: String) { 16 self.pattern = pattern 17 var error: NSError? 18 self.internalExpression = NSRegularExpression(pattern: pattern, options: .CaseInsensitive, error: &error) 19 } 20 21 func is_found_in(input: String) -> Bool { 22 let matches = self.internalExpression.matchesInString(input, options: nil, range:NSMakeRange(0, countElements(input))) 23 var count = matches.count 24 return matches.count > 0 25 } 26 } 27 28 //Usage Example: Regex("\\w{4}").is_found_in("abdddcd")