iOS代碼重構之提升類的接口抽象能力

今天看到《代碼大全第二版》裏面對ADT及類抽象能力的描述讓我受益不淺,查看咱們的iOS項目,立刻就找到了這樣的壞味道(不良好的類接口設計),下面就分享一下個人改進方法。(這裏的類名作了修改,並不是實際項目中的類名)bash


改進前:

這是一個自定義單元格類:ide

@protocol  CustomCellDeletage <NSObject>

- (void)checkDidTouch:(CustomCell *)cell;

@end

@interface CustomCell : UITableViewCell

@property (nonatomic, weak)id<CustomCellDeletage> delegate;

@property (nonatomic, retain)UIButton * checkBtn;
//這裏直接暴露了類的成員變量細節,致使類對自身內部控制的鬆動,按鈕的狀態改變將難以追蹤,而且這個類和按鈕對象如何使用也會讓調用方摸不着頭腦,此次的改動將以此爲重

@end

@implementation CustomCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.selectionStyle = UITableViewCellSelectionStyleNone;
        _checkBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_checkBtn addTarget:self action:@selector(checkBtnPressed:) forControlEvents:UIControlEventTouchUpInside];
        //...省略部分代碼

    }
    return self;
}
- (void)checkBtnPressed:(id)sender {
    // 切換選中狀態
    _checkBtn.selected = !_checkBtn.selected;
    
    if (self.delegate && [self.delegate respondsToSelector:@selector(checkDidTouch:)]) {
        [self.delegate checkDidTouch:self];
    }
}
複製代碼

外部Controller調用:函數

cell.checkBtn.selected = [self isChecked:orderDict[@"orderId"]];
複製代碼

外部Controller回調:ui

#pragma mark CustomCellDeletage ---- 點擊選擇按鈕事件
- (void)checkDidTouch:(CustomCell *)cell {
    // 若是是選中狀態,就添加   反之這移除
    if (cell.checkBtn.selected) {
        [self.checkArray addObject:cell.orderDict];
    } else {
        [self.checkArray removeObject:cell.orderDict];
    }
    [self updateViewInfo];
}
複製代碼

這裏根據《代碼大全第二版》做者書裏對ADT好處的描述來作代碼評價。因爲這裏直接暴露了類的成員變量checkBtn而帶來了不少壞處。
ADT(抽象數據類型)
  • 隱藏實現細節

若是數據類型發生改變,你只需在一處修改而不會影響程序的其他部分。例如這裏若是由UIButton類型變爲UISwitch,應只須要改動接口的具體實現,而不影響接口調用方atom

  • 讓接口能提供更多信息,程序更具自我說明性

例如這裏後面改進了類的接口爲selectOn和selectOff一組更具說明意義的方法spa

抽象
  • 類的接口爲隱藏在其後的具體實現提供一種抽象
  • 類的接口應該展現一致的抽象層次

例如這裏cell單元格類,應提供選中單元格和取消選中單元格的接口,而不是直接暴露成員變量設計

  • 儘量地限制類和成員的可訪問性以提升封裝性
  • 不要公開暴露成員數據
  • 避免把私用的實現細節放入類的接口中

核心改動思想

把對按鈕的操縱隔離到一組子程序裏,爲須要操做按鈕的其它程序部分提供更好的抽象層,同時能夠在針對按鈕屬性狀態的操做發生變化時提供一層保護。 像在現實世界中那樣操做實體,而不用在底層實現上操做它code


改進

@protocol  CustomCellDeletage <NSObject>

- (void)customCellDidSelectOn:(CustomCell *)cell;

- (void)customCellDidSelectOff:(CustomCell *)cell;

@end

@interface CustomCell : UITableViewCell

@property (nonatomic, weak)id<CustomCellDeletage> delegate;

- (void)selectOn;

- (void)selectOff;

@end

@interface CustomCell ()

@property (nonatomic, strong)UIButton * checkBtn;

@end

@implementation CustomCell

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier{
    self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
    if (self) {
        self.selectionStyle = UITableViewCellSelectionStyleNone;
        _checkBtn = [UIButton buttonWithType:UIButtonTypeCustom];
        [_checkBtn addTarget:self action:@selector(checkBtnPressed:) forControlEvents:UIControlEventTouchUpInside];
         //...省略部分代碼
    }
    return self;
}
- (void)checkBtnPressed:(id)sender {
    if (_checkBtn.selected) {
        [self selectOff];
        if (self.delegate && [self.delegate respondsToSelector:@selector(customCellDidSelectOff:)]) {
        [self.delegate customCellDidSelectOff:self];
    }
    } else {
        [self selectOn];
        if (self.delegate && [self.delegate respondsToSelector:@selector(customCellDidSelectOn:)]) {
        [self.delegate customCellDidSelectOn:self];
    }
    }
}
- (void)selectOn {
    _checkBtn.selected = YES;
}
- (void)selectOff {
    _checkBtn.selected = NO;
}
@end

複製代碼

外部Controller調用:對象

if ([self isChecked:orderDict[@"orderId"]]) {
        [cell selectOn];
    } else {
        [cell selectOff];
    }
複製代碼

外部Controller回調:接口

#pragma mark <CustomCellDelegate>
- (void)customCellDidSelectOn:(CustomCell *)cell {
    
    [self.checkArray addObject:cell.orderDict];
    [self updateViewInfo];

}

- (void)customCellDidSelectOff:(CustomCell *)cell {
    
    [self.checkArray removeObject:cell.orderDict];
    [self updateViewInfo];
}
複製代碼

改動點:

  1. 將按鈕做爲類的私有屬性
  2. 將改變按鈕對象的selected狀態包裝起來,提供更高層的抽象selectOn和selectOff接口,抽象成更像現實世界同樣操做實體,選中單元格和取消選中單元格,而不是直接接觸底層細節,將按鈕對象的selected屬性設置成YES或NO。
  3. 調用方調用更高層次、更具說明意義的抽象接口selectOn和selectOff方法,而無須關心實現細節。cell內部也可經過此方法改變外觀,之後關於外觀的改動也無須在多個地方改動了。
  4. 回調方法職責更單一,原來在一個方法中處理選中和非選中,如今分開到兩個方法中,幹掉了一個if,也就下降了函數的複雜度,之後添加選中或取消選中後可能會處理其它事情將更容易維護。
  5. 若之後將按鈕對象更換爲其它類型對象,或是更復雜的自定義按鈕對象,那隻需改動封裝內的內部實現,不影響程序的其它地方的使用。

改進後的代碼:

  • 隱藏了實現細節,提升了類的封裝性
  • 類的接口和類保持一致的抽象層次
  • 類接口更高層次的抽象
  • 單一職責

有同窗可能以爲這樣的改動有那麼必要嗎,弄的如此大張旗鼓,但咱們應該時刻保持代碼的乾淨整潔,若是每一個類都不能時刻保持對外接口的高度一致抽象層次,那麼多個迭代之後,這樣的類將愈來愈難以理解和難以維護,類持續的龐大和存在各類互不相關的數據和邏輯,增長新功能時更容易牽扯老功能的bug,咱們應該時刻記住

勿以惡小而爲之,勿以善小而不爲

相關文章
相關標籤/搜索