Your SlideShare is downloading. ×
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
アップルのテンプレートは有害と考えられる
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

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

13,663

Published on

0 Comments
9 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
13,663
On Slideshare
0
From Embeds
0
Number of Embeds
12
Actions
Shares
0
Downloads
9
Comments
0
Likes
9
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. テンプレートは有害と考えられる Appleのテンプレートと責任単一原則 Brian Gesiak 2014年3月28日 リクルート株式会社 研究生、東京大学 @modocache
  • 2. 内容 • 単一責任原則 • The single responsibility principle、またはSRP • UITableViewControllerのファイル・テンプレート • モデルとコントローラの役割を両方果たしている • 責任を分担させる一例 • GitHubViewer.appのview controllerからUITableViewDataStoreの コードを取り出す • Appleのファイルやプロジェクト・テンプレートについて • その多くは単一責任原則(SRP)に違反する • 実装の一例であり、プロダクション・コードで真似すべきでは ない
  • 3. 単一責任原則 クラスを変更する理由は一つ以上存 在してはならない - Robert C. Martin Single Responsibility Principle
  • 4. Robert C. Martin (Uncle Bob)
  • 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 { /* ... */ } テンプレートの内容
  • 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 { /* ... */ } テンプレートの内容
  • 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 { /* ... */ } テンプレートの内容
  • 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 { /* ... */ } テンプレートの内容
  • 9. UITableViewDelegate UITableViewDataSource UITableViewController 責任(変更すべき理由) 1. テーブル内で表示されるデータを決定する ! ! 2. ユーザがテーブルをタップするときの動作などを 決定する
  • 10. UITableViewDelegate UITableViewDataSource UITableViewController 責任(変更すべき理由) 1. テーブル内で表示されるデータを決定する ! ! 2. ユーザがテーブルをタップするときの動作などを 決定する
  • 11. UITableViewDelegate UITableViewDataSource UITableViewController 責任(変更すべき理由) 1. テーブル内で表示されるデータを決定する ! ! 2. ユーザがテーブルをタップするときの動作などを 決定する
  • 12. UITableViewDelegate UITableViewDataSource UITableViewController 責任(変更すべき理由) 1. テーブル内で表示されるデータを決定する ! ! 2. ユーザがテーブルをタップするときの動作などを 決定する
  • 13. GitHubViewer GitHubのアイコンは@peterhajasによ る著作物であり、Creative Commons License version 3.0のもとで公開されて います。
  • 14. GitHubViewer GitHubのアイコンは@peterhajasによ る著作物であり、Creative Commons License version 3.0のもとで公開されて います。
  • 15. GHVReposViewController - (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; } View Controllerの責任
  • 16. GHVReposViewController - (void)viewDidLoad { [super viewDidLoad]; ! self.title = NSLocalizedString(@"Repositories", nil); [self getRepositories]; } View Controllerの責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 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の責任
  • 43. GHVReposViewController
  • 44. GHVReposViewController 1. View controllerの責任
  • 45. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る
  • 46. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる
  • 47. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任
  • 48. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任 • レポのデータを持つ
  • 49. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任 • レポのデータを持つ • テーブルビューのsectionの数を決定する
  • 50. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任 • レポのデータを持つ • テーブルビューのsectionの数を決定する • テーブルビューのrowsの数を決定する
  • 51. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任 • レポのデータを持つ • テーブルビューのsectionの数を決定する • テーブルビューのrowsの数を決定する • レポのデータをもとにセルのビュー構成を構築する
  • 52. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任 • レポのデータを持つ • テーブルビューのsectionの数を決定する • テーブルビューのrowsの数を決定する • レポのデータをもとにセルのビュー構成を構築する 3. UITableViewDelegateの責任
  • 53. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任 • レポのデータを持つ • テーブルビューのsectionの数を決定する • テーブルビューのrowsの数を決定する • レポのデータをもとにセルのビュー構成を構築する 3. UITableViewDelegateの責任 • タップしたレポを表示するためのview controllerをプッシュする
  • 54. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任 • レポのデータを持つ • テーブルビューのsectionの数を決定する • テーブルビューのrowsの数を決定する • レポのデータをもとにセルのビュー構成を構築する 3. UITableViewDelegateの責任 • タップしたレポを表示するためのview controllerをプッシュする コードが100行以上にも及ぶ
  • 55. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージを送る • 通信インジケーターを表示させる 2. UITableViewDataSourceの責任 • レポのデータを持つ • テーブルビューのsectionの数を決定する • テーブルビューのrowsの数を決定する • レポのデータをもとにセルのビュー構成を構築する 3. UITableViewDelegateの責任 • タップしたレポを表示するためのview controllerをプッシュする コードが100行以上にも及ぶ
  • 56. GHVReposViewController 肥大化したview controllerの図
  • 57. GHVReposViewController 責任分担
  • 58. GHVReposViewController 責任分担
  • 59. GHVReposViewController 責任分担
  • 60. GHVRepoStore UITableViewDataStoreの責任のみを果たす - (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:/* ... */]; }
  • 61. GHVRepoStore UITableViewDataStoreの責任のみを果たす - (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:/* ... */]; }
  • 62. GHVRepoStore UITableViewDataStoreの責任のみを果たす - (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:/* ... */]; }
  • 63. GHVRepoStore UITableViewDataStoreの責任のみを果たす - (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:/* ... */]; }
  • 64. GHVRepoStore UITableViewDataStoreの責任のみを果たす - (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:/* ... */]; }
  • 65. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; ! ! ! ! ! ! ! ! }
  • 66. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; ! ! ! ! ! ! ! ! }
  • 67. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; ! ! ! ! ! ! ! ! }
  • 68. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; ! ! ! ! ! ! ! ! }
  • 69. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; ! ! ! ! ! ! ! ! }
  • 70. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; ! ! ! ! ! ! ! ! }
  • 71. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 72. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 73. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 74. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 75. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 76. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 77. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 78. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 79. GHVReposViewController GHVRepoStoreへの責任分担 - (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]; }];
  • 80. GHVReposViewController
  • 81. GHVReposViewController 1. View controllerの責任
  • 82. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージする
  • 83. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージする • 通信インジケーターを表示させる
  • 84. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージする • 通信インジケーターを表示させる 2. UITableViewDelegateの責任
  • 85. GHVReposViewController 1. View controllerの責任 • レポを取得させるようにメッセージする • 通信インジケーターを表示させる 2. UITableViewDelegateの責任 • タップしたレポを表示するためのview controllerを プッシュする
  • 86. GHVReposViewController コードは100行にも満たない 1. View controllerの責任 • レポを取得させるようにメッセージする • 通信インジケーターを表示させる 2. UITableViewDelegateの責任 • タップしたレポを表示するためのview controllerを プッシュする
  • 87. 責任単一原則 メリット
  • 88. 責任単一原則 • 実装の変更があった場合は小さなクラスの中で収ま るのでいろんなところが壊れる可能性も低くなる メリット
  • 89. 責任単一原則 • 実装の変更があった場合は小さなクラスの中で収ま るのでいろんなところが壊れる可能性も低くなる • 責任が明確なクラスで構成されているのでコードが 読みやすくて、デバッグもしやすい メリット
  • 90. Appleのテンプレートの問題点 SRPに違反している例
  • 91. Appleのテンプレートの問題点 SRPに違反している例 • Master-Detail Application (with Core Data) • AppDelegate • UIWindowのroot view controllerの初期化 • Core Data関連のセットアップ、エラーハンドリング • 100行以上
  • 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行以上
  • 93. クリーンなテンプレート 実装がそれほどひどくない例
  • 94. クリーンなテンプレート 実装がそれほどひどくない例 • Page-Based Application • UIPageViewDelegateとUIPageViewDataSourceの責任 分担をしている • それぞれのクラスも責任がはっきりしていて簡潔
  • 95. クリーンなテンプレート 実装がそれほどひどくない例 • Page-Based Application • UIPageViewDelegateとUIPageViewDataSourceの責任 分担をしている • それぞれのクラスも責任がはっきりしていて簡潔 • SpriteKit Game • それぞれのクラスが小さく、責任分担ができてい る
  • 96. 要約 • 単一責任原則 • クラスを変更する理由は一つ以上存在してはならな い • Appleのテンプレートは真似すべきものではなく、実装 の一例であるにすぎないと考えたほうがいい • Appleのコードだからといってよく書かれているコード だというわけではない • 何がよく書かれているコードで、何がそうでないの かは自分で判断する必要がある
  • 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

×