1. 程式人生 > >iOS類簇(Class Cluster)使用心得

iOS類簇(Class Cluster)使用心得

轉自:http://www.tuicool.com/articles/YJFvMnb

我們都知道在iOS中類簇的使用是非常普遍的,比如NSNumber, NSString, NSArray等等都是類簇。我們以NSNumber距離來說,對於int, bool, unsigned int等等資料型別,我們如何把他們封裝成類的形式呢?通常情況下我們可能會想到,對於每一種資料型別獨立封裝成一個類,比如對於int型別我們可以做一個NSInt的類,以此類推。這樣想是正確的,但是,我們再來想想這樣會有什麼問題呢?當需要支援的資料型別越來越多時,這些類也會相應的越來越多,那麼對於開發者來說,我們就需要記住越來越多的類。那麼還有沒有好的方案呢?哈哈,這個是hi偶類簇就閃亮登場了。

現在我們遇到的問題是,相似的類越來越多,對於開發者來說需要太多的記憶。顯然,我們解決問題的方向肯定是朝著減少暴露給開發者的類的數量這個方向。讓我們來回憶一下設計模式,抽象工廠模式不就是幹這個事情的嘛!

對於NSNumber,我們使用一個NSNumber來作為int, bool, long等原生資料型別的工廠,對外定義一堆的工廠方法,在NSNumber內部則轉調給相應的像NSInt這樣的內部類去實現具體的邏輯。這樣,我們遇到的問題就得到了完美的解決。

下面我們來自己實現一個具體的類簇。場景是這樣的,我現在有一個APP,只支援英文版,現在我想讓它也支援中文版和日文版等等。我們先來看看模型類吧!

@interface PowerUpDetail: WMModelObject

@property(nonatomic, copy) NSString *name;
@property(nonatomic, copy) NSString *desc;

@end

name和desc分別是英文版對應的欄位,現在我想實現中文版,併為以後的其他語言版本做好擴充套件結構。

現在有幾個方案:

  1. 繼承PowerupDetail,實現子類PowerupDetailCN和PowerupDetailEN在所有呼叫PowerupDetail的地方,區分出到底應該呼叫PowerupDetailCN還是PowerupDetailEN。
  2. 把PowerupDetail做成類簇,對外暴露通用介面,對內轉調給內部類來實現具體邏輯。
方案1是最容易想到的,但是它的缺點是需要對已有的呼叫程式碼進行修改,而且如果將來隨著越來越多的語言被支援,這些類將會非常多,在呼叫的地方需要一堆的if-else語句來區分,程式碼很醜,工作量很大。

方案2是比較好的一種方案,做到了對外暴露最小化,而所有呼叫的地方都可以不用修改,所有的修改可以只在PowerupDetail這個類中完成。

下面上程式碼~(≧▽≦)/~啦啦啦

在PowerUpDetail.h檔案中

@interface PowerUpDetail:WMModelObject

@property(nonatomic, copy) NSString*name;
@property(nonatomic, copy) NSString*desc;

-(NSString *)localizedName;
-(NSString *)localizedDesc;

@end

在PowerUpDetail.m檔案中:
#pragma mark - For English Version
@interface _PowerUpDetailEN:PowerUpDetail
@end

@implementation _PowerUpDetailEN
@end

#pragma mark - For Chinese Version
@interface _PowerUpDetailCH:PowerUpDetail
@property(nonatomic, copy) NSString *nameCH
@property(nonatoic, copy)NSString*descCH;
@end

@implementation _PowerUpDetailCH

-(instancetype)initWithDictionary:(NSDictionary *)dic{
    if(self = [super initWithDictionary:dic]){
        _nameCH = [[dic objectForKey:@"nameCH"]copy];
        _descCH = [[dic objectForKey:@"descCH"]copy];
    }
    return self;
}

-(NSString *)localizedName{
    return self.nameCH;
}

-(NSString *)localizedDesc{
    return self.descCH;
}

@end

#pragma mark - Class Cluster
@impelmentation PowerUpDetail

+(instancetype)alloc{
    if([self class] == [PowerUpDetail class]){
        if (WMIsChinesePreference){
            return [_PowerUpDetailCH alloc];
        }else{
            return [_PowerUpDetailCH alloc];
        }else{
            return [super alloc];
        }
    }
}

-(instancetype)initWithDictionary:(NSDictionary *)dic{
    if (self = [super initWithDictionary:dic]){
        _name = [[dic objectForKey:@"name"]copy];
        _desc = [[dic objectForKey:@"desc"]copy];
    }
    return self;
}

-(NSString *)localizedName{
    return self.name;
}

-(NSString *)localizedDesc{
    return self.desc;
}

@end

這裡,我實現了兩個內部類,_PowerUpDetailCH和_PowerUpDetailEN,他們都繼承於PowerUpDetail,而PowerUpDetail暴露給外邊兩個方法:
-(NSString*) localizedName;
-(NSString*) localizedDesc;

這些方法可以再子類中按需要進行重寫。

需要特別注意下類簇PowerUpDetail的alloc方法,這裡要注意

if ([self class] == [PowerUpDetail class])
這個條件的使用,否則會出現死迴圈,原因你應該很容易看出來,我就不囉嗦了!
+(instancetype)alloc{
    if([self class] == [PowerUpDetail class]){
        if(WMIsChinesePreference){
            return[_PowerUpDetailCH alloc];
        }else{
            return [_PowerUpDetailCH alloc];
        }
    }else{
        return[super alloc];
    }
}

OK,我講完了,類簇其實很簡單,就是一個抽象工廠模式的iOS版本的實現。

另外,推薦閱讀一下這篇文章類簇在iOS開發中的應用,我也是從這篇文章學到的知識,非常感謝李忠。