Top Banner
テンプレートは有害と考えられる Appleのテンプレートと責任単一原則 Brian Gesiak 2014328日 リクルート株式会社 研究生、東京大学 @modocache
97

アップルのテンプレートは有害と考えられる

Jan 15, 2015

Download

Documents

Brian Gesiak

 
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: アップルのテンプレートは有害と考えられる

テンプレートは有害と考えられるAppleのテンプレートと責任単一原則

Brian Gesiak

2014年3月28日 リクルート株式会社

研究生、東京大学 @modocache

Page 2: アップルのテンプレートは有害と考えられる

内容•単一責任原則

• The single responsibility principle、またはSRP

• UITableViewControllerのファイル・テンプレート •モデルとコントローラの役割を両方果たしている

•責任を分担させる一例 • GitHubViewer.appのview controllerからUITableViewDataStoreのコードを取り出す

• Appleのファイルやプロジェクト・テンプレートについて

•その多くは単一責任原則(SRP)に違反する •実装の一例であり、プロダクション・コードで真似すべきではない

Page 3: アップルのテンプレートは有害と考えられる

単一責任原則

クラスを変更する理由は一つ以上存在してはならない

- Robert C. Martin

Single Responsibility Principle

Page 4: アップルのテンプレートは有害と考えられる

Robert C. Martin (Uncle Bob)

Page 5: アップルのテンプレートは有害と考えられる

UITableViewController

#pragma mark - UIViewController - (id)initWithStyle:(UITableViewStyle)style { /* ... */ } - (void)viewDidLoad { /* ... */ } - (void)didReceiveMemoryWarning { /* ... */ } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { /* ... */ } !#pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView: (UITableView *)tableView { /* ... */ } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { /* ... */ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ } !#pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }

テンプレートの内容

Page 6: アップルのテンプレートは有害と考えられる

UITableViewController

#pragma mark - UIViewController - (id)initWithStyle:(UITableViewStyle)style { /* ... */ } - (void)viewDidLoad { /* ... */ } - (void)didReceiveMemoryWarning { /* ... */ } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { /* ... */ } !#pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView: (UITableView *)tableView { /* ... */ } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { /* ... */ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ } !#pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }

テンプレートの内容

Page 7: アップルのテンプレートは有害と考えられる

UITableViewController

#pragma mark - UIViewController - (id)initWithStyle:(UITableViewStyle)style { /* ... */ } - (void)viewDidLoad { /* ... */ } - (void)didReceiveMemoryWarning { /* ... */ } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { /* ... */ } !#pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView: (UITableView *)tableView { /* ... */ } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { /* ... */ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ } !#pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }

テンプレートの内容

Page 8: アップルのテンプレートは有害と考えられる

UITableViewController

#pragma mark - UIViewController - (id)initWithStyle:(UITableViewStyle)style { /* ... */ } - (void)viewDidLoad { /* ... */ } - (void)didReceiveMemoryWarning { /* ... */ } - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { /* ... */ } !#pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView: (UITableView *)tableView { /* ... */ } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { /* ... */ } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ } !#pragma mark - UITableViewDelegate - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { /* ... */ }

テンプレートの内容

Page 9: アップルのテンプレートは有害と考えられる

UITableViewDelegate

UITableViewDataSource

UITableViewController責任(変更すべき理由)

1. テーブル内で表示されるデータを決定する !

!

2. ユーザがテーブルをタップするときの動作などを決定する

Page 10: アップルのテンプレートは有害と考えられる

UITableViewDelegate

UITableViewDataSource

UITableViewController責任(変更すべき理由)

1. テーブル内で表示されるデータを決定する !

!

2. ユーザがテーブルをタップするときの動作などを決定する

Page 11: アップルのテンプレートは有害と考えられる

UITableViewDelegate

UITableViewDataSource

UITableViewController責任(変更すべき理由)

1. テーブル内で表示されるデータを決定する !

!

2. ユーザがテーブルをタップするときの動作などを決定する

Page 12: アップルのテンプレートは有害と考えられる

UITableViewDelegate

UITableViewDataSource

UITableViewController責任(変更すべき理由)

1. テーブル内で表示されるデータを決定する !

!

2. ユーザがテーブルをタップするときの動作などを決定する

Page 13: アップルのテンプレートは有害と考えられる

GitHubViewer

GitHubのアイコンは@peterhajasによる著作物であり、Creative Commons

License version 3.0のもとで公開されています。

Page 14: アップルのテンプレートは有害と考えられる

GitHubViewer

GitHubのアイコンは@peterhajasによる著作物であり、Creative Commons

License version 3.0のもとで公開されています。

Page 15: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; }

View Controllerの責任

Page 16: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; }

View Controllerの責任

Page 17: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 18: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 19: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 20: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 21: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 22: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 23: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 24: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 25: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 26: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)getRepositories { [self startNetworkIndicators]; [self.apiClient allRepositoriesForUsername:self.username success:^(NSArray *repositories) { [self stopNetworkIndicators]; self.repositories = repositories; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }]; }

View Controllerの責任

Page 27: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 28: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 29: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 30: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 31: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 32: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 33: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 34: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 35: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kGHVRepoCellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:kGHVRepoCellIdentifier]; } ! GHVRepo *repository = self.repositories[indexPath.row]; cell.textLabel.text = repository.name; cell.detailTextLabel.text = repository.repositoryDescription; return cell; }

UITableViewDataSourceの責任

Page 36: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { GHVRepo *repository = self.repositories[indexPath.row]; GHVRepoViewController *controller = [[GHVRepoViewController alloc] initWithRepository:repository]; [self.navigationController pushViewController:controller animated:YES]; }

UITableViewDelegateの責任

Page 37: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { GHVRepo *repository = self.repositories[indexPath.row]; GHVRepoViewController *controller = [[GHVRepoViewController alloc] initWithRepository:repository]; [self.navigationController pushViewController:controller animated:YES]; }

UITableViewDelegateの責任

Page 38: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { GHVRepo *repository = self.repositories[indexPath.row]; GHVRepoViewController *controller = [[GHVRepoViewController alloc] initWithRepository:repository]; [self.navigationController pushViewController:controller animated:YES]; }

UITableViewDelegateの責任

Page 39: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { GHVRepo *repository = self.repositories[indexPath.row]; GHVRepoViewController *controller = [[GHVRepoViewController alloc] initWithRepository:repository]; [self.navigationController pushViewController:controller animated:YES]; }

UITableViewDelegateの責任

Page 40: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { GHVRepo *repository = self.repositories[indexPath.row]; GHVRepoViewController *controller = [[GHVRepoViewController alloc] initWithRepository:repository]; [self.navigationController pushViewController:controller animated:YES]; }

UITableViewDelegateの責任

Page 41: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { GHVRepo *repository = self.repositories[indexPath.row]; GHVRepoViewController *controller = [[GHVRepoViewController alloc] initWithRepository:repository]; [self.navigationController pushViewController:controller animated:YES]; }

UITableViewDelegateの責任

Page 42: アップルのテンプレートは有害と考えられる

GHVReposViewController

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { GHVRepo *repository = self.repositories[indexPath.row]; GHVRepoViewController *controller = [[GHVRepoViewController alloc] initWithRepository:repository]; [self.navigationController pushViewController:controller animated:YES]; }

UITableViewDelegateの責任

Page 43: アップルのテンプレートは有害と考えられる

GHVReposViewController

Page 44: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任

Page 45: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る

Page 46: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

Page 47: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任

Page 48: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任• レポのデータを持つ

Page 49: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任• レポのデータを持つ• テーブルビューのsectionの数を決定する

Page 50: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任• レポのデータを持つ• テーブルビューのsectionの数を決定する• テーブルビューのrowsの数を決定する

Page 51: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任• レポのデータを持つ• テーブルビューのsectionの数を決定する• テーブルビューのrowsの数を決定する• レポのデータをもとにセルのビュー構成を構築する

Page 52: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任• レポのデータを持つ• テーブルビューのsectionの数を決定する• テーブルビューのrowsの数を決定する• レポのデータをもとにセルのビュー構成を構築する

3. UITableViewDelegateの責任

Page 53: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任• レポのデータを持つ• テーブルビューのsectionの数を決定する• テーブルビューのrowsの数を決定する• レポのデータをもとにセルのビュー構成を構築する

3. UITableViewDelegateの責任• タップしたレポを表示するためのview controllerをプッシュする

Page 54: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任• レポのデータを持つ• テーブルビューのsectionの数を決定する• テーブルビューのrowsの数を決定する• レポのデータをもとにセルのビュー構成を構築する

3. UITableViewDelegateの責任• タップしたレポを表示するためのview controllerをプッシュする

コードが100行以上にも及ぶ

Page 55: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージを送る• 通信インジケーターを表示させる

2. UITableViewDataSourceの責任• レポのデータを持つ• テーブルビューのsectionの数を決定する• テーブルビューのrowsの数を決定する• レポのデータをもとにセルのビュー構成を構築する

3. UITableViewDelegateの責任• タップしたレポを表示するためのview controllerをプッシュする

コードが100行以上にも及ぶ

Page 56: アップルのテンプレートは有害と考えられる

GHVReposViewController肥大化したview controllerの図

Page 57: アップルのテンプレートは有害と考えられる

GHVReposViewController責任分担

Page 58: アップルのテンプレートは有害と考えられる

GHVReposViewController責任分担

Page 59: アップルのテンプレートは有害と考えられる

GHVReposViewController責任分担

Page 60: アップルのテンプレートは有害と考えられる

GHVRepoStoreUITableViewDataStoreの責任のみを果たす

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /// Dequeue, configure, and return a cell } !- (void)reloadRepositories:(GHVRepoStoreSuccessBlock)success failure:(GHVRepoStoreFailureBlock)failure { [self.apiClient allRepositoriesForUsername:self.username success:/* ... */ failure:/* ... */]; }

Page 61: アップルのテンプレートは有害と考えられる

GHVRepoStoreUITableViewDataStoreの責任のみを果たす

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /// Dequeue, configure, and return a cell } !- (void)reloadRepositories:(GHVRepoStoreSuccessBlock)success failure:(GHVRepoStoreFailureBlock)failure { [self.apiClient allRepositoriesForUsername:self.username success:/* ... */ failure:/* ... */]; }

Page 62: アップルのテンプレートは有害と考えられる

GHVRepoStoreUITableViewDataStoreの責任のみを果たす

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /// Dequeue, configure, and return a cell } !- (void)reloadRepositories:(GHVRepoStoreSuccessBlock)success failure:(GHVRepoStoreFailureBlock)failure { [self.apiClient allRepositoriesForUsername:self.username success:/* ... */ failure:/* ... */]; }

Page 63: アップルのテンプレートは有害と考えられる

GHVRepoStoreUITableViewDataStoreの責任のみを果たす

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /// Dequeue, configure, and return a cell } !- (void)reloadRepositories:(GHVRepoStoreSuccessBlock)success failure:(GHVRepoStoreFailureBlock)failure { [self.apiClient allRepositoriesForUsername:self.username success:/* ... */ failure:/* ... */]; }

Page 64: アップルのテンプレートは有害と考えられる

GHVRepoStoreUITableViewDataStoreの責任のみを果たす

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } !- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.repositories count]; } !- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { /// Dequeue, configure, and return a cell } !- (void)reloadRepositories:(GHVRepoStoreSuccessBlock)success failure:(GHVRepoStoreFailureBlock)failure { [self.apiClient allRepositoriesForUsername:self.username success:/* ... */ failure:/* ... */]; }

Page 65: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

Page 66: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

Page 67: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

Page 68: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

Page 69: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

Page 70: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

Page 71: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 72: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 73: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 74: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 75: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 76: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 77: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 78: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 79: アップルのテンプレートは有害と考えられる

GHVReposViewControllerGHVRepoStoreへの責任分担

- (id)initWithRepoStore:(GHVRepoStore *)repoStore { self = [super initWithStyle:UITableViewStylePlain]; if (self) { _repoStore = repoStore; } return self; } !- (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; !!!!!!!!}

self.tableView.dataSource = self.repoStore; [self startNetworkIndicators]; [self.repoStore reloadRepositories:^{ [self stopNetworkIndicators]; [self.tableView reloadData]; } failure:^(NSError *error) { [self stopNetworkIndicators]; [UIAlertView showAlertForError:error]; }];

Page 80: アップルのテンプレートは有害と考えられる

GHVReposViewController

Page 81: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任

Page 82: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージする

Page 83: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージする• 通信インジケーターを表示させる

Page 84: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージする• 通信インジケーターを表示させる

2. UITableViewDelegateの責任

Page 85: アップルのテンプレートは有害と考えられる

GHVReposViewController

1. View controllerの責任• レポを取得させるようにメッセージする• 通信インジケーターを表示させる

2. UITableViewDelegateの責任• タップしたレポを表示するためのview controllerをプッシュする

Page 86: アップルのテンプレートは有害と考えられる

GHVReposViewController

コードは100行にも満たない1. View controllerの責任

• レポを取得させるようにメッセージする• 通信インジケーターを表示させる

2. UITableViewDelegateの責任• タップしたレポを表示するためのview controllerをプッシュする

Page 87: アップルのテンプレートは有害と考えられる

責任単一原則メリット

Page 88: アップルのテンプレートは有害と考えられる

責任単一原則

•実装の変更があった場合は小さなクラスの中で収まるのでいろんなところが壊れる可能性も低くなる

メリット

Page 89: アップルのテンプレートは有害と考えられる

責任単一原則

•実装の変更があった場合は小さなクラスの中で収まるのでいろんなところが壊れる可能性も低くなる

•責任が明確なクラスで構成されているのでコードが読みやすくて、デバッグもしやすい

メリット

Page 90: アップルのテンプレートは有害と考えられる

Appleのテンプレートの問題点SRPに違反している例

Page 91: アップルのテンプレートは有害と考えられる

Appleのテンプレートの問題点SRPに違反している例• Master-Detail Application (with Core Data)

• AppDelegate • UIWindowのroot view controllerの初期化

• Core Data関連のセットアップ、エラーハンドリング

• 100行以上

Page 92: アップルのテンプレートは有害と考えられる

Appleのテンプレートの問題点SRPに違反している例• Master-Detail Application (with Core Data)

• AppDelegate • UIWindowのroot view controllerの初期化

• Core Data関連のセットアップ、エラーハンドリング

• 100行以上• OpenGL Application

•ひとつのView controllerがすべての責任を負っている

• OpenGLのcontextの初期化

• Shadersのコンパイル

• Vertexのデータも持っている

• 400行以上

Page 93: アップルのテンプレートは有害と考えられる

クリーンなテンプレート実装がそれほどひどくない例

Page 94: アップルのテンプレートは有害と考えられる

クリーンなテンプレート実装がそれほどひどくない例• Page-Based Application

• UIPageViewDelegateとUIPageViewDataSourceの責任分担をしている

•それぞれのクラスも責任がはっきりしていて簡潔

Page 95: アップルのテンプレートは有害と考えられる

クリーンなテンプレート実装がそれほどひどくない例• Page-Based Application

• UIPageViewDelegateとUIPageViewDataSourceの責任分担をしている

•それぞれのクラスも責任がはっきりしていて簡潔• SpriteKit Game

•それぞれのクラスが小さく、責任分担ができている

Page 96: アップルのテンプレートは有害と考えられる

要約

•単一責任原則 •クラスを変更する理由は一つ以上存在してはならない

• Appleのテンプレートは真似すべきものではなく、実装の一例であるにすぎないと考えたほうがいい

• Appleのコードだからといってよく書かれているコードだというわけではない

•何がよく書かれているコードで、何がそうでないのかは自分で判断する必要がある

Page 97: アップルのテンプレートは有害と考えられる

最後に•本日のスライド: http://modocache.io/apple-templates-considered-harmful

• Twitter、GitHubでフォローしてください:@modocache •クリーンコード

• Robert C. Martin:@unclebobmartin

• Object-Oriented Design:http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod

• Clean Codeのビデオ:http://cleancoders.com/

• Appleのエンジニアリングの適当さを知るには

• Peter Steinberger:@steipete

• Justin Spahr-Summers:@ jspahrsummers