一、起點:以「書名」來找一本書
目前 API 還沒有任何一行 code 被產生。沒關係,我們先在 API 的 client 寫下欲設計的結果。
for (NSString *bookName in @[@"Objective-C 真簡單", @"Objective-C 學不會"]) {
[self printReportHeader:bookName];
[BookFinder bookWithName: bookName //以書名找書
actionIfFound: foundAction //若有找到書,就做 foundAction
actionIfNotFound: notFoundAction]; //若找不到書,就做 notFoundAction
}
- (void)printReportHeader:(NSString *)bookName
{
NSLog(@"======================================");
NSLog(@"==> 欲尋找的書籍: %@", bookName);
}
上面就是我想要的結果:一個 BookFinder 以 bookWithName:actionIfFound:actionIfNotFound 這個 Class Method 查找書籍。這個 Method 以書名(bookName)作為參數傳入,若書有找到,就執行 actionIfFound;反之,沒找到書,就執行 actionIfNotFound。
注意,上面我用「執行」作為及物動詞,而 actionIfFound 和 actionNotFound 為受詞。可見這兩個 action 不是單純的 object instance;事實上,它們是 block 。
繼續,我們進一步設計 actionIfFound 和 actionNotFound 這兩個 block 的工作內容。這兩個 block 是所謂的 in-line block :
void (^foundAction)(Book *) = ^(Book *book) {
NSLog(@"==> 書籍售價: %@", book.price);
};
void(^notFoundAction)(BookInfo *) = ^(BookInfo *bookInfo) {
NSLog(@"==> 找不到的原因: %@", bookInfo.reason);
};
這裡設計:若找到書,則把 Book 的 object 作為參數傳入 block ,我們就可以印出書籍的價格;若沒找到書,block 內應該能從 BookInfo 的 object 中取得找不到的原因並印出來。
然後我預想一下我要的輸出結果:
======================================
==> 欲尋找的書籍: Objective-C 真簡單
==> 書籍售價: 100
======================================
==> 欲尋找的書籍: Objective-C 學不會
==> 找不到的原因: 絕版
嗯,差不多了,開始把其他的程式碼長出來吧!
in BookFinder.h
宣告出我們在 Client 中使用的 Class Method :
+(void) bookWithName:(NSString *)name actionIfFound:(BookFindAction)actionIfFound actionIfNotFound:(BookNotFindAction)actionIfNotFound;
上面的 BookFindAction 和 BookNotFindAction 是自訂的 Block,使用 C 語言的 typedef 進行定義:
typedef void(^BookFindAction)(Book *);
typedef void(^BookNotFindAction)(BookInfo *);
從上式可知,這兩個 Block 都沒有回傳值 (void),傳入的參數則是遵照在 Client 端程式碼怎麼用它們來設計。記得,上面兩式得寫在 @interface BookFinder : NSObject 區段的上方才行哦!
in BookFinder.m
+(void) bookWithName:(NSString *)name actionIfFound:(BookFindAction)actionIfFound actionIfNotFound:(BookNotFindAction)actionIfNotFound
{
Book *book = [BookFinder findByName: name];
if (book != nil) {
actionIfFound(book);
} else {
BookInfo *bookInfo = [BookInfo new];
bookInfo.reason = @"絕版";
actionIfNotFound(bookInfo);
}
}
+(Book *)findByName:(NSString *)name
{
if ([name isEqual:@"Objective-C 真簡單"]) {
Book *book = [Book new];
book.name = name;
book.price = @100;
return book;
}
return nil;
}
這裡的實作很簡單,透過一個簡單的比對,只要書名不是「Objective-C 真簡單」,就當那本書絕版。其餘的實作邏輯也是完全遵照一開始在 Client 端程式碼的設定。
至於 Book 和 BookInfo 只是兩個設定了 Property 的簡單類別,就不列出來佔版面了。
上面所謂「Client 端的程式碼」,若是寫在 Unit Test 中當然更好,不但能更佳的突顯、收納設計意圖,也能時時驗證。
這裡展示的是如何在對於 Block 最少量所知、而且尚未有任何程式碼前進行設計,並一步步把設計實現的思維及步驟,希望對於尚未使用 Block 在自己的程式裡進行設計的人能有所助益。
沒有留言:
張貼留言