《52個(gè)有效方法》筆記4——接口與API設(shè)計(jì)

OC中的類簇模式:

OC中的類簇模式和工廠設(shè)計(jì)模式的思想是一致的是嗜。就是若有幾個(gè)功能接近的類溢谤,我們可以給它們定義一個(gè)公共的“抽象類”媳危。使用者只需要和這個(gè)公用的抽象類來打交道就行了,而不必關(guān)心具體某類的細(xì)節(jié)患整。
“工廠模式”這個(gè)名字就非常形象:我們可能會(huì)使用“刀”拜效,“斧”,“錘子”等各種工具各谚,人類一開始都是自產(chǎn)自用的紧憾,也就是說自己造刀斧等工具,但是這樣的話使用者得了解昌渤、掌握各種工具的生產(chǎn)方法或者使用方式赴穗,這對使用者而言是及其麻煩的。社會(huì)漸漸發(fā)展膀息,開始出現(xiàn)了專門制造這些個(gè)工具的“工廠”般眉,只需要告訴它我們需要什么樣的工具,工廠就會(huì)給我們什么樣的工具潜支。比如我想要把錘子甸赃,只需要告訴工廠,我需要的工具是一把錘子冗酿,那工廠內(nèi)部會(huì)開動(dòng)它們的“錘子”生產(chǎn)部門來生產(chǎn)出錘子埠对。

** 看個(gè)代碼例子: **
Staff類就是個(gè)工廠類,它是“抽象基類”裁替,我們需要返回什么樣的員工项玛,只需要傳入員工類型type參數(shù)就行了,具體是怎么生成的弱判,我們不必耗費(fèi)精力去關(guān)心襟沮,Staff這個(gè)工廠類內(nèi)部會(huì)去實(shí)現(xiàn),我們只需等著結(jié)果就行了。

#import "ViewController.h"
#import "Staff.h"

@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    Staff *staff = [Staff staffWithType:StaffType_Coder];
    [staff doSomework];
    NSLog(@"----%@----",[staff class]);   
}

@end
// Staff.h

#import <Foundation/Foundation.h>

typedef NS_ENUM(NSInteger, StaffType)
{
    StaffType_Coder,
    StaffType_Designer,
};

@interface Staff : NSObject

// 傳入type參數(shù)开伏,生成不同類型的員工
+ (Staff *)staffWithType:(StaffType)type;

// 做一些工作...
- (void)doSomework;

@end
// Staff.m

#import "Staff.h"
#import "CoderStaff.h"
#import "DesignerStaff.h"

@implementation Staff

// 傳入type參數(shù)膀跌,生成不同類型的員工
+ (Staff *)staffWithType:(StaffType)type
{
    switch (type)
    {
        case StaffType_Coder:
            return [CoderStaff new];
            break;
            
        case StaffType_Designer:
            return [DesignerStaff new];
            break;

        default:
            break;
    }
}


// 做一些工作...
- (void)doSomework
{
    NSLog(@"the staff is working...");
}


// 為了避免調(diào)用者直接調(diào)用基類的初始化方法,可以返回nil,或者拋出異常提醒調(diào)用者硅则。
- (instancetype)init
{
    return nil;
}

@end

所謂設(shè)計(jì)模式淹父,就是在長期實(shí)踐過程中琢磨出的更好的一種方式,不是說非使用設(shè)計(jì)模式不可怎虫,非用設(shè)計(jì)模式其他方式不可實(shí)現(xiàn)。其實(shí)像上面的例子困介,我們完全可以把CoderStaff大审、DesignerStaff這些類提供給調(diào)用者,在其中分別實(shí)現(xiàn)相應(yīng)的初始化方法座哩。但是這樣的話徒扶,調(diào)用者就得了解每個(gè)類提供的方法等,而“類簇”妙就妙在讓調(diào)用者不必勞心關(guān)注太多細(xì)節(jié)根穷,需要什么告訴我姜骡,我給你。


關(guān)于初始化方法

我們通常在自己寫的獨(dú)立的控件等時(shí)需要給外部提供調(diào)用接口屿良,當(dāng)然首要的是初始化方法圈澈,而且通常要提供多個(gè)初始化方法。
其中尘惧,我們應(yīng)定義一個(gè)“全能初始化方法”康栈,即該初始化方法能為對象提供足夠的信息以完成創(chuàng)建。而其他初始化方法在內(nèi)部其實(shí)都調(diào)用它喷橙。

// Rectangle.h

#import <Foundation/Foundation.h>

@interface Rectangle : NSObject

@property (nonatomic, assign) float         width;
@property (nonatomic, assign) float         height;

- (instancetype)initWithWidth:(float)width andHeight:(float)height;

@end
// Rectangle.m

#import "Rectangle.h"

@implementation Rectangle

- (instancetype)init
{
    return [self initWithWidth:100.f andHeight:20.f];
}

- (instancetype)initWithWidth:(float)width andHeight:(float)height
{
    self = [super init];
    if(self)
    {
        _width = width;
        _height = height;
    }

    return self;
}

@end

Rectangle類提供了initWithWidth:andHeight:這個(gè)初始化方法(它算該類的全能初始化方法)啥么。但是不排除調(diào)用者調(diào)用了init這個(gè)模式初始化方法,所以我們重寫它贰逾,在其內(nèi)部調(diào)用全能初始化方法悬荣,傳入默認(rèn)值的參數(shù)。
假如疙剑,此時(shí)又定義了一個(gè)子類Square氯迂。它繼承于基類Rectangle。

// Square.h

#import "Rectangle.h"

@interface Square : Rectangle

- (instancetype)initWithLength:(float)length;

@end

Square.h暴露給外部一個(gè)initWithLength:的初始化方法核芽。

// Square.m

#import "Square.h"

@implementation Square

- (instancetype)init
{
    self = [super init];
    return [self initWithLength:100.f];
}

// 重寫父類的初始化方法
- (instancetype)initWithWidth:(float)width andHeight:(float)height
{
    float resultLength = MAX(width, height);
    return [self initWithLength:resultLength];
}


- (instancetype)initWithLength:(float)length
{
   return [self initWithWidth:length andHeight:length];
}

@end

需要注意的是囚戚,既然Square繼承于基類Rectangle,那調(diào)用者也有調(diào)用基類的initWithWidth:andHeight:這個(gè)初始化方法的可能性轧简。要么驰坊,我們可以像上面一樣,返回一個(gè)以width和height倆較大值為邊長的正方形哮独。但也可從另外一個(gè)角度想:認(rèn)為這屬于調(diào)用者調(diào)用錯(cuò)誤拳芙,而拋出異常提醒調(diào)用者不該使用父類的初始化方法察藐。


盡量使用不可變對象。

“盡量使用不可變對象”旨在安全考量舟扎。
** 有時(shí)我們只想在類內(nèi)部修改數(shù)據(jù)分飞,但不想令這些數(shù)據(jù)為外人所改動(dòng)這時(shí),我們可以把暴露給外部的屬性聲明為readonly睹限,但在內(nèi)部重新聲明為readwrite譬猫。**

// Staff.m

#import <Foundation/Foundation.h>

@interface Staff : NSObject

@property (nonatomic, copy, readonly)NSString       *staffId; // 工號
@property (nonatomic, copy, readonly)NSString       *salay;  // 薪水

@end
// Staff.m

#import "Staff.h"
#import "CoderStaff.h"
#import "DesignerStaff.h"

@interface Staff ()

@property (nonatomic, copy, readwrite)NSString      *staffId;
@property (nonatomic, copy, readwrite)NSString      *salay;

@end

@implementation Staff


@end

在Staff.m的Extension(拓展)中重新聲明屬性為readwirite。現(xiàn)在羡疗,只能在Staff內(nèi)部設(shè)置這些屬性值染服,外部僅是可讀的。
** 注意 :** 即便上面這樣寫了叨恨,但是還是有法子在外部修改屬性值的柳刮,比如可以使用KVC修改屬性值。不過痒钝,沒有絕對的安全秉颗,上面這樣寫總歸要規(guī)范嚴(yán)謹(jǐn)?shù)枚唷?/p>

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    Staff *staff = [[Staff alloc] init];
    [staff setValue:@"10000.00" forKey:@"salary"];
    NSLog(@"salary----%@",staff.salary);
}
// 2016-03-11 00:39:34.791 WangDemo310[5844:106406] salary----10000.00

** 不要把可變的集合類作為屬性公開,而應(yīng)該提供相關(guān)方法送矩,以此來修改對象中可變的集合類 **
比如下面Person類蚕甥,它是可以進(jìn)行添加、刪除好友操作的益愈,但是作為屬性暴露給外部的是一個(gè)不可變的(只可讀的)的NSArray梢灭,而在內(nèi)部卻是

// Person.h

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, copy)NSString     *personId;
@property (nonatomic, copy)NSString     *name;
@property (nonatomic, strong)NSArray    *myFriendsArr;

- (instancetype)initWithPerson:(NSString *)personId andName:(NSString *)name;

- (void)addFriend:(Person *)perspon;

- (void)removeFriend:(Person *)person;

@end
// Person.m

#import "Person.h"

@interface Person ()
{
    NSMutableArray      *_friendsMarr; // 內(nèi)部維護(hù)的其實(shí)是個(gè)可變的數(shù)組
}

@end

@implementation Person

- (instancetype)initWithPerson:(NSString *)personId andName:(NSString *)name
{
    self = [super init];
    if(self)
    {
        _personId = personId;
        _name = name;
        _friendsMarr = [[NSMutableArray alloc] init];
    }
    
    return self;
}

- (NSArray *)myFriendsArr
{
    return [_friendsMarr copy]; // _friendsMarr本是可變的,進(jìn)行copy操作蒸其,成為不可變的敏释,然后返回給外部。
}

- (void)addFriend:(Person *)perspon
{
    [_friendsMarr addObject:perspon];
}

- (void)removeFriend:(Person *)person
{
    [_friendsMarr removeObject:person];
}

@end

在Peson內(nèi)部維護(hù)的其實(shí)是個(gè)可變的NSMutableArray摸袁,可以進(jìn)行add和remove操作钥顽,但通過copy動(dòng)作后暴露給外部卻是一個(gè)不可變的NSArray。這和第一個(gè)例子的思想其實(shí)是一致的:** 只給外部暴露需要暴露的靠汁,而且暴露的東西得不可變蜂大,外人不能更改。 **


關(guān)于OC中的錯(cuò)誤蝶怔、異常

首先奶浦,OC中的異常應(yīng)該盡量少用,因?yàn)椤白詣?dòng)引用計(jì)數(shù)”不是異常安全的踢星。即當(dāng)拋出異常時(shí)澳叉,本應(yīng)在作用域末尾進(jìn)行釋放的代碼便不會(huì)執(zhí)行了,這就會(huì)造成內(nèi)存泄露。
所以成洗,異常只應(yīng)該用于極其嚴(yán)重的錯(cuò)誤五督,比如本應(yīng)使用子類方法,調(diào)用者卻使用了“抽象類”基類的方法瓶殃,這時(shí)應(yīng)該重寫基類的這些方法充包,在其中拋出異常,提醒調(diào)用者不能調(diào)用基類的方法遥椿。

對于一般性錯(cuò)誤基矮,可以返回nil和或者0什么的提示調(diào)用者這樣做有問題。

而NSError修壕,可以把導(dǎo)致錯(cuò)誤的信息回饋給調(diào)用者愈捅。
NSError常見用法:
1.在代理方法或者block回調(diào)中把錯(cuò)誤信息傳遞給調(diào)用者;
2.經(jīng)由方法的“輸出參數(shù)”返回給調(diào)用者慈鸠,(NSError **)指向指針的指針。

- (BOOL)doSomething:(NSError **)error;
傳遞給方法的參數(shù)是個(gè)指針灌具,該指針又指向另外一個(gè)指向NSError對象的指針青团。或者也可以把它當(dāng)成一個(gè)直接指向NSError對象的指針咖楣。
** 該方法一般返回BOOL類型的結(jié)果督笆,表示操作成功與否。這么一來诱贿,該方法既可以返回表示該操作成功與否的bool值娃肿,而且還能經(jīng)由“輸出參數(shù)”把NSError對象回傳給調(diào)用者,告訴調(diào)用者該操作返回NO的錯(cuò)誤細(xì)節(jié)珠十。**

// 調(diào)用
    NSError *error = nil;
    BOOL result = [self doSomething:&error];
    if(error){
        // 說明有錯(cuò)誤料扰,此時(shí)result一般為NO
        NSLog(@"錯(cuò)誤細(xì)節(jié):%@",error.domain);
    }
- (BOOL)doSomething:(NSError **)error
{
    if(/* there was a error*/1) // 當(dāng)出現(xiàn)錯(cuò)誤時(shí)
    {
        if(error){ // 并且存在error參數(shù)
            *error = [NSError errorWithDomain:@"xxx" code:-1 userInfo:nil]; // 生成error對象
            return NO;
        }else{
            return YES;
        }

    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市焙蹭,隨后出現(xiàn)的幾起案子晒杈,更是在濱河造成了極大的恐慌,老刑警劉巖孔厉,帶你破解...
    沈念sama閱讀 211,948評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件拯钻,死亡現(xiàn)場離奇詭異,居然都是意外死亡撰豺,警方通過查閱死者的電腦和手機(jī)粪般,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,371評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來污桦,“玉大人亩歹,你說我怎么就攤上這事。” “怎么了捆憎?”我有些...
    開封第一講書人閱讀 157,490評論 0 348
  • 文/不壞的土叔 我叫張陵舅柜,是天一觀的道長。 經(jīng)常有香客問我躲惰,道長致份,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,521評論 1 284
  • 正文 為了忘掉前任础拨,我火速辦了婚禮氮块,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诡宗。我一直安慰自己滔蝉,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,627評論 6 386
  • 文/花漫 我一把揭開白布塔沃。 她就那樣靜靜地躺著蝠引,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛀柴。 梳的紋絲不亂的頭發(fā)上螃概,一...
    開封第一講書人閱讀 49,842評論 1 290
  • 那天,我揣著相機(jī)與錄音鸽疾,去河邊找鬼吊洼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛制肮,可吹牛的內(nèi)容都是我干的冒窍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,997評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼豺鼻,長吁一口氣:“原來是場噩夢啊……” “哼综液!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拘领,我...
    開封第一講書人閱讀 37,741評論 0 268
  • 序言:老撾萬榮一對情侶失蹤意乓,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后约素,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體届良,經(jīng)...
    沈念sama閱讀 44,203評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,534評論 2 327
  • 正文 我和宋清朗相戀三年圣猎,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了士葫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,673評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡送悔,死狀恐怖慢显,靈堂內(nèi)的尸體忽然破棺而出爪模,到底是詐尸還是另有隱情,我是刑警寧澤荚藻,帶...
    沈念sama閱讀 34,339評論 4 330
  • 正文 年R本政府宣布屋灌,位于F島的核電站,受9級特大地震影響应狱,放射性物質(zhì)發(fā)生泄漏共郭。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,955評論 3 313
  • 文/蒙蒙 一疾呻、第九天 我趴在偏房一處隱蔽的房頂上張望除嘹。 院中可真熱鬧,春花似錦岸蜗、人聲如沸尉咕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,770評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽年缎。三九已至,卻和暖如春铃慷,著一層夾襖步出監(jiān)牢的瞬間晦款,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,000評論 1 266
  • 我被黑心中介騙來泰國打工枚冗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛇损。 一個(gè)月前我還...
    沈念sama閱讀 46,394評論 2 360
  • 正文 我出身青樓赁温,卻偏偏與公主長得像,于是被迫代替她去往敵國和親淤齐。 傳聞我的和親對象是個(gè)殘疾皇子股囊,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,562評論 2 349

推薦閱讀更多精彩內(nèi)容