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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
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
クライアントワークでSREをするということ。あるいは事業会社におけるSREと同じこと・違うこと
nnaka2992
1
340
Fundamentals of Software Engineering In the Age of AI
therealdanvega
1
250
Cyrius ーLinux非依存にコンテナをネイティブ実行する専用OSー
n4mlz
0
150
nilとは何か 〜interfaceの構造とnil!=nilから理解する〜
kuro_kurorrr
3
1.9k
AWS×クラウドネイティブソフトウェア設計 / AWS x Cloud-Native Software Design
nrslib
16
3.1k
野球解説AI Agentを開発してみた - 2026/02/27 LayerX社内LT会資料
shinyorke
PRO
0
290
DevinとClaude Code、SREの現場で使い倒してみた件
karia
1
1.1k
CDIの誤解しがちな仕様とその対処TIPS
futokiyo
0
210
Swift ConcurrencyでよりSwiftyに
yuukiw00w
0
270
CSC307 Lecture 14
javiergs
PRO
0
470
Go Conference mini in Sendai 2026 : Goに新機能を提案し実装されるまでのフロー徹底解説
yamatoya
0
590
AI 開発合宿を通して得た学び
niftycorp
PRO
0
120
Featured
See All Featured
The Limits of Empathy - UXLibs8
cassininazir
1
260
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.4k
GraphQLの誤解/rethinking-graphql
sonatard
75
11k
Deep Space Network (abreviated)
tonyrice
0
90
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
118
110k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
240
Making Projects Easy
brettharned
120
6.6k
First, design no harm
axbom
PRO
2
1.1k
[SF Ruby Conf 2025] Rails X
palkan
2
830
Code Reviewing Like a Champion
maltzj
528
40k
WCS-LA-2024
lcolladotor
0
480
SEO for Brand Visibility & Recognition
aleyda
0
4.4k
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͕͋ΔΑ
͋Γ͕ͱ͏͍͟͝·ͨ͠