my perspective on mvp and architecture discussions

49
Most Volatile Palaver Paul Blundell MVP examined holistically and subjectively

Upload: paul-blundell

Post on 13-Apr-2017

491 views

Category:

Software


0 download

TRANSCRIPT

Page 1: My perspective on MVP and architecture discussions

Most Volatile Palaver

Paul BlundellMVP examined holistically and subjectively

Page 2: My perspective on MVP and architecture discussions

My Vision Program- Highlight the variations- Talk about the conflictions- Define the naming- Example implementation- Emphasize important points- Get Pragmatic

Page 3: My perspective on MVP and architecture discussions

image

Oh you know MVP?

Page 4: My perspective on MVP and architecture discussions

Oh you know MVP?http://www.wildcrest.com/Potel/Portfolio/mvp.pdf http://martinfowler.com/eaaDev/uiArchs.html https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter https://msdn.microsoft.com/en-us/library/ff649571.aspx https://github.com/konmik/konmik.github.io/wiki/Introduction-to-Model-View-Presenter-on-Android http://c2.com/cgi/wiki?ModelViewPresenter http://www.gwtproject.org/articles/mvp-architecture.html https://www.youtube.com/watch?v=oiNfPjV72lg http://polymorphicpodcast.com/shows/mv-patterns/ https://developer.ibm.com/open/2015/10/22/model-view-presenter-mvp-for-ibm-ready-apps/ https://caster.io/episodes/episode-48-model-view-presenter-part-1-what-is-the-mvp-pattern/ http://fernandocejas.com/tag/model-view-presenter/ http://engineering.remind.com/android-code-that-scales/ http://antonioleiva.com/mvp-android/ http://geekswithblogs.net/nharrison/archive/2008/09/22/125373.aspx

Page 5: My perspective on MVP and architecture discussions

image

Page 6: My perspective on MVP and architecture discussions

image

PV

Page 7: My perspective on MVP and architecture discussions

image

Page 8: My perspective on MVP and architecture discussions

image

https://leanpub.com/software-architecture-for-developers https://leanpub.com/visualising-software-architecture

Page 9: My perspective on MVP and architecture discussions

image

I know MVP

No I know MVP

No. I know MVP

Page 10: My perspective on MVP and architecture discussions

Ivory Tower Syndrome- My MVP works so it should work

everywhere- My MVP is the only possible

architecture- Non collaboration- Lack of discussion of the details

further reading: http://techdistrict.kirkk.com/2009/11/03/turtles-and-architecture/

Page 11: My perspective on MVP and architecture discussions

image

Page 12: My perspective on MVP and architecture discussions

image

Indecent Exposure (42):

This smell indicates the lack of what David Parnas so famouslytermed information hiding. The smell occurs when methods or classes that oughtnot to be visible to clients are publicly visible to them. Exposing such code means thatclients know about code that is unimportant or only indirectly important. This contributesto the complexity of a design.

Page 13: My perspective on MVP and architecture discussions

image

Page 14: My perspective on MVP and architecture discussions

image

Page 15: My perspective on MVP and architecture discussions

image

Page 16: My perspective on MVP and architecture discussions

Here is another opinion!- Hangover Cures is an app I wrote back in 2012- Two screens, a list of hangover cures, individual

details- All code was in the Activity- Added new feature:Now has a comment

stream, for people to leave comments about the cures.

Page 17: My perspective on MVP and architecture discussions

Model- Thin layer and a boundary to the rest of the

architecture of the application (in my case ‘clean architecture’)

- It’s not so much a domain model more of a ‘what can be done on this view’ api

- Model contains the threading choices

Page 18: My perspective on MVP and architecture discussions

View- Passive View (not observing the model)- Gets information from ViewModel objects- The Activity as the View on Android- Treat Android Views like primitives and we

always want to wrap them in a Domain View- Encapsulate native Android user interaction

mechanisms and define Domain specific ones

Page 19: My perspective on MVP and architecture discussions

Presenter- Has knowledge of Model & View- Decides on action after user interaction- Observes the Model for changes- Decides how View is notified of Model changes- Doesn’t care about threading- Never uses primitive Views or native Listeners

Page 20: My perspective on MVP and architecture discussions

image

Page 21: My perspective on MVP and architecture discussions

Package Structure- Not necessary for MVP but

a good bedrock to work from

- Feature based- Clearly see the app’s

concerns- Minimal learning curve

Page 22: My perspective on MVP and architecture discussions

Core / Mobile splitMVP is a UI level architecture it is not an application

architecture

MVP belongs in the mobile module*

The presenter can have Android imports, that’s ok

For application wide architecture see: https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.htmlhttps://www.novoda.com/blog/hexawhat-architecture/

*controversial

Page 23: My perspective on MVP and architecture discussions

Single Feature Example - CommentsOnly the Activity needs to be public

First place to start [Feature]Mvp.java

Classes shaded out not directly involved in MVP

Page 24: My perspective on MVP and architecture discussions

[Feature]Mvp.javaclass CommentsMvp {

public interface Model extends Closeable {

...

}

public interface View {

...

}

public interface Presenter {

...

}

- The inner classes allow for great naming practices and improved readabilty.

- This stops strange things like CommentsPresenterImpl implements

CommentsPresenter

- It also highlights the UI layers MVP pattern very obviously to the legacy developer

Page 25: My perspective on MVP and architecture discussions

CommentsMvp.java Implementersclass CommentsModel implements CommentsMvp.Model {

...}

public class CommentsActivityView extends AppCompatActivity implements CommentsMvp.View, CommentsStream.Listener {...

}

class CommentsAnalyticsView implements CommentsMvp.View {...

}

class CommentsPresenter implements CommentsMvp.Presenter, CommentsMvp.Model.AdvertRequestLoadedCallback, CommentsMvp.Model.UserLoadedCallback, CommentsMvp.Model.CommentSavedCallback, CommentsMvp.Model.CommentsLoadedCallback, CommentsMvp.Model.CommentUpdatedCallback {

...}

Page 26: My perspective on MVP and architecture discussions

CommentsMvp.Modelpublic interface Model extends Closeable {

void loadUser(UserLoadedCallback callback); void loadAdvertRequest(AdvertRequestLoadedCallback callback); void loadCommentsFor(Cure cure, SortOrder sortOrder, CommentsLoadedCallback callback);

void saveComment(Cure.Id id, ViewComment comment, CommentSavedCallback callback); void saveVoteFor(Cure.Id id, ViewComment comment, Vote down, CommentUpdatedCallback callback); boolean canVoteOnCommentWith(Cure.Id cureId, Comment.Id commentId);

interface CommentSavedCallback { void onSaved(ViewComment comment); } interface UserLoadedCallback { void onLoaded(String username); // TODO param can be an object containing also users profile pic } interface AdvertRequestLoadedCallback { void onLoaded(AdRequest advertRequest); } interface CommentsLoadedCallback { void onLoaded(List<ViewComment> comments); } interface CommentUpdatedCallback { void onUpdated(ViewComment comment); }}

- callbacks here, can be any observing method

- save/load is a simple pattern when you don’t have much client side state

- knowledge of MVP domain objects

- no knowledge of Android

Page 27: My perspective on MVP and architecture discussions

CommentsMvp.Model Implementorclass CommentsModel implements CommentsMvp.Model { ...

private final UseCaseModelAdapter modelAdapter; private final Log log;

public CommentsModel(UseCaseModelAdapter modelAdapter, Log log) { this.modelAdapter = modelAdapter; this.log = log; }

@Override public void loadAdvertRequest(final AdvertRequestLoadedCallback callback) { Subscription subscription = Observable.create(modelAdapter.getAdvertRequest()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())

...

}

- Thin layer that then moves from MVP architecture to our back end of the app architecture

- Injects the logger to allow dependency inversion

- Controls asynchronicity as the model knows when its innards need threads

Page 28: My perspective on MVP and architecture discussions

CommentsMvp.Model Implementorclass CommentsModel implements CommentsMvp.Model { ...

private final UseCaseModelAdapter modelAdapter; private final Log log;

public CommentsModel(UseCaseModelAdapter modelAdapter, Log log) { this.modelAdapter = modelAdapter; this.log = log; }

@Override public void loadAdvertRequest(final AdvertRequestLoadedCallback callback) { Subscription subscription = Observable.create(modelAdapter.getAdvertRequest()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())

...

}

- Thin layer that then moves from MVP architecture to our back end of the app architecture

- Injects the logger to allow dependency inversion

- Controls asynchronicity as the model knows when its innards need threads

Page 29: My perspective on MVP and architecture discussions

CommentsMvp.Model Implementorclass CommentsModel implements CommentsMvp.Model { ...

private final UseCaseModelAdapter modelAdapter; private final Log log;

public CommentsModel(UseCaseModelAdapter modelAdapter, Log log) { this.modelAdapter = modelAdapter; this.log = log; }

@Override public void loadAdvertRequest(final AdvertRequestLoadedCallback callback) { Subscription subscription = Observable.create(modelAdapter.getAdvertRequest()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread())

...

}

- Thin layer that then moves from MVP architecture to our back end of the app architecture

- Injects the logger to allow dependency inversion

- Controls asynchronicity as the model knows when its innards need threads

Page 30: My perspective on MVP and architecture discussions

CommentsMvp.Viewpublic interface View { void create();

void show(ViewCure cure);

void show(String username);

void show(AdRequest advertRequest);

void show(List<ViewComment> comments);

void showInteractionsFor(ViewComment comment);

void update(ViewComment comment);

void notifyAlreadyVoted();}

- All commanding methods, telling what to do

- Use ViewModels to pass the data, here the naming I think lets it down a bit as ViewCure could be read incorrectly as “view the cure” rather than “the view cure model”

Page 31: My perspective on MVP and architecture discussions

CommentsMvp.Presenterpublic interface Presenter { void onCreate(ViewCure cure);

void onCommented(String comment);

void onSelected(ViewComment comment);

void onSelectedVoteUp(ViewComment comment);

void onSelectedVoteDown(ViewComment comment);

void onSelectedFilterCommentsByMostPopularFirst();

void onSelectedFilterCommentByNewestFirst();}

- includes the lifecycle methods needed

- includes methods that react to user interaction (i.e. methods you call inside a click listener)

Want to investigate this for the lifecycle callbacks https://github.com/soundcloud/lightcycle

Page 32: My perspective on MVP and architecture discussions

CommentsMvp.Presenter Implementorclass CommentsPresenter implements CommentsMvp.Presenter, CommentsMvp.Model.AdvertRequestLoadedCallback, CommentsMvp.Model.UserLoadedCallback, CommentsMvp.Model.CommentSavedCallback, CommentsMvp.Model.CommentsLoadedCallback, CommentsMvp.Model.CommentUpdatedCallback {

private final CommentsMvp.Model model; private final CommentsMvp.View view;

private String username; private ViewCure cure;

@Overridepublic void onCreate(ViewCure cure) { this.cure = cure; view.create(); view.show(cure); model.loadAdvertRequest(this); model.loadUser(this); model.loadCommentsFor(cure, SortOrder.MOST_POPULAR_FIRST, this);}

- observes the model

- knows about the Model and the View

- keeps state

- onCreate it tells the view to create, populates the view and requests first load from the model

Page 33: My perspective on MVP and architecture discussions

CommentsMvp.Presenter Implementorclass CommentsPresenter implements CommentsMvp.Presenter, CommentsMvp.Model.AdvertRequestLoadedCallback, CommentsMvp.Model.UserLoadedCallback, CommentsMvp.Model.CommentSavedCallback, CommentsMvp.Model.CommentsLoadedCallback, CommentsMvp.Model.CommentUpdatedCallback {

private final CommentsMvp.Model model; private final CommentsMvp.View view;

private String username; private ViewCure cure;

@Overridepublic void onCreate(ViewCure cure) { this.cure = cure; view.create(); view.show(cure); model.loadAdvertRequest(this); model.loadUser(this); model.loadCommentsFor(cure, SortOrder.MOST_POPULAR_FIRST, this);}

- observes the model

- knows about the Model and the View

- keeps state

- tells the view to create, populates the view and requests first load from the model

Page 34: My perspective on MVP and architecture discussions

CommentsMvp.Presenter Implementorclass CommentsPresenter implements CommentsMvp.Presenter, CommentsMvp.Model.AdvertRequestLoadedCallback, CommentsMvp.Model.UserLoadedCallback, CommentsMvp.Model.CommentSavedCallback, CommentsMvp.Model.CommentsLoadedCallback, CommentsMvp.Model.CommentUpdatedCallback {

private final CommentsMvp.Model model; private final CommentsMvp.View view;

private String username; private ViewCure cure;

@Overridepublic void onCreate(ViewCure cure) { this.cure = cure; view.create(); view.show(cure); model.loadAdvertRequest(this); model.loadUser(this); model.loadCommentsFor(cure, SortOrder.MOST_POPULAR_FIRST, this);}

- observes the model

- knows about the Model and the View

- keeps state

- tells the view to create, populates the view and requests first load from the model

Page 35: My perspective on MVP and architecture discussions

CommentsMvp.Presenter Implementorclass CommentsPresenter implements CommentsMvp.Presenter, CommentsMvp.Model.AdvertRequestLoadedCallback, CommentsMvp.Model.UserLoadedCallback, CommentsMvp.Model.CommentSavedCallback, CommentsMvp.Model.CommentsLoadedCallback, CommentsMvp.Model.CommentUpdatedCallback {

private final CommentsMvp.Model model; private final CommentsMvp.View view;

private String username; private ViewCure cure;

@Overridepublic void onCreate(ViewCure cure) { this.cure = cure; view.create(); view.show(cure); model.loadAdvertRequest(this); model.loadUser(this); model.loadCommentsFor(cure, SortOrder.MOST_POPULAR_FIRST, this);}

- observes the model

- knows about the Model and the View

- keeps state

- tells the view to create, populates the view and requests first load from the model

Page 36: My perspective on MVP and architecture discussions

CommentsMvp.Presenter Implementor…

@Overridepublic void onSelectedFilterCommentByNewestFirst() { model.loadCommentsFor(cure, SortOrder.NEWEST_FIRST, this);}

...

@Overridepublic void onSaved(ViewComment comment) { view.update(comment);}

...

- observes the View and decides what action to take on user interaction

- observes the Model and decides how the view should be updated

Page 37: My perspective on MVP and architecture discussions

ViewCommentA comment whos responsibilities lie in the view layer

- vs a comment whos responsibilities lie in the domain layer

- Meaning it can have view centric methods- getContentDescription, getFormattedDate

- Immutable, only get never set - Could implement Parcelable

Page 38: My perspective on MVP and architecture discussions

CommentsAnalyticsView- Analytics tracking can be

thought of as another view on the same presenter

- Uses decorator pattern, could also be an observer pattern if you wanted more than two

- Experimental ...

class CommentsAnalyticsView implements CommentsMvp.View { ...

public CommentsAnalyticsView(Tracker tracker, CommentsMvp.View view) { this.tracker = tracker; this.view = view; }

@Override public void create() { tracker.setScreenName("Comments"); tracker.send(new HitBuilders.ScreenViewBuilder().build()); view.create(); }

@Override public void show(Cure cure) { view.show(cure); }… @Override public void showInteractionsFor(ViewComment comment) { tracker.setScreenName("Interaction " + comment.getName()); tracker.send(new HitBuilders.ScreenViewBuilder().build()); view.showInteractionsFor(comment); }}

Page 39: My perspective on MVP and architecture discussions

Presenter InstantiationonCreate is in the Activity, newInstance is in the Presenter (or in any factory)

@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ViewCure cure = (ViewCure) getIntent().getSerializableExtra(EXTRA_CURE); HangoverCuresApplication dependencyProvider = (HangoverCuresApplication) getApplicationContext(); presenter = CommentsPresenter.newInstance(dependencyProvider, this); presenter.onCreate(cure);}

public static CommentsMvp.Presenter newInstance(HangoverCuresApplication dependencyProvider, CommentsMvp.View view) { Log log = new AndroidLog(); FirebaseCommentRepository firebase = new FirebaseCommentRepository(); SharedPrefsVoteRepository sharedPrefsVoteRepository = SharedPrefsVoteRepository.newInstance(dependencyProvider.getApplicationContext()); CommentUseCase commentUseCase = new CommentUseCase(firebase); VotingUseCase votingUseCase = new VotingUseCase(sharedPrefsVoteRepository); UseCaseModelAdapter useCaseModelAdapter = new UseCaseModelAdapter(commentUseCase, votingUseCase); CommentsMvp.Model model = new CommentsModel(useCaseModelAdapter, log); Tracker tracker = dependencyProvider.getAnalyticsTracker(); return new CommentsPresenter(model, new CommentsAnalyticsView(tracker, view));}

Page 40: My perspective on MVP and architecture discussions

Deliberate View language- Try to consider the domain of your UI (talk to the designers &

business analysts)- Never end a custom view with the word view- Avoids communication issues & complications

class CommentsStream extends LinearLayout {

- When using primitive views consider naming them widgetspublic class DetailsActivityView extends AppCompatActivity implements DetailsMvp.View {

private DetailsMvp.Presenter presenter;

private TextView titleWidget;

private TextView descriptionWidget;

private RatingBar ratingBarWidget;

private AdView advertWidget;

Page 41: My perspective on MVP and architecture discussions

image

Page 42: My perspective on MVP and architecture discussions

image

SRS TEAM TIME

Page 43: My perspective on MVP and architecture discussions

image

Page 44: My perspective on MVP and architecture discussions

image

Page 45: My perspective on MVP and architecture discussions

image

Page 46: My perspective on MVP and architecture discussions

image

https://github.com/novoda/spikes/tree/master/Architecture

Page 47: My perspective on MVP and architecture discussions

image

Page 48: My perspective on MVP and architecture discussions

Thanksblundell_appsG what?blundell

Page 49: My perspective on MVP and architecture discussions

throw new SlideIndexOutOfBoundsException();

blundell_appsG what?blundell

Questions?