MVP

Una arquitectura limpia 

MVC

 

Activity / Fragment

 

Tareas en la UI

Consultas a bases de datos

Control de configuración

MVP

Una arquitectura más limpia 

(Desde mi humilde punto de vista)

MVP

By example

 

Interactor

 

 

Presenter

 

 

ViewModel

 

Petición de datos

Notificar

Mostrar información

ViewModel

Todos los métodos que afecten a la vista

public interface ArtistSearchView {

    void setupList();

    void setupAdapter();

    void setupSearchInput();

    void displayFoundArtists(ArrayList<Artist> artists);

    void displayFailedSearch();

    void displayNetworkError();

    void displayServerError();
}

ViewModel

Comunmente implementado por una actividad o fragmento


 @Override
public void setupList() {
    mArtistResultsList.setLayoutManager(new LinearLayoutManager(CONTEXT));
    mArtistResultsList.setAdapter(mResultsAdapter);
}

@Override
public void setupAdapter() {
    mResultsAdapter.setOnItemClickListener(mSearchPresenter);
}

@Override
public void setupSearchInput() {
    mArtistSearchInput.setQueryListener(mSearchPresenter);
}

@Override
public void onQueyChange(String query){
    mPresenter.searchArtists(query);
}

@Override
public void displayFoundArtists(ArrayList<Artist> artists) {
    mResultsAdapter.replace(artists);
}

@Override
public void displayFailedSearch() {
    Toast.makeText(CONTEXT, R.string.failed_search, Toast.LENGTH_SHORT).show();
}

@Override
public void displayNetworkError() {
    Toast.makeText(CONTEXT, R.string.network_error, Toast.LENGTH_SHORT).show();
}

@Override
public void displayServerError() {
    Toast.makeText(CONTEXT, R.string.server_error, Toast.LENGTH_SHORT).show();
}

Recibe las interacciones del usuario con la vista

Presenter

Es el puente entre la vista y el interactor

Ejecuta los métodos del interactor para obtener datos

Transfiere los datos obtenidos por el interactor a la vista

Presenter

 @Override
    public void searchArtists(String query) {
        searchInteractor.performSearch(query, this);
    }

    @Override
    public void onArtistsFound(ArrayList<Artist> artists) {
        searchView.displayFoundArtists(artists);
    }

    @Override
    public void onFailedSearch() {
        searchView.displayFailedSearch();
    }

    @Override
    public void onNetworkError(RetrofitError error) {
        error.printStackTrace();
        searchView.displayNetworkError();
    }

    @Override
    public void onServerError(RetrofitError error) {
        error.printStackTrace();
        searchView.displayServerError();
    }

Interactor

Ejecuta llamadas al servidor, bases de datos o preferencias locales

Se establece comunicación con el presentador a través de interfaces o un Bus de eventos

Interactor

@Override
    public void performSearch(String query, final ArtistSearchServerCallback callback){
        apiService.searchArtist(query, new Callback<ArtistSearchResponse>() {
            @Override
            public void success(ArtistSearchResponse artistSearchResponse, Response response) {
                if(artistSearchResponse.getArtists().isEmpty())
                    callback.onFailedSearch();

                else
                    callback.onArtistsFound(artistSearchResponse.getArtists());
            }

            @Override
            public void failure(RetrofitError error) {
                if (error.getKind() == RetrofitError.Kind.NETWORK)
                    callback.onNetworkError(error);

                else
                    callback.onServerError(error);
            }
        });
    }

MVP

Recapitulando el proceso de busqueda

ViewModel

void setupList()

void setupAdapter()

void setupSearchInput()
------------------------
void onQueryChange(String query);

Presenter

void searchArtists(String query)

Interactor

void performSearch(String query, 
    ArtistSearchServerCallback callback);

Presenter

 void onArtistsFound(ArrayList<Artist> artists) 

 void onFailedSearch()

 void onNetworkError(RetrofitError error)

 void onServerError(RetrofitError error)

ViewModel

void displayFoundArtists(ArrayList<Artist> artists);

void displayFailedSearch();

void displayNetworkError();

void displayServerError();

MVP

Una arquitectura limpia 

Dagger 2

Inyección de dependencias

Dagger 2

Dependencias visibles

 

Interactor

 

 

Presenter

 

 

ViewModel

 

Dependency

Dependency

Dagger 2

¿Qué quiere decir dependencia?

public ArtistSearchPresenter(ArtistSearchView view, ArtistSearchInteractor interactor) {
        searchView = view;
        searchInteractor = interactor;
}

Dependencia

Dependencia

Necesitamos de otros objetos para construir un objeto

Dagger 2

¿Cómo lo soluciona Dagger 2?

Module

Component

Clases que crean instancias de los objetos para la inyección de dependencias

Constituyen una API interna a través de la cual obtenemos los objetos que necesitamos para resolver las dependencias

Dagger 2

Ejemplo

SpotifyStreamerComponent

@Modules

SpotifyStreamerModule

InteractorModule

Context getContext();
ArtistSearchInteractor getArtistSearchInteractor();
SpotifyApiService getSpotifyApiService();

API

@Provides @Singleton 
public Context provideContext()

@Provides @Singleton 
public SpotifyApiService provideSpotifyApiService(RestAdapter adapter)
@Provides
public ArtistSearchInteractor provideArtistSearchInteractor(SpotifyApiService apiService)

@Provides

@Provides

Dagger 2

Un vistazo más de cerca

En nuestro fragmento, necesitamos el presentador

public class ArtistSearchFragment implements ArtistSearchView  {
//...

    ArtistSearchPresenter mSearchPresenter;

//...
}

¿Qué necesitamos para construir este presentador?

 public ArtistSearchPresenter(ArtistSearchView view, ArtistSearchInteractor interactor) {
        searchView = view;
        searchInteractor = interactor;
}

Dagger 2

Un vistazo más de cerca

El módulo definirá como construir el presentador

@Module
public class ArtistSearchModule {

    private ArtistSearchView view;

    //Para construir este modulo necesitamos el ViewModel
    public ArtistSearchModule(ArtistSearchView view) {
        this.view = view;
    }

    @Provides
    public ArtistSearchView provideView() {
        return view;
    }

    @Provides
    public ArtistSearchPresenter providePresenter(ArtistSearchView view, ArtistSearchInteractor interactor) {
        return new ArtistSearchPresenter(view, interactor);
    }
}

Dagger 2

Un vistazo más de cerca

¿Y que se necesita para construir el interactor?

@Module
public class InteractorModule {

    @Provides
    public ArtistSearchInteractor provideArtistSearchInteractor(SpotifyApiService apiService){
        return new ArtistSearchInteractorImpl(apiService);
    }

}

¿Dónde se encuentra el SpotifyApiService?

@Module
public class SpotifyStreamerModule {

    private SpotifyStreamerApp app;

    public SpotifyStreamerModule (SpotifyStreamerApp app) {
        this.app = app;
    }

    @Provides @Singleton public Application provideApplication() {
        return app;
    }

    @Provides @Singleton public Context provideContext() {
        return app;
    }

    @Provides @Singleton public RestAdapter provideApiAdapter() {
        return SpotifyApiAdapter.getInstance();
    }

    @Provides @Singleton public SpotifyApiService provideSpotifyApiService(RestAdapter adapter){
        return adapter.create(SpotifyApiService.class);
    }

}

Dagger 2

Un vistazo más de cerca

Finalmente los componentes proveeran las instancias

@ActivityScope
@Component(
        //El interactor puede ser proporcionado por este componente
        dependencies = SpotifyStreamerComponent.class,
        modules = ArtistSearchModule.class
)
public interface ArtistSearchComponent {

    ArtistSearchPresenter getPresenter();
}
@Singleton
@Component(
        modules = {
                SpotifyStreamerModule.class,
                InteractorModule.class
        }
)
public interface SpotifyStreamerComponent {

    ArtistSearchInteractor getArtistSearchInteractor();
}

Dagger 2

Un vistazo más de cerca

Ya podemos inyectarlo a nuestro fragmento

@Inject
ArtistSearchPresenter mSearchPresenter;

//Este es el método que declara la inyección de las dependencias
protected void setUpComponent() {
        DaggerArtistSearchComponent.builder()
                .spotifyStreamerComponent(SpotifyStreamerApp.getApp(getActivity()).getComponent())
                .artistSearchModule(new ArtistSearchModule(this))
                .build()
                .inject(this);
}

SpotifyStreamerComponent

@Modules

SpotifyStreamerModule

InteractorModule

Context getContext();
ArtistSearchInteractor getArtistSearchInteractor();
@Provides @Singleton 
public Context provideContext()

@Provides @Singleton 
public RestAdapter provideApiAdapter() 

@Provides @Singleton 
public SpotifyApiService provideSpotifyApiService(RestAdapter adapter)
@Provides
public ArtistSearchInteractor provideArtistSearchInteractor(SpotifyApiService apiService)

@Provides

@Provides

Dependency

ArtistSearchComponent

void inject(ArtistSearchFragment searchFragment);

ArtistSearchPresenter getPresenter();

ArtistSearchModule

@Modules

@Provides

@Provides
public ArtistSearchView provideView()

@Provides
public ArtistSearchPresenter providePresenter(ArtistSearchView view, ArtistSearchInteractor interactor)

MVP + Dagger 2

By Petter Hdz

MVP + Dagger 2

  • 781