Upload
christian-panadero
View
1.893
Download
0
Embed Size (px)
Citation preview
Christian Panaderohttp://panavtec.me
@PaNaVTEC Github - PaNaVTEC
My way to clean Android
Fernando Cejas Jorge Barroso
Pedro Gomez Sergio Rodrigo
@fernando_cejas @flipper83
@pedro_g_s @srodrigoDev
Android developer en Sound Cloud
Android developer en Tuenti
Cofounder & Android expert en Karumi
Android developer en Develapps
Alberto Moraga Carlos Morera@albertomoraga @CarlosMChicaiOS Developer en Selltag Android Developer en Viagogo
Agradecimientos
¿Por qué Clean?• Desacoplado de los frameworks
• Testable
• Desacoplado de la UI
• Desacoplado de BDD
• Desacoplado de cualquier detalle de implementación
Conceptos• Patrón command (Invoker, command, receiver)
• Interactors / Casos de uso
• Abstracciones
• Data Source
• Repository
Modelando el proyecto• App (UI, DI y detalles de implementación)
• Presentation
• Domain y Entities
• Repository
• Data Sources
Flow
View
Presenter
Presenter
Interactor
Interactor
Interactor
Interactor
Repository
Repository
DataSource
DataSource
DataSource
UI: MVP - View
public class MainActivity extends BaseActivity implements MainView { @Inject MainPresenter presenter; @Override protected void onResume() { super.onResume(); presenter.onResume(); } @Override public void onRefresh() { presenter.onRefresh(); }
UI: MVP - Presenterpublic class MainPresenter extends Presenter { private MainView mainView; private Bus bus;
public void onResume() { bus.register(this); }
public void onPause() { bus.unregister(this); }
public void onRefresh() { mainView.clearData();
… }
Presentation - Domainpublic class MainPresenter extends Presenter { public void onCreate() { interactorInvoker.execute(getContactsInteractor); }
public void onEvent(GetContactsEvent event) { if (event.getError() == null) { List<PresentationContact> contacts = event.getContacts();
mainView.refreshContactsList(contacts); } else { mainView.showGetContactsError(); } }
public class GetContactsInteractor implements Interactor { private Bus bus; private ContactsRepository repository;
@Override public void execute() { GetContactsEvent event = new GetContactsEvent(); try { List<Contact> contacts = repository.obtainContacts(); event.setContacts(contacts); } catch (RetrieveContactsException e) { event.setError(e); } bus.post(event); } }
Domain - Interactor
Repository Interfacepublic interface ContactsRepository {
List<Contact> obtainContacts() throws CantRetrieveContactsException; Contact obtain(String md5) throws CannotObtainContactException;
}
Repository imp@Override public List<Contact> obtainContacts() throws RetrieveContactsException { List<Contact> contacts = null; try { contacts = bddDataSource.obtainContacts(); } catch (ObtainContactsBddException | InvalidCacheException e) { try { contacts = networkDataSource.obtainContacts(); bddDataSource.persist(contacts); } catch (UnknownObtainContactsException | ContactsNetworkException) { throw new RetrieveContactsException(); } catch (PersistContactsBddException) { e.printStackTrace(); } } return contacts; }
Data source Interface
public interface ContactsNetworkDataSource { public List<Contact> obtainContacts() throws ContactsNetworkException, UnknownObtainContactsException; }
private ContactsApiService apiService; private static final ApiContactMapper mapper = new ApiContactMapper();
@Override public List<Contact> obtainContacts() throws ContactsNetworkException { try { ApiContactsResponse apiContactsResponse = apiService.obtainUsers(100); List<ApiContactResult> results = apiContactsResponse.getResults();
<MAP APICONTACTS TO CONTACTS>
return contacts; } catch (Throwable e) { throw new ContactsNetworkException(); } }
Data source imp
Caching Strategypublic interface CachingStrategy<T> { boolean isValid(T data);}
public TtlCachingStrategy(int ttl, TimeUnit timeUnit) { ttlMillis = timeUnit.toMillis(ttl); }
@Override public boolean isValid(T data) { return (data.getPersistedTime() + ttlMillis) > System.currentTimeMillis(); }
Caching Strategy@Override public List<Contact> obtainContacts() throws ObtainContactsBddException, UnknownObtainContactsException, InvalidCacheException { try { List<BddContact> bddContacts = daoContacts.queryForAll(); if (!cachingStrategy.isValid(bddContacts)) { deleteBddContacts(cachingStrategy.candidatesToPurgue(bddContacts)); throw new InvalidCacheException(); } ArrayList<Contact> contacts = new ArrayList<>(); for (BddContact bddContact : bddContacts) { contacts.add(transformer.transform(bddContact, Contact.class)); } return contacts; } catch (java.sql.SQLException e) { throw new ObtainContactsBddException(); } catch (Throwable e) { throw new UnknownObtainContactsException(); } }
Ventajas de Repository
• La lógica de negocio no sabe de donde vienen los datos.
• Fácil de cambiar la implementación de los orígenes de datos.
• En caso de cambio de orígenes de datos la lógica de negocio no se ve alterada.
Picassopublic interface ImageLoader { public void load(String url, ImageView imageView); public void loadCircular(String url, ImageView imageView); }
public class PicassoImageLoader implements ImageLoader { private Picasso picasso; public PicassoImageLoader(Picasso picasso) { this.picasso = picasso; }
public void load(String url, ImageView imageView) { picasso.load(url).into(imageView); }
@Override public void loadCircular(String url, ImageView imageView) { picasso.load(url).transform(new CircleTransform()).into(imageView); }
ErrorManagerpublic interface ErrorManager { public void showError(String error);}
public class SnackbarErrorManagerImp implements ErrorManager { @Override public void showError(String error) {SnackbarManager.show(Snackbar.with(activity).text(error)); }}
public class ToastErrorManagerImp implements ErrorManager { @Override public void showError(String error) { Toast.makeText(activity, error, Toast.LENGTH_LONG).show(); }}
Consejos
• Trabaja siempre con abstracciones nunca con concreciones.
• Usa un buen naming, si ves que hay alguna figura que has creado que no sabes que nombre poner, seguramente esté mal modelada.
• Si creas nuevas figuras usa la diana inicial para asegurarte que las creas en la capa correspondiente
Show me the code!https://github.com/PaNaVTEC/Clean-Contacts
Referencias
• Fernando Cejas - Clean way
• Jorge Barroso - Arquitectura Tuenti
• Pedro Gomez - Dependency Injection
• Pedro Gomez - Desing patterns
• Uncle Bob - The clean architecture
¿Preguntas?
Christian Panaderohttp://panavtec.me
@PaNaVTEC Github - PaNaVTEC