안드로이드 아키텍처 패턴 MVP

MVP

  • MVC 패턴에서는 UI와 비즈니스 로직이 모두 Activity와 Fragment에 공존했다. MVP 디자인 패턴에서는 MVC와는 비슷하지만 Activity와 Fragment의 UI와 비즈니스 로직을 분리하는데 집중해 데이터의 흐름이 다르다.

Model과 View의 역할은 MVC와 동일 하지만 MVP 패턴에서는 Controller 대신에 Presenter개념을 이용해 UI와 비즈니스 로직을 구분해 유닛테스트가 용이 하게 만들었다.

  • Presenter는 View와 Model의 인스턴스를 가지며 이 둘을 연결하는 역할을 하기때문에 Presenter와 View는 1:1 관계를 가진다.

MVP 패턴의 장점

  • View와 Model간의 의존성이 없고, UI와 비즈니스 로직을 구분했기 때문에 유닛 테스트가 용이하다.

MVP 패턴의 단점

  • View와 Presenter간의 의존성이 높고 1:1 관계를 유지해야 하기 때문에 Presenter를 재사용 할 수 없다. View가 늘어날때 마다 Presenter도 같이 늘어나 클래스가 많아진다.
  • 기능이 많아질수록 Presenter가 거대해지는 단점이 있다.

Google android architecture-sample MVP

  • MVP 아키텍처 패턴을 이용한 google architecture-sample MVP 코드 일부 분석 내용

view와 Presenter간에는 1:1 관계를 유지한다.

TaskActivity

  • TasksFragment는 TasksContract.View 인터페이스를 상속하고있다.
  • TaskPresenter는 TasksContract.presenter 인터페이스를 상속하고있다.
  • TasksContract인터페이스내에 View와 Presenter 인터페이스를 정의한 구조, View와 Presenter의 역할을 정의 해두었다.
  • TaskActivity는 TaskPresenter타입의 멤버변수를 가지고 있는데 onCreate()메서드 단계에서 TaskPresenter와 TasksFragment(view)간의 매핑을 진행 해주고있다.
  • TaskFragment는 TasksContract.Presenter 타입을 멤버 변수로 가지고 있고, TaskPresenter는 TasksContract.View 타입을 멤버 변수로 가지고 있다. 실제로 바인딩을 해줄 때는 인터페이스의 구현체를 넣어주지만 이렇게 인터페이스를 참조함으로써 구현에 의존하는것이 아닌 역할에 의존하게 되고 결합도가 낮아지게 된다. TasksContract.Presenter의 구현체인 TaskPresenter의 구현을 바꿔도 TasksFragment에는 아무 영향이 미치지 않게 되는것이다.

TaskPresenter메서드 정리

void loadTasks(boolean forceUpdate)

  • constract.presenter에 정의된 추상 메서드의 구현체
  • 내부적으로 forceUpdate와 first load인지 체크후 LoadingUI를 표시할지 여부를 포함한 loadTasks 메서드 호출

void loadTasks(boolean forceUpdate, boolean showLoadingUI)

  • LoadIngUI가 True인 경우 mTasksView.setLoadingIndicator호출
    • srl.setRefreshing(active)를 통해서 새로고침 애니메이션 동작
  • MVP 패턴으로 인해 View에서 SwipeRefreshLayout을 이용하더라도 presenter를 거쳐서 view를 다시 갱신하는것을 알 수 있다.

Data

  • Google Sample MVP에서는 DataSource 객체가 수행해야할 동작들을 정의해둔 TaskDataSource라는 인터페이스가 존재한다.
  • 해당 인터페이스내에는 API 호출, DB io와 같은 비동기 동작의 결과 값을 핸들링 하기 위한 콜백을 명시해둔 인터페이스가 2개 더 존재한다.
interface LoadTasksCallback {
	void onTasksLoaded(List<Task> tasks);
    void onDataNotAvailable();
}
interface GetTaskCallback {
	void onTaskLoaded(Task task)
    void onDataNotAvailable()
}

TaskDataSource 인터페이스를 구현한 클래스는 총 3개로

  1. TaskLocalDataSource
    • LocalData에 접근하는 객체
  2. TaskRemoteDataSource
    • 서버와 통신해서 데이터를 받아오는 객체
      • 해당코드에서는 서버를 사용하지 않으므로 Mock으로 구현되어 있다.
  3. TaskRepository
    • TaskDataSource타입의 localDataSource변수와 remoteDataSource 변수를 가지고있다.
    • 즉 LocalDataSource와 RemoteDataSource를 감싸고 있는 클래스가 된다.

이렇게 3개가 존재한다.

그림으로 표현하면 다음과같다.

TaskRepository

  • TaskRepository는 LocalDatasource와 RemoteDataSource를 감싸고있는 구조다.
  • 내부에 LocalDataSource 객체와(실제론 인터페이스), RemoteDataSource를 가지고 있다. 또한 메모리상에 데이터를 캐싱하기 위한 프로퍼티도 가지고 있다.

void getTasks(LoadTasksCallback)

  • 캐시, local data source, remote data source 중에서 가장먼저 데이터를 가져올수 있는곳에서 tasks를 가져오는 메서드
  • cachedTasks에 데이터가 있고 isDirty! 인 경우 cachedTasks에서 데이터를 가져와 반환한다.
  • cacheIsDirty인 경우 서버로부터 데이터를 받아온다.
    • 서버로부터 데이터를 제대로 수신할 경우 local data source와 메모리에 해당 데이터를 최신화시킨다.
  • 그렇지않은 경우 로컬디비로부터 데이터를 가져와 콜백을 수행한다.

좋은 웹페이지 즐겨찾기