Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
MVPのようなものをチームに提案した話
Search
amacou
March 02, 2016
Programming
2.5k
3
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
MVPのようなものをチームに提案した話
チームに開発効率化のためにMVPっぽいものを提案した話
amacou
March 02, 2016
More Decks by amacou
See All by amacou
LGTMについて
amacou
6
110k
Other Decks in Programming
See All in Programming
市販E-Readerを乗っ取れ 〜Embedded Swiftで電子ペーパーガジェットを制御する〜
trickart
0
230
MVNOの申込からeSIM開通までをiOSアプリでつなぐ- 本人確認・MNP・通信事業者基盤をまたぐ実装
satotakeshi
0
520
Workers Cache を知る
syumai
0
320
GitHubハンズオン講座 — 実務レベルのチーム開発のフローを身につけよう
junhat6
0
120
Heart of Swift Concurrency
koher
0
1.1k
Security issues being discussed on Web Platforms
petamoriken
0
1.4k
AgentCore CLI で進化した AWS での AI エージェントの作り方 : 必要な機能を必要な時に
icoxfog417
PRO
4
410
モデルのリファクタリングが難しいと思ったら、そもそも複雑だったのはビジネス仕様だった ? / is-the-business-domain-the-real-complexity
hatsu38
0
430
JRuby: Past, Present, and Future
headius
0
220
The Rails Doctrine Decade
koic
2
460
Verilogで学ぶCPU自作入門.pdf
uyuki234
7
3.9k
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
320
Featured
See All Featured
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
46k
GraphQLの誤解/rethinking-graphql
sonatard
75
12k
Designing Powerful Visuals for Engaging Learning
tmiket
1
590
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
550
We Are The Robots
honzajavorek
0
380
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
710
Rails Girls Zürich Keynote
gr2m
96
14k
End of SEO as We Know It (SMX Advanced Version)
ipullrank
3
4.4k
Why You Should Never Use an ORM
jnunemaker
PRO
61
10k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
570
Rebuilding a faster, lazier Slack
samanthasiow
85
9.7k
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͕͋ΔΑ
͋Γ͕ͱ͏͍͟͝·ͨ͠