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
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
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
thread_parallel_with_free-threaded_Python_and_NumPy.pdf
riku_sakamoto
0
240
Swift愛好会100回記念 第1回を振り返る
jollyjoester
0
120
スマート反転とウェブアクセシビリティ
camiha
0
170
変化を抱擁するドキュメントの作り方 - ビジネスルール駆動開発がもたらす、コードとの新しい関係
ioki
2
130
Webの地図
yosuke_furukawa
PRO
3
3.6k
From 6 People Classroom Meetup to 100 People Regional Conference / FOSS4G Hiroshima 2026
furukawayasuto
0
130
Snowflakeで業務アプリを作ろう。 Snowflakeのアプリ機能解説&実践ガイド
ayumu_yamaguchi
1
200
LL言語やWebフレームワークのPostgreSQL対応 〜DBの機能がユーザーに届くまで〜
kentaroutakeda
0
150
Laravelのアプリケーションをどこにデプロイするか #ツナギメオフライン.9
akase244
0
120
【DroidKaigi 2026】「アクセシビリティを利用するとき、 アクセシビリティもまたこちらを利用している」 〜マルウェアによる攻撃と防衛について〜
halunoyo
0
430
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
140
GraphRAGのKnowledge Graphを 直接!見る/View-GraphRAG's-KnowledgeGraph-directly!
tyumugi1113
1
280
Featured
See All Featured
4 Signs Your Business is Dying
shpigford
187
23k
Claude Code どこまでも/ Claude Code Everywhere
nwiizo
67
58k
My Coaching Mixtape
mlcsv
0
310
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
520
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
520
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.8k
Design in an AI World
tapps
1
310
Reflections from 52 weeks, 52 projects
jeffersonlam
356
21k
Navigating Team Friction
lara
192
16k
Build your cross-platform service in a week with App Engine
jlugia
234
19k
HDC tutorial
michielstock
2
860
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͕͋ΔΑ
͋Γ͕ͱ͏͍͟͝·ͨ͠