O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.
Using Protocol to
Refactor
邱志強, Green Chiu, iOS Developer.
在 iOS ,提到 Protocol
你會想到什什麼?
繼承 ?
Delegation ?
其他 ?
Delegation Pattern
• Apple ⼤大量量使⽤用在 CocoaTouch SDK

• UITableView, UICollectionView, UIGestureRecognizer
and so on.

• NSU...
NS-Protocols
• 為了了達到特定⽬目的 archive, copy, enumerate

• NSCopying

• NSCoding

• NSFastEnumeration
繼承
• Objective-C/Swift 不⽀支援多重繼承,但可以實作多個
Protocols

• 我們很習慣使⽤用繼承,⼤大部份了了只是為了了部分的 method
或實作

• 可怕的繼承樹 

• 在調整後常出現 ”驚喜”
Protocol in Objective-C
@protocol SampleProtocol <NSObject>
- (void)sampleMethodA;
- (void)sampleMethodB;
@optional
- (voi...
Protocol 讓⼀一個 class 或
method 知道如何操作物件
The End
重構
• 類似/同樣的程式碼重複出現
Class LocalPlaylistInfo
- (void)fetchPlaylistCoverImageWithSize …
{
if (…) {
…
UIImage *image = nil;
DBMetaReference *ref ...
Class SongInfoViewModel
- (void)loadSongInfo:(LocalSongInfo *)inSongInfo {
if (inSongInfo.type == LocalDBContextSongSource...
Issues
• 相似的實作出現在多個地⽅方

• 為了了圖片,View or Model 載入了了很多 classes/framework
Design Protocol
typedef NS_ENUM(NSInteger, ProvideImageWay) {
ProvideImageWayNone = NSNotFound,
ProvideImageWayFetchWithUR...
After implemented
- (void)loadSongInfo:(LocalSongInfo *)inSongInfo
{
switch ([inSongInfo getCoverImageWay]) {
case UPProvi...
Optimized
// UIImageView+LocalItemImageProvider.m
- (void)loadImageWithImageProvider:(id<LocalItemImageProvider>)inImagePr...
Finally
- (void)loadSongInfo:(UPLocalSongInfo *)inSongInfo
{
[self.albumCoverImageView loadImageWithImageProvider:inSongIn...
Besides
• 使⽤用 Protocol 讓程式更更容易易被測試

• Mock 物件變得容易易
Testing
// UIImageView+LocalItemImageProvider.m
- (void)loadImageWithImageProvider:(id<LocalItemImageProvider>)inImageProv...
Testing
@interface TCDummyLocalImageProvider: NSObject <LocalItemImageProvider>
- (instancetype)initWithType:(ProvideImage...
Testing
- (void)testUIImageLoadImageWithImageProvider
{
UIImageView *imageView = [[UIImageView alloc] init];
[imageView lo...
This is
Protocol-Oriented Programming
One more thing…
We are hiring
iOS Developer and others
Thanks
Using Protocol to Refactor
Próximos SlideShares
Carregando em…5
×

Using Protocol to Refactor

196 visualizações

Publicada em

使用 Protocol 來重構 (ObjC) @ CocoaHeads Taipei 2017.11.09

Publicada em: Engenharia
  • Seja o primeiro a comentar

Using Protocol to Refactor

  1. 1. Using Protocol to Refactor 邱志強, Green Chiu, iOS Developer.
  2. 2. 在 iOS ,提到 Protocol 你會想到什什麼?
  3. 3. 繼承 ?
  4. 4. Delegation ?
  5. 5. 其他 ?
  6. 6. Delegation Pattern • Apple ⼤大量量使⽤用在 CocoaTouch SDK • UITableView, UICollectionView, UIGestureRecognizer and so on. • NSURLSession, StoreKit • 第三⽅方套件
  7. 7. NS-Protocols • 為了了達到特定⽬目的 archive, copy, enumerate • NSCopying • NSCoding • NSFastEnumeration
  8. 8. 繼承 • Objective-C/Swift 不⽀支援多重繼承,但可以實作多個 Protocols • 我們很習慣使⽤用繼承,⼤大部份了了只是為了了部分的 method 或實作 • 可怕的繼承樹 • 在調整後常出現 ”驚喜”
  9. 9. Protocol in Objective-C @protocol SampleProtocol <NSObject> - (void)sampleMethodA; - (void)sampleMethodB; @optional - (void)sampleOptionalMethod; @end
  10. 10. Protocol 讓⼀一個 class 或 method 知道如何操作物件
  11. 11. The End
  12. 12. 重構 • 類似/同樣的程式碼重複出現
  13. 13. Class LocalPlaylistInfo - (void)fetchPlaylistCoverImageWithSize … { if (…) { … UIImage *image = nil; DBMetaReference *ref = … ; if (ref.sourceType == …) { DBMetaItem *item = …; … } else if (ref.sourceType == …) { MPMediaItem *item = …; image = [item.artwork imageWith …]; } else if (ref.sourceType == LocalDBContextSongSourceTypeStore) { DBMetaItem *item = …; if (!item) { return; } NSString *imageFileURLString = …; void (^imageCallback)(NSString *fileURLString, UIImage *image) = ^(NSString *fileURLString, UIImage *image) { if (image …) { } else if ([NSURL URLWithString:item.photoURL]) { [[KKRadioImageManager sharedImageManager] fetchImageWithURL:… requester:nil callback:^(UIImage *receiveImage, NSError *error) { if (receiveImage) { … } }]; } }; …
  14. 14. Class SongInfoViewModel - (void)loadSongInfo:(LocalSongInfo *)inSongInfo { if (inSongInfo.type == LocalDBContextSongSourceTypeStore || ...) { DBMetaItem *item = inSongInfo.rawItem; if (inSongInfo.type == LocalDBContextSongSourceTypeStore) { } else { … } self.imageFileURLString = …; void (^imageCallback)(NSString *fileURLString, UIImage *image) = ^void(NSString *fileURLString, UIImage *image){ if (image) { … weakSelf.albumCoverImage = cropImage; } else if ([NSURL URLWithString:item.photoURL]) { [[KKRadioImageManager sharedImageManager] fetchImageWithURL:… requester:nil callback:^(UIImage *receiveImage, NSError *error) { if (receiveImage) { weakSelf.albumCoverImage = … } … }]; } … }; … return; } if (inSongInfo.image) { … } …
  15. 15. Issues • 相似的實作出現在多個地⽅方 • 為了了圖片,View or Model 載入了了很多 classes/framework
  16. 16. Design Protocol typedef NS_ENUM(NSInteger, ProvideImageWay) { ProvideImageWayNone = NSNotFound, ProvideImageWayFetchWithURLString = 0, ProvideImageWayGetWithSize, ProvideImageWayGenerateWithCallback }; @protocol LocalItemImageProvider <NSObject> - (ProvideImageWay)getCoverImageWay; - (NSString *)coverURLString; - (UIImage *)coverImageWithSize:(CGSize)inSize; - (void)generateImageWithCallback:(void(^)(UIImage *))inCallback; @end
  17. 17. After implemented - (void)loadSongInfo:(LocalSongInfo *)inSongInfo { switch ([inSongInfo getCoverImageWay]) { case UPProvideImageWayGetWithSize: self.albumCoverImageView.image = [inSongInfo coverImageWithSize:CGSizeMake(…)]; break; case UPProvideImageWayGenerateWithCallback: { __weak typeof(self) weakSelf = self; [inSongInfo generateImageWithCallback:^(UIImage *image) { weakSelf.albumCoverImageView.image = image; }]; break; } case UPProvideImageWayFetchWithURLString: … break; case UPProvideImageWayNone: … break; } … }
  18. 18. Optimized // UIImageView+LocalItemImageProvider.m - (void)loadImageWithImageProvider:(id<LocalItemImageProvider>)inImageProvider { if (![inImageProvider conformsToProtocol:@protocol(LocalItemImageProvider)]) { return; } switch ([inImageProvider getCoverImageWay]) { case ProvideImageWayGetWithSize: self.image = [inImageProvider coverImageWithSize:CGSizeMake(44, 44)]; break; case ProvideImageWayGenerateWithCallback: { __weak typeof(self) weakSelf = self; [inImageProvider generateImageWithCallback:^(UIImage *image) { weakSelf.image = image; }]; break; } case ProvideImageWayFetchWithURLString: [self fetchImageWithURLString:[inImageProvider coverURLString]]; break; case ProvideImageWayNone: default: … break; } }
  19. 19. Finally - (void)loadSongInfo:(UPLocalSongInfo *)inSongInfo { [self.albumCoverImageView loadImageWithImageProvider:inSongInfo]; … }
  20. 20. Besides • 使⽤用 Protocol 讓程式更更容易易被測試 • Mock 物件變得容易易
  21. 21. Testing // UIImageView+LocalItemImageProvider.m - (void)loadImageWithImageProvider:(id<LocalItemImageProvider>)inImageProvider { if (![inImageProvider conformsToProtocol:@protocol(LocalItemImageProvider)]) { return; } switch ([inImageProvider getCoverImageWay]) { case ProvideImageWayGetWithSize: self.image = [inImageProvider coverImageWithSize:CGSizeMake(44, 44)]; break; case ProvideImageWayGenerateWithCallback: { __weak typeof(self) weakSelf = self; [inImageProvider generateImageWithCallback:^(UIImage *image) { weakSelf.image = image; }]; break; } case ProvideImageWayFetchWithURLString: [self fetchImageWithURLString:[inImageProvider coverURLString]]; break; case ProvideImageWayNone: default: … break; } }
  22. 22. Testing @interface TCDummyLocalImageProvider: NSObject <LocalItemImageProvider> - (instancetype)initWithType:(ProvideImageWay)inWay; @end @implementation TCDummyLocalImageProvider { ProvideImageWay way; } - (instancetype)initWithType:(ProvideImageWay)inWay { self = [super init]; if (self) { way = inWay; } return self; } - (ProvideImageWay)getCoverImageWay { return way; } ... @end
  23. 23. Testing - (void)testUIImageLoadImageWithImageProvider { UIImageView *imageView = [[UIImageView alloc] init]; [imageView loadImageWithImageProvider:[NSObject new]]; [imageView loadImageWithImageProvider:[[TCDummyLocalImageProvider alloc] initWithWay:-1000]]; [imageView loadImageWithImageProvider:[[TCDummyLocalImageProvider alloc] initWithWay:ProvideImageWayGetWithSize]]; [imageView loadImageWithImageProvider:[[TCDummyLocalImageProvider alloc] initWithWay:...]]; }
  24. 24. This is Protocol-Oriented Programming
  25. 25. One more thing…
  26. 26. We are hiring iOS Developer and others
  27. 27. Thanks

×