Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
MVPのようなものをチームに提案した話
Search
amacou
March 02, 2016
Programming
3
2.5k
MVPのようなものをチームに提案した話
チームに開発効率化のためにMVPっぽいものを提案した話
amacou
March 02, 2016
Tweet
Share
More Decks by amacou
See All by amacou
LGTMについて
amacou
6
110k
Other Decks in Programming
See All in Programming
XSLTで作るBrainfuck処理系
makki_d
0
210
たった 1 枚の PHP ファイルで実装する MCP サーバ / MCP Server with Vanilla PHP
okashoi
1
150
SODA - FACT BOOK
sodainc
1
1.1k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
42
28k
LINEヤフー データグループ紹介
lycorp_recruit_jp
0
770
Composerが「依存解決」のためにどんな工夫をしているか #phpcon
o0h
PRO
1
130
Beyond Portability: Live Migration for Evolving WebAssembly Workloads
chikuwait
0
390
GoのGenericsによるslice操作との付き合い方
syumai
2
680
Is Xcode slowly dying out in 2025?
uetyo
1
180
Create a website using Spatial Web
akkeylab
0
290
Kotlin エンジニアへ送る:Swift 案件に参加させられる日に備えて~似てるけど色々違う Swift の仕様 / from Kotlin to Swift
lovee
1
250
生成AIで日々のエラー調査を進めたい
yuyaabo
0
630
Featured
See All Featured
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.5k
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
181
53k
Rails Girls Zürich Keynote
gr2m
94
14k
Rebuilding a faster, lazier Slack
samanthasiow
81
9k
Automating Front-end Workflow
addyosmani
1370
200k
Code Review Best Practice
trishagee
68
18k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
31
1.2k
A Modern Web Designer's Workflow
chriscoyier
693
190k
It's Worth the Effort
3n
184
28k
A Tale of Four Properties
chriscoyier
160
23k
Thoughts on Productivity
jonyablonski
69
4.7k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
16k
Transcript
MVPͷΑ͏ͳͷΛ νʔϜʹఏҊͨ͠ shibuya.swift #3 2016/03/01 @amacou
ࣗݾհ • ఱ ળ(Amano Yoshihiko) • @amacou • GMOϖύϘ minneࣄۀ෦
• ruby Rails Objective-c swift
͍ͬͯΔ͜ͱ • ೖ͔ࣾͯ͠ΒminneͷαʔόαΠυ(Rails)ΛҰ ؒ • ͦͷޙiOSΞϓϦҰؒObjective-c(ͱͪΐͬ ͱ͚ͩSwift) • جຊతʹදʹग़ͯ͜ͳ͍ͱ͜ΖͷվળΛ୲
ࠓ͢͜ͱ • ෳࡶͳը໘ͷΞϓϦͩͱMVCͰͭΒ͘ͳ͍ Ͱ͔͢ͱ͍͏ • ͦΕͬͯMVP͑࠷ݶͷมߋͰ͋Δఔ ͏·͍͘Μ͡ΌͶ͍ͬͯ͏
ෳࡶͳը໘ͷΞϓϦͰͭΒ͍ ͜ͱ
ෳࡶͳը໘ͱ • ୯७Ͱͳ͍ը໘ • ෳͷίϯϙʔωϯτ͕ஔͯ͋͠Δ • Ұͭͷը໘ʹෳͷTableView CollectionViewΛͬͯͨΓ͢Δ
ෳࡶͳը໘ͷΞϓϦͰͳʹ͕ͭ Β͍͔ • ίʔυϨϏϡʔ͕ͭΒ͍ • ςετ͕ͭΒ͍
ίʔυϨϏϡʔ͕ͭΒ͍
ෳࡶͳը໘ͷϨϏϡʔͷͭΒ͞ • ViewControllerͷߦ͕ଟ͘ͳΓ͕ͪ • ϨϏϡʔ࣌ʹ֬ೝ͠ͳ͚Ε͍͚ͳ͍ൣғ͕͕Δ • मਖ਼ʹ͕͔͔࣌ؒΔ • ࠶ϨϏϡʔ͔࣌ͬ͢ΓΕͯ·ͨ࠷ॳ͔Βݟ͠ •
ͭΒ͍
ςετ͕ͭΒ͍
ςετͷͭΒ͞ • ςετΤϯδχΞͷᅂΈ • ͔͠͠ෳࡶͳViewControllerͷUnitςετͭ Β͍ • UIςετʹཔΓ͕ͪ
ෳࡶͳViewControllerͷUnitς ετͷͭΒ͞ • ViewͱViewControllerབྷΈ߹͍ͬͯΔ͜ͱ͕ଟ͍ • ෳࡶͳ࣮ͱෳࡶͳςετηοτ • ෳࡶͳ࣮Λςετ͢ΔͱΕͳ͘ෳࡶͳςετʹͳΔ • ෳࡶͳςετอकͰ͖ͳ͍
• UIςετʹཔΓ͕ͪʹͳΔ
UIςετͷͭΒ͞ • ςετΛॻ͘ͷʹ͕͔͔࣌ؒΔ • ࣮ߦ͕͔͔࣌ؒ͘͢͝Δ • ࡉ͔͍ذͷςετ͕ͩΔ͘ͳΔ • ςετΛ૿͠ʹ͍͘ •
ςετͷอकʹ͕͔͔࣌ؒΔ • յΕ͍͢ • ͬͺΓUnitςετΛްͨ͘͘͠ͳΔ
ͳͥ͜ΜͳʹͭΒ͍ͷ͔
ͳͥ͜ΜͳʹͭΒ͍ͷ͔ • Unitςετ͕ͭΒ͍ͷɺϨϏϡʔ͕ͭΒ͍ͷෳ ࡶͳViewControllerݪҼ • ViewControllerʹɺؔ৺ࣄ͕ूத͍͗ͯ͢͠Δ • γϯϓϧʹ͢Δʹɺؔ৺ࣄͰׂ͢Ε͍͍ • =>
MVPͬΆ͘͢Ε͍͍͡ΌΜͯͳͬͨ
MVPͬΆ͘͢Δͱ
MVPͱ • Model-View-Presenterͷུ • Observer SynchronizationํࣜͱPassive View ํࣜͷ2ͭํ͕ࣜ͋Δ
MVP (Observer Synchronization) • Model • MVCͷModelͱಉ͡ • View •
UIΛ୲͠ɺModelΛObserverͰࢹͯ͠ɺModelͷมߋΛ Viewʹөͤ͞Δ • Presenter • View͔ΒͷΠϕϯτΛड͚औͬͯɺModelΛߋ৽͢Δ
MVP(Passive View) • Model • MVCͷModelͱಉ͡ • View • MVCͷView
• ModelΛ͞ΘΒͳ͍ • ViewΛߋ৽͢ΔͨΊͷInterfeceΛ࣋ͭ • Presenter • View͔ΒͷΠϕϯτΛड͚औͬͯɺModelΛߋ৽͢Δ • ඞཁʹԠͯ͡ViewΛInterfaceΛ௨ͯ͡ߋ৽͢Δ
ࠓճѻ͏MVP • ࠓճѻ͏MVPPassive Viewํࣜ • ैདྷͷMVCͱͷੑ͕ߴ͍ͨΊ • ͳΔ͘ViewΛബ͓͖͍ͯͨͨ͘͠Ί • =>
ࠓճMVPͱݴͬͨΒPassive Viewํ ࣜͩͱࢥ͍ͬͯͩ͘͞
MVCͱMVPͷҧ͍
MVC 7JFX View ViewController Model
MVP View/VC Presenter 7JFX Model
͋Μ·ΓҧΘͳ͍?
͋Μ·ΓҧΘͳ͍ʂ • جຊతͳҧ͍2ͭͷখ͞ͳଋ͚ͩʂ • View͔ΒModelΛ৮Βͳ͍ • View <=> PresenterϝιουΛ༻ҙͯ͠ ૢ࡞͢Δ
͡Ό͋རʁ
MVC View ViewController Model
MVP 7JFX 7JFX 7JFX$POUSPMMFS 7JFX View/VC Presenter Model
େ͖ͳར • MVPখ͞ͳView୯ҐͰPresenterΛׂΓͯΒΕΔ • ؔ৺ࣄΛখ͘͞Ͱ͖Δ = ίʔυΛίϯύΫτ/γϯϓϧʹͰ͖Δ • ςετγϯϓϧʹͰ͖Δ •
=> Unitςετ͍͢͠ʂ • Presenter͍·Θ͍͢͠ʂʂ • ॏෳ࣮ͨ͠͠ͳͯ͘ࡁΉ
ͰͦΕࠓྲྀߦΓͷΠέͯ ΔΞʔΩςΫνϟͰಉͩ͡ ΑͶʁ
ͳͥଞͷΞʔΩςΫνϟͰ ͳ͘MVPͳͷ͔
ଞͷΞʔΩςΫνϟ • MVVM(Model View ViewModel)ͱ͔DDD(Domain-Driven Design)ͱ͔ • ొਓ͕ଟ͍(Domain Model/Usecase/Input Port/
Output Port/Interface Adapter/Controller/Presenter/ Gateway) • νʔϜશһʹจԽΛਁಁͤ͞Δͷ݁ߏେม • DataBindingศར͚ͩͲɺࠓղܾ͍ͨ͜͠ͱ͡Όͳ͍
MVPͷར • MVC͔ΒͷୈҰาʹ࠷ద • ͦͷޙMVVMDDDͳͲͷൃలͰ͖Δ • ΏΔ;ΘͰ͋Γͳ͕Βͦͦ͜͜γϯϓϧʹͳ Γͦ͏
࣮ํ๏
͍͍ͩͨ͜Μͳը໘
None
AMNTopViewController.m #import "AMNTopViewController.h" @interface AMNTopViewController () @end @implementation AMNTopViewController -
(void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; } @end
AMNTimelineTableViewPresenter.m @interface AMNTimelineTableViewPresenter() @property (nonatomic) AMNTimeline *timeline; @end @implementation AMNTimelineTableViewPresenter
- (instancetype)init { if (self = [super init]) { self.timeline = [AMNTimeline new]; } return self; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.timeline.posts.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { id post = self.timeline.posts[indexPath.row]; NSString *identifier = [NSString stringWithFormat:@"%@Cell", NSStringFromClass([post class])]; id cell = [tableView dequeueReusableCellWithIdentifier:identifier]; if ([cell respondsToSelector:@selector(presenter)]) { NSObject<AMNPresenterProtocol> *presenter = [cell presenter]; [presenter presentWithModel:post viewController:self.viewController]; } return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { return 160.f; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { id post = self.timeline.posts[indexPath.row]; if ([post isKindOfClass:[AMNPhoto class]]) { AMNImageViewController *imageViewController = [[self.viewController storyboard] instantiateViewControllerWithIdentifier: NSStringFromClass([AMNImageViewController class])]; imageViewController.presenter.photo = post; [[self.viewController navigationController] pushViewController:imageViewController animated:YES]; } else if ([post isKindOfClass:[AMNText class]]) { AMNTextViewController *textViewController = [[self.viewController storyboard] instantiateViewControllerWithIdentifier: NSStringFromClass([AMNTextViewController class])]; textViewController.presenter.text = post; [[self.viewController navigationController] pushViewController:textViewController animated:YES]; } [tableView deselectRowAtIndexPath:indexPath animated:YES]; } @end
None
AMNPhotoTableViewCell/ AMNPhotoPresenter @interface AMNPhotoTableViewCell() @property (nonatomic) IBOutlet UIImageView *photoImageView; @end
@implementation AMNPhotoTableViewCell - (void)awakeFromNib { self.presenter = [[AMNPhotoPresenter alloc] initWithView:self]; } - (void)setImage:(UIImage *)image { self.photoImageView.image = image; } @end @implementation AMNPhotoPresenter - (instancetype)initWithView: (UIView<AMNPhotoPresenterProtocol> *)view { if (self = [self init]) { self.view = view; } return self; } - (void)presentWithModel:(AMNPhoto *)model viewController:(UIViewController *)viewController { self.photo = model; self.viewController = viewController; [self reloadData]; } - (void)reloadData { [self.view setImage:self.photo.image]; } @end
None
AMNTextTableViewCell/ AMNTextPresenter @interface AMNTextTableViewCell() @property (nonatomic) IBOutlet UILabel *label; @end
@implementation AMNTextTableViewCell - (void)awakeFromNib { self.presenter = [[AMNTextPresenter alloc] initWithView:self]; } - (void)setText:(NSString *)text { self.label.text = text; } @end @implementation AMNTextPresenter - (instancetype)initWithView: (UIView<AMNTextPresenterProtocol> *)view { if (self = [self init]) { self.view = view; } return self; } - (void)presentWithModel:(AMNText *)model viewController:(UIViewController *)viewController { self.text = model; self.viewController = viewController; [self reloadData]; } - (void)reloadData { [self.view setText:self.text.text]; } @end
None
AMNTextTableViewCell/ AMNTextPresenter @interface AMNPhotoListTableViewCell() @property (nonatomic) IBOutlet UICollectionView *collectionView; @end
@implementation AMNPhotoListTableViewCell - (void)awakeFromNib { self.presenter = [[AMNPhotoListCollectionViewPresenter alloc] initWithView:self.collectionView]; } @implementation AMNPhotoListCollectionViewPresenter - (instancetype)initWithView:(UICollectionView *)view { if (self = [super init]) { self.view = view; self.view.delegate = self; self.view.dataSource = self; } return self; } - (void)presentWithModel:(AMNPhotoList *)model viewController:(UIViewController *)viewController { self.photoList = model; self.viewController = viewController; [self reloadData]; } - (void)reloadData { [self.view reloadData]; } - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { return self.photoList.list.count; } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { AMNPhotoCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"AMNPhotoCollectionViewCell" forIndexPath:indexPath]; cell.presenter.photo = self.photoList.list[indexPath.row]; [cell.presenter reloadData]; return cell; } - (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { return CGSizeMake(CGRectGetHeight(self.view.bounds), CGRectGetHeight(self.view.bounds)); } - (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath { id post = self.photoList.list[indexPath.row]; if ([post isKindOfClass:[AMNPhoto class]]) { AMNImageViewController *imageViewController = [[self.viewController storyboard] instantiateViewControllerWithIdentifier: NSStringFromClass([AMNImageViewController class])]; imageViewController.presenter.photo = post; [[self.viewController navigationController] pushViewController:imageViewController animated:YES]; } [collectionView deselectItemAtIndexPath:indexPath animated:YES]; } @end
None
AMNPhotoCollectionViewCell @interface AMNPhotoCollectionViewCell() @property (nonatomic, weak) IBOutlet UIImageView *imageView; @end
@implementation AMNPhotoCollectionViewCell - (void)awakeFromNib { self.presenter = [[AMNPhotoPresenter alloc] initWithView:self]; } - (void)setImage:(UIImage *)image { self.imageView.image = image; } @end
࣮ํ๏ • ଓ͖GithubͰ • https://github.com/amacou/MVPExample
·ͱΊ • ͠ɺViewController͕ෳࡶʹͳͬͯେมʹ ͳ͖ͬͯͨΒׂͯ͠ΈΔͱΘΓͱ͍͍͔ • ͦͷͱ͖࠷ݶͷมߋͰՄೳͳΞʔΩςΫνϟ ͱͯ͠MVP͕͋ΔΑ
͋Γ͕ͱ͏͍͟͝·ͨ͠