參考書籍:《Head First Design Patterns》
OO設(shè)計原則:
1.多組合,少繼承
2.針對接口編程,不對實現(xiàn)編程
3.松耦合
4.類對擴展開放對修改關(guān)閉
5.依賴抽象不依賴具體
書中的原版內(nèi)容就默默地私下背誦吧,我就講一下我在這個例子中對工廠模式的使用:
實際項目需求:用戶數(shù)據(jù)中心設(shè)計,存儲一般用戶的操作歷史,個人信息,收藏等數(shù)據(jù)。
下圖為所有數(shù)據(jù)類型及功能比較:

先具體的分析需求,將需求歸納比較,找出公共的部分及必須依賴的部分:
相同點:初步的一看,貌似像上圖所示的那樣,預約數(shù)據(jù)與收藏數(shù)據(jù)是數(shù)據(jù)分頁需求的,比如添加一個“顯示更多”的按鈕,且這兩種數(shù)據(jù)好像是不需要進行排序的,每次加入的數(shù)據(jù)排到隊尾即可,但觀看歷史和??搭l道及搜索歷史這三種數(shù)據(jù)是由明確的排序需求的,我想最近觀看的歷史必須放到最前面,乍一看的確是這樣。但是可以用歸納總結(jié)的方式來想一想,預約數(shù)據(jù)和收藏數(shù)據(jù)有分頁需求,但是觀看歷史也許很少,并沒有分頁需求,所以若設(shè)計一個能滿足所有數(shù)據(jù)類型需求的大工廠的話,必須講究更加細則的需求,即為所有的數(shù)據(jù)提供分頁功能,類似于觀看歷史這樣的少量的數(shù)據(jù)就可以全部放在第一頁中了,即第一頁數(shù)據(jù)就放置所有的數(shù)據(jù)。再說排序功能,既然搜索歷史和觀看記錄這樣的對排序有著嚴格的需求,所以工廠必須提供此方法,至于預約數(shù)據(jù)與收藏甚至未來會出現(xiàn)的更多的沒有排序需求的數(shù)據(jù),也得滿足排序的需求,而且,我相信從一位用戶的角度來看,我將您最近預約的數(shù)據(jù)放在第一個,相信用戶也是不會在意的,說不定體驗更好。
不同點:每種數(shù)據(jù)總歸是有很多不同的地方,所以在開始構(gòu)造工廠框架之前,就要先盡量多的考慮到數(shù)據(jù)間的不同之處,這些是工廠無法提供包辦的地方。比如:數(shù)據(jù)存貯的最大數(shù)量,每頁數(shù)據(jù)的數(shù)目等等。
下面先給出我最初的設(shè)計思路,這種思路很簡單,幾乎是聽到需求后所有新手都會想到的:
就拿收藏和歷史來說吧:
歷史數(shù)據(jù)類m文件:
#import"HistoryDataManager.h"
@interfaceHistoryDataManager()
{
NSMutableArray*dataArr;
}
@end
@implementationHistoryDataManager
- (void)addData:(NSString*)data{
[dataArr addObject:data];
}
- (void)deleteData:(NSString*)data{
[dataArr removeObject:data];
}
@end
收藏數(shù)據(jù)類m文件:
#import"CollectDataManager.h"
@interfaceCollectDataManager()
{
NSMutableArray*dataArr;
}
@end
@implementationCollectDataManager
- (void)addData:(NSString*)data{
[dataArr addObject:data];
}
- (void)deleteData:(NSString*)data{
[dataArr removeObject:data];
}
@end
使用時將兩個類定義為單例來調(diào)用,可以清楚地感受到,這樣做對業(yè)務(wù)層來說接口簡單,易理解,但是仔細的看兩個類的實現(xiàn)中,有大部分功能是完全一樣的,甚至接口可以定義的一模一樣,那這樣,是否能將這部分重復的功能歸納出一個框架呢,那么所有的數(shù)據(jù)就不用再自己實現(xiàn)一遍這些公有的功能了,這就是我想用工廠模式來解決這個需求的初衷。下面講講我是怎樣避免大段重復的代碼,盡量精簡的完成類似功能的類的構(gòu)建的吧。
首先建立一個工廠,盡量滿足所有的數(shù)據(jù)類的需求,但是也正是因為這一點,這個工廠的拓展性不如我預期的好,關(guān)于工廠模式下對于工廠的拓展方面希望能得到更好的指點。同時,考慮到OC中并不存在抽象類,但是我想讓所有的數(shù)據(jù)類都擁有我提供的方法,那么必須用繼承來實現(xiàn)了,其次,關(guān)于上述中提到的除了共同之處還有很多不同之處存在的,而這些不同就用OC中的協(xié)議方式來實現(xiàn)。下面貼出工廠的偽代碼:
工廠協(xié)議聲明:
@protocol ZYDataManagerProtocol
@required
/**
*子類來創(chuàng)建單例,并在單例中初始化父類,保證每一單例都擁有單獨的內(nèi)存空間
*
*@return返回子類單例對象
*/
+ (instancetype)sharedInstance;
/**
*為maxDataCount的get方法,由子類來重寫此方法,設(shè)置保存的數(shù)據(jù)最大數(shù)量
*
*@return返回保存的最大數(shù)目值
*/
- (NSInteger)maxDataCount;
/**
*為eachPageNum的get方法,由子類來重寫此方法,設(shè)置每頁顯示的數(shù)據(jù)數(shù)
*
*@return返回每頁返回的數(shù)據(jù)數(shù)目
*/
- (NSInteger)eachPageNum;
工廠類中的添加數(shù)據(jù),獲取數(shù)據(jù)以及刪除數(shù)據(jù)方法舉例:
- (void)addData:(id)data{
//判斷本地是否已有此數(shù)據(jù),若有則先刪除該條數(shù)據(jù)
[self deleteDataIfExistWithData:data];
//判斷數(shù)組是否已經(jīng)到達上界,若達到上界,則刪除最后的一個
if(operateDataArray.count==aMaxDataCount) {
[operateDataArray removeLastObject];
}
//將新數(shù)據(jù)插入到最前
[operateDataArray insertObjectAtIndex:0];
}
- (NSArray*)getDataWithPageNum: (NSInteger)pageNum{
NSArray *pageArray = [[NSArray alloc] init];
if(aEachPageNum<1) {
return pageArray;
}
if(!operateDataArray.count) {
return;
}
//判斷用戶請求的頁數(shù)是否超過本身的頁數(shù),若超過返回空,若不超過,返回該頁的數(shù)據(jù)
if(pageNum <=operateDataArray.count/aEachPageNum+1&& pageNum >0) {
NSInteger startIndex = (pageNum-1) *self.eachPageNum;
NSInteger endRange;
if((operateDataArray.count- startIndex)
endRange =operateDataArray.count- startIndex;
}else{
endRange =aEachPageNum;
}
pageArray = [operateDataArray objectsAtIndexes:[NSIndex SetindexSetWithIndexesInRange:NSMakeRange(startIndex, endRange)]];
}
return pageArray;
}
- (void)deleteData:(id)data{
[self deleteDataIfExistWithData:data];
}
- (void)deleteAllData{
[operateDataArray removeAllObjects];
}];
}
- (BOOL)isDataExist:(id)data{
BOOL ifExist =NO;
if([operateDataArray containsObject:data]) {
ifExist =YES;
}
return ifExist;
}
#pragma mark - Private Methods
//判斷本地是否已有此數(shù)據(jù),若有則先刪除該條數(shù)據(jù)
- (void)deleteDataIfExistWithData:(id)data{
//判斷是否存在此對象,若存在,則刪除對應(yīng)的對象
if([operateDataArray containsObject:data]) {
[operateDataArray removeObject:data];
}
}
我寫了一個demo,詳細代碼請見demo,放在github上,地址為:https://github.com/shirleyzyy/UserDataCenter.git
子類繼承后需要自己實現(xiàn)工廠的協(xié)議,就像你要開一家加盟店,但是首先得遵循總店的要求,總店也必須知道你這家加盟店所要買的商品數(shù)量等。
子類偽代碼如下:
@implementation ZYHistoryDataManager
#pragma mark - DataManagerProtocol
+ (instancetype)sharedInstance{
static dispatch_once_t predicate;
static ZYHistoryDataManager *instance =nil;
dispatch_once(&predicate, ^{
instance = [[super alloc] initUniqueInstance];
});
return instance;
}
-(instancetype) initUniqueInstance {
return[super init];
}
- (NSInteger)maxDataCount{
return20;
}
- (NSInteger)eachPageNum{
return10;
}
即實現(xiàn)單例和父類所定義的協(xié)議方法就OK了,在實際的項目中,這個工廠同時為近十種數(shù)據(jù)提供服務(wù),目前未出現(xiàn)重大的bug,但是如果希望代碼得到更好的精簡的話我建議將工廠中操控可遍數(shù)組的queue設(shè)置為其他類的單例,因為所有的數(shù)據(jù)操作都由這個工廠完成,然而工廠只有一個,換句話說,對于對數(shù)據(jù)存儲效率不高的情況下,完全可以工廠只持有一個queue,而不是為所有的子類新建queue,這個唯一的queue保持串行執(zhí)行就OK了,這個我也試驗過,也是可以的。暫時就記錄這些了,也許我說的并不深刻,但是我有一顆虛心學習的心,歡迎各路大俠指點迷境_!