Upgrade to Pro — share decks privately, control downloads, hide ads and more …

From Legacy to Hexagonal Android (Droidcon Lond...

From Legacy to Hexagonal Android (Droidcon London 2014)

Rubén Serrano

October 30, 2014
Tweet

More Decks by Rubén Serrano

Other Decks in Programming

Transcript

  1. ! ! 3. One dev from a contractor + one

    senior iOS dev one junior iOS dev
  2. ! ! 4. One dev from a contractor + one

    senior iOS dev one junior iOS dev
  3. ! 4. A different Android dev + One dev from

    a contractor one senior iOS dev one junior iOS dev
  4. ! 5. A different Android dev + One dev from

    a contractor one senior iOS dev one junior iOS dev + one confused Android team
  5. 1. We have huge technical debt 2. We can’t stop

    developing new features 3. We can’t remove debt at this point
  6. 1. We have huge technical debt 2. We can’t stop

    developing new features 3. We can’t remove debt at this point 4. We shouldn’t increase our technical debt
  7. ! 
 getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
 @Override
 public Loader<Cursor>

    onCreateLoader(int id, Bundle args) {
 return new CursorLoader(getActivity(), MainContentProvider.URI, null, null, null, null);
 }
 
 @Override
 public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
 listAdapter.swapCursor(data);
 }
 
 @Override
 public void onLoaderReset(Loader<Cursor> loader) {
 listAdapter.swapCursor(null);
 }
 }); MainFragment
  8. MainView & MainModel public interface MainView {
 public void swaplListData(Cursor

    cursor);
 } ! public interface MainModel {
 public void setPresenter(MainPresenter presenter);
 public void startLoadingData(Context context);
 }

  9. MainFragment public class MainFragment extends Fragment implements MainView {
 //...


    
 @Override
 public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 presenter = PresenterFactory.getMainPresenter(this);
 } ! @Override
 public void onActivityCreated(Bundle savedInstanceState) {
 //...
 presenter.notifyOnCreate(getActivity());
 } ! @Override
 public void swaplListData(Cursor cursor) {
 listAdapter.swapCursor(cursor);
 }
  10. MainPresenter public class MainPresenter {
 private MainView mainView;
 private MainModel

    mainModel;
 
 //...
 
 public void notifyOnCreate(Context context) {
 mainModel.startLoadingData(context);
 }
 
 public void notifiyLoadedDataAvailable(Cursor cursor) {
 mainView.swaplListData(cursor);
 }
 }
  11. PresenterFactory public class PresenterFactory {
 public static MainPresenter getMainPresenter(MainView view)

    {
 MainModel model = new MainCursorModel();
 return MainPresenter.newInstance(view, model);
 }
 }
  12. MainCursorModel public class MainCursorModel implements MainModel {
 //... 
 @Override


    public void startLoadingData(Context context) {
 new LoadDataAsyncTask().execute(context);
 }
 
 private class LoadDataAsyncTask extends AsyncTask<Context, Void, Cursor > {
 //... 
 @Override
 protected void onPostExecute(Cursor result) {
 super.onPostExecute(result);
 presenter.notifiyLoadedDataAvailable(result);
 }
 }
 }
  13. Pros & Cons View decoupled from model Cleaner code and

    smaller fragment/activities View and model not really decoupled (cursor) All the components use the framework
  14. Logic Sensors Network Database Layout + Activity/ Fragment Port Port

    Port Port Boundary Boundary Boundary Boundary
  15. Module Core Module App Module App Module App Module App

    Core Core Core Core App App App App
  16. MainFragment public class MainFragment extends Fragment {
 //...
 private MainFragmentBoundary

    viewBoundary;
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
 //...
 viewBoundary = MainFragmentBoundary.newInstance(this);
 }
 
 @Override
 public void onActivityCreated(Bundle savedInstanceState) {
 //...
 viewBoundary.notifyOnCreate();
 }
  17. MainFragment public void setListAdapter() {
 listAdapter = new ArrayAdapter<String>(getActivity(),
 android.R.layout.simple_list_item_1,


    android.R.id.text1,
 new ArrayList<String>(0));
 listView.setAdapter(listAdapter);
 }
 
 public void swapList(List<String> names) {
 listAdapter.clear();
 listAdapter.addAll(names);
 }
  18. MainFragmentBoundary public class MainFragmentBoundary implements MainViewPort {
 private MainFragment mainFragment;


    private MainLogic logic;
 
 //...
 public void notifyOnCreate() {
 logic.notifyOnCreate();
 } 
 @Override
 public void swaplListData(List<String> names) {
 mainFragment.swapList(names);
 }
  19. MainModelBoundary public class MainModelBoundary implements MainModelPort {
 private MainLogic logic;


    private MainRepository repository;
 //...
 
 @Override
 public void startLoadingData() {
 repository.startLoadingData(new MainRepository.OnDataLoadedListener() {
 @Override
 public void onDataLodaded(Cursor cursor) {
 notifyDataLoaded(cursor);
 }
 });
 }
 
 private void notifyDataLoaded(Cursor cursor) {
 List<String> names = mapCursorToList(cursor);
 logic.notifiyLoadedDataAvailable(names);
 }
  20. MainModelBoundary private List<String> mapCursorToList(Cursor cursor) {
 List<String> names = new

    ArrayList<String>();
 int nameColumnIndex = cursor.getColumnIndex(FakeDatabase.COLUMN_NAME);
 while (cursor.moveToNext()) {
 String name = cursor.getString(nameColumnIndex);
 names.add(name);
 }
 return names;
 }
  21. Pros & Cons Logic is not going to be affected

    by framework changes Logic is pure Java: easier to test Less changes when replacing a plugin Easier for 2 devs to work on the same feature More complex architecture Need to map each POJO for each layer What happens when the plugins need to cooperate?
  22. Logic Sensors Network Database Layout + Activity/ Fragment Port Port

    Port Port Boundary Boundary Boundary Boundary
  23. We don’t like: callback’s hell + AsyncTasks We don’t mind

    (but could be a problem): only commands in separate threads
  24. We don’t like: callback’s hell + AsyncTasks We don’t mind

    (but could be a problem): only commands in separate threads We would love: RxJava
  25. Use case Model plugin Layout + Activity/ Fragment Presenter Use

    case Use case Model plugin Model plugin
  26. When? • If you expect 2+ devs working on the

    same feature • Unless you are sure the app is going to die in a near future • You know for sure you will change your plugins