LDP Local Directional Pattern & LDN Local Directional Number Pattern 报告人:黄倩颖.
Data Uni-Directional Architecture in Android · 2018-11-10 · 목차 1. 세션 목표 2. 현...
Transcript of Data Uni-Directional Architecture in Android · 2018-11-10 · 목차 1. 세션 목표 2. 현...
이승민, GDE Android Korea@maryangmin
Seoul
Data Uni-Directional Architecture in Android
목차1.세션 목표2.현 구조의 문제점3. Data Uni-Directional Architecture (UDA)4. Flux5. Redux6. MVI7. UDA 정리8.간단 응용9.마무리
Korea
이번 세션을 통해 여러분들이 얻어갔으면 좋겠는 것세션 목표
1.UDA란 것이 있구나2.저런 이유에서 UDA 라는 것이 나왔구나3.UDA는 기존 아키텍쳐와 저런 차이점이 있구나4.우리 앱은 UDA가 필요할까?5.기존 구조로 짜여진 우리 앱에 UDA를 어떻게 적용할까?(마지막) Flux / Redux / MVI는 이런거구나
3가지 아키텍쳐 예시를 통해 UDA 근본을 고민해보시기를 바래요!
현 구조의 문제점
Seoul
아까 다 읽었는데 숫자가 또 있네?
한두번도 아니고... 왜 이럴까?
새 댓글 Count=2
우리가 바라는 시나리오
Count=0
Count++
Clear알림클릭
비동기 작업View Input View 갱신
새 댓글 Count=0
현실
Count=1
Count++
Clear알림클릭 숫자가남는다
비동기 작업View Input View 갱신
입력 갱신
절망
작업
입력 갱신작업
입력 갱신작업
입력 갱신
결과를 예상할 수 없다!
난 직접 갱신!
무엇이 문제일까?
무엇이 문제일까?- View에 영향을 주는 State 변화가 여러곳에서 일어남
입력 갱신난 직접 갱신!
무엇이 문제일까?- View에 영향을 주는 State 변화가 여러곳에서 일어남- 비동기로 일어나는 State 변화 시점을 예상할 수 없음
입력 갱신작업
다수의 입력 언제 끝날지 모르는 작업
입력 갱신난 직접 갱신!
무엇이 문제일까?- View에 영향을 주는 State 변화가 여러곳에서 일어남- 비동기로 일어나는 State 변화 시점을 예상할 수 없음- 모델에서 결정되는 State를 View에서 넘겨받아 따로 관리하기 때문에 격차가 생김
입력 갱신작업
다수의 입력 언제 끝날지 모르는 작업
State모델 갱신할 때는 사실 달라진 State
입력 갱신난 직접 갱신!
Data Uni-Directional Architecture(단방향 데이터 구조)
Uni-Directional Architecture (UDA) Core
- View에 영향을 주는 State는 한 방향으로만 수정할 수 있다. 단방향
Uni-Directional Architecture (UDA) Core
- View에 영향을 주는 State는 한 방향으로만 수정할 수 있다. 단방향- 앞의 액션이 끝나지 않으면 뒤 액션을 실행하지 않는다. 동기적 실행
Uni-Directional Architecture (UDA) Core
- View에 영향을 주는 State는 한 방향으로만 수정할 수 있다. 단방향- 앞의 액션이 끝나지 않으면 뒤 액션을 실행하지 않는다. 동기적 실행- Model은 State를 변화시키고, View는 State를 참조만 한다. View와 State 분리
Uni-Directional Architecture (UDA) Core
UDA 종류
-Flux (Architecture by Facebook)
-Redux (Library related Flux)
-MVI (UDA Architecture in Android)
UDA 종류
-Flux (Architecture by Facebook)
-Redux (Library related Flux)
-MVI (UDA Architecture in Android)
3가지 예시를 통해 UDA를 알아봅시다!
Flux
Seoul
출처: https://facebook.github.io/flux/docs/in-depth-overview.html#content
FluxFacebook이 만든 단방향 데이터 아키텍쳐
ActionCreator API
(EventBus)
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { Str ing text = inputTodo.getText() . toStr ing() ; i f ( "" .equals( text)) return; act ionCreator.create(text) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> act ionCreator.c lear()) ;}
View
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { Str ing text = inputTodo.getText() . toStr ing() ; i f ( "" .equals( text)) return; act ionCreator.create(text) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> act ionCreator.c lear()) ;}
View
publ ic c lass TodoAct ionCreator { publ ic void create(Str ing todo) { Dispatcher.dispatch(Act ion.wi th(TodoAct ions.TYPE_CREATE) .data(TodoAct ions.KEY_TEXT, todo)) ; }
publ ic void c lear() { Dispatcher.dispatch(Act ion.wi th(TodoAct ions.TYPE_CLEAR)); }}
publ ic c lass TodoAct ions { publ ic stat ic f inal Str ing TYPE_CREATE = "CREATE"; publ ic stat ic f inal Str ing TYPE_CLEAR = "CLEAR"; publ ic stat ic f inal Str ing KEY_TEXT = “KEY_TEXT";}
ActionCreator
publ ic c lass TodoAct ionCreator { publ ic void create(Str ing todo) { Dispatcher.dispatch(Act ion.wi th(TodoAct ions.TYPE_CREATE) .data(TodoAct ions.KEY_TEXT, todo)) ; }
publ ic void c lear() { Dispatcher.dispatch(Act ion.wi th(TodoAct ions.TYPE_CLEAR)); }}
publ ic c lass TodoAct ions { publ ic stat ic f inal Str ing TYPE_CREATE = "CREATE"; publ ic stat ic f inal Str ing TYPE_CLEAR = "CLEAR"; publ ic stat ic f inal Str ing KEY_TEXT = “KEY_TEXT";}
ActionCreator
Action 생성 및 Dispatch
publ ic c lass Dispatcher { pr ivate stat ic EventBus bus = EventBus.getDefaul t ( ) ;
publ ic stat ic void dispatch(Act ion act ion) { bus.post(act ion); }}
Dispatcher
publ ic c lass Dispatcher { pr ivate stat ic EventBus bus = EventBus.getDefaul t ( ) ;
publ ic stat ic void dispatch(Act ion act ion) { bus.post(act ion); }}
Dispatcher
publ ic c lass TodoStore extends Store { pr ivate List<Todo> todos = new ArrayList<>() ;
@Subscr ibe publ ic void onEvent(Act ion act ion) { switch (act ion.getType()) { case TodoAct ions.TYPE_CREATE: Str ing text = (Str ing) act ion.getData() .get(TodoAct ions.KEY_TEXT); todos.add(new Todo(text)) ; emitChange() ; break; case TodoAct ions.TYPE_CLEAR: todos.clear() ; emitChange() ; break; } }}
Store
publ ic c lass TodoStore extends Store { pr ivate List<Todo> todos = new ArrayList<>() ;
@Subscr ibe publ ic void onEvent(Act ion act ion) { switch (act ion.getType()) { case TodoAct ions.TYPE_CREATE: Str ing text = (Str ing) act ion.getData() .get(TodoAct ions.KEY_TEXT); todos.add(new Todo(text)) ; emitChange() ; break; case TodoAct ions.TYPE_CLEAR: todos.clear() ; emitChange() ; break; } }}
Store
비지니스 로직 수행(State 변경 )
publ ic c lass TodoStore extends Store { pr ivate List<Todo> todos = new ArrayList<>() ;
@Subscr ibe publ ic void onEvent(Act ion act ion) { switch (act ion.getType()) { case TodoAct ions.TYPE_CREATE: Str ing text = (Str ing) act ion.getData() .get(TodoAct ions.KEY_TEXT); todos.add(new Todo(text)) ; emitChange() ; break; case TodoAct ions.TYPE_CLEAR: todos.clear() ; emitChange() ; break; } }}
Store
protected void emitChange() { Dispatcher.emitChange(changeEvent()) ;}
@Subscr ibe(threadMode = ThreadMode.MAIN)publ ic void onEvent(TodoStore.TodoChangeEvent event) { updateUI() ;}
pr ivate void updateUI() { adapter.set I tems(store.getTodos()) ;}
View
@Subscr ibe(threadMode = ThreadMode.MAIN)publ ic void onEvent(TodoStore.TodoChangeEvent event) { updateUI() ;}
pr ivate void updateUI() { adapter.set I tems(store.getTodos()) ;}
View
Store의 State 참조
Flux 정리
Flux 정리- View -> ActionCreator -> Action -> Dispatcher -> Store -> View
Flux 정리- View -> ActionCreator -> Action -> Dispatcher -> Store -> View- Multiple Store- Store가 비지니스 로직을 실행한다. (State를 관리한다.)- Mutable State- 필요할 경우 ActionCreator가 Repository 역할을 한다.
Redux
Seoul
출처: https://blog.novoda.com/introduction-to-redux-in-flutter/
ReduxFlux에서 고안한 State 관리 라이브러리(프레임워크)
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { Str ing text = inputTodo.getText() . toStr ing() ; i f ( "" .equals( text)) return; store.dispatch(act ionCreator.create(text)) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> store.dispatch(act ionCreator.c lear())) ;}
View
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { Str ing text = inputTodo.getText() . toStr ing() ; i f ( "" .equals( text)) return; store.dispatch(act ionCreator.create(text)) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> store.dispatch(act ionCreator.c lear())) ;}
View
Store.dispatch() 직접 호출
publ ic c lass TodoAct ionCreator { publ ic Act ion create(Str ing todo) { return Act ion.wi th(TodoAct ions.TYPE_CREATE) .data(TodoAct ions.KEY_TEXT, todo); }
publ ic Act ion c lear() { return Act ion.wi th(TodoAct ions.TYPE_CLEAR); }}
ActionCreator
publ ic c lass TodoAct ionCreator { publ ic Act ion create(Str ing todo) { return Act ion.wi th(TodoAct ions.TYPE_CREATE) .data(TodoAct ions.KEY_TEXT, todo); }
publ ic Act ion c lear() { return Act ion.wi th(TodoAct ions.TYPE_CLEAR); }}
ActionCreator
Action 생성만 수행
publ ic c lass SimpleStore<S extends State> implements Store<S> { pr ivate S in i t ia lState; pr ivate Reducer<S> reducer;
pr ivate SimpleStore() { act ionsSubject.map(act ion -> reducer.reduce( in i t ia lState, act ion)) .doOnNext(s -> in i t ia lState = s) .subscr ibe(s -> statesSubject .onNext(s) , Throwable: :pr intStackTrace); }
@Overr ide publ ic void dispatch(Act ion act ion) { act ionsSubject.onNext(act ion); }
@Overr ide publ ic Observable<S> asObservable() { return statesSubject ; }}
Store
publ ic c lass SimpleStore<S extends State> implements Store<S> { pr ivate S in i t ia lState; pr ivate Reducer<S> reducer;
pr ivate SimpleStore() { act ionsSubject.map(act ion -> reducer.reduce( in i t ia lState, act ion)) .doOnNext(s -> in i t ia lState = s) .subscr ibe(s -> statesSubject .onNext(s) , Throwable: :pr intStackTrace); }
@Overr ide publ ic void dispatch(Act ion act ion) { act ionsSubject.onNext(act ion); }
@Overr ide publ ic Observable<S> asObservable() { return statesSubject ; }}
Store
publ ic c lass SimpleStore<S extends State> implements Store<S> { pr ivate S in i t ia lState; pr ivate Reducer<S> reducer;
pr ivate SimpleStore() { act ionsSubject.map(act ion -> reducer.reduce( in i t ia lState, act ion)) .doOnNext(s -> in i t ia lState = s) .subscr ibe(s -> statesSubject .onNext(s) , Throwable: :pr intStackTrace); }
@Overr ide publ ic void dispatch(Act ion act ion) { act ionsSubject.onNext(act ion); }
@Overr ide publ ic Observable<S> asObservable() { return statesSubject ; }}
Store
initialState로부터 newState 생성
publ ic c lass TodoReducer implements Reducer<TodoState> {
@Overr ide publ ic TodoState reduce(TodoState oldState, Act ion act ion) { switch (act ion.getType()) { case TodoAct ions.TYPE_CREATE: TodoState newState = new TodoState(oldState); Str ing text = (Str ing) act ion.getData() .get(TodoAct ions.KEY_TEXT); newState.getTodos() .add(new Todo(text)) ; return newState; case TodoAct ions.TYPE_CLEAR: return new TodoState() ; defaul t : return oldState; } }}
Reducer
publ ic c lass TodoReducer implements Reducer<TodoState> {
@Overr ide publ ic TodoState reduce(TodoState oldState, Act ion act ion) { switch (act ion.getType()) { case TodoAct ions.TYPE_CREATE: TodoState newState = new TodoState(oldState); Str ing text = (Str ing) act ion.getData() .get(TodoAct ions.KEY_TEXT); newState.getTodos() .add(new Todo(text)) ; return newState; case TodoAct ions.TYPE_CLEAR: return new TodoState() ; defaul t : return oldState; } }}
Reducer
Reducer는 하나의 함수
publ ic c lass TodoReducer implements Reducer<TodoState> {
@Overr ide publ ic TodoState reduce(TodoState oldState, Act ion act ion) { switch (act ion.getType()) { case TodoAct ions.TYPE_CREATE: TodoState newState = new TodoState(oldState); Str ing text = (Str ing) act ion.getData() .get(TodoAct ions.KEY_TEXT); newState.getTodos() .add(new Todo(text)) ; return newState; case TodoAct ions.TYPE_CLEAR: return new TodoState() ; defaul t : return oldState; } }}
Reducer
publ ic c lass TodoReducer implements Reducer<TodoState> {
@Overr ide publ ic TodoState reduce(TodoState oldState, Act ion act ion) { switch (act ion.getType()) { case TodoAct ions.TYPE_CREATE: TodoState newState = new TodoState(oldState); Str ing text = (Str ing) act ion.getData() .get(TodoAct ions.KEY_TEXT); newState.getTodos() .add(new Todo(text)) ; return newState; case TodoAct ions.TYPE_CLEAR: return new TodoState() ; defaul t : return oldState; } }}
Reducer
비지니스 로직 수행( init ialState로부터
newState 생성 )
publ ic c lass SimpleStore<S extends State> implements Store<S> { pr ivate S in i t ia lState; pr ivate Reducer<S> reducer;
pr ivate SimpleStore() { act ionsSubject.map(act ion -> reducer.reduce( in i t ia lState, act ion)) .doOnNext(s -> in i t ia lState = s) .subscr ibe(s -> statesSubject .onNext(s) , Throwable: :pr intStackTrace); }
@Overr ide publ ic void dispatch(Act ion act ion) { act ionsSubject.onNext(act ion); }
@Overr ide publ ic Observable<S> asObservable() { return statesSubject ; }}
Store
pr ivate void subscr ibeStore() { store.asObservable() .observeOn(AndroidSchedulers.mainThread()) .subscr ibe(this: :updateUI) ;}
pr ivate void updateUI(TodoState state) { adapter.set I tems(state.getTodos()) ;}
View
pr ivate void subscr ibeStore() { store.asObservable() .observeOn(AndroidSchedulers.mainThread()) .subscr ibe(this: :updateUI) ;}
pr ivate void updateUI(TodoState state) { adapter.set I tems(state.getTodos()) ;}
View
Store의 State 참조
Redux 정리
Redux 정리- View -> ActionCreator -> Action -> Reducer -> Store -> View
Redux 정리- View -> ActionCreator -> Action -> Reducer -> Store -> View- 3원칙: 단일 Store, Immutable State, Functional Reducer- View에서 직접 Store dispatch를 실행- Single Store- Reducer가 비지니스 로직을 실행한다.- Immutable State- Reducer 앞에 별도기능 Middleware 추가 가능- 필요할 경우 Middleware가 Repository 역할을 한다.
Flux Redux
Dispatcher 있음
Multiple Store
Business logic in Store
Mutable State
Dispatcher 없음
Single Store
Business logic in Reducer
Immutable State
MVI
Seoul
출처: http://hannesdorfmann.com/android/model-view-intent
MVIHannes Dorfmann이 만든 Android UDA
pr ivate Publ ishSubject<MviIntent> addIntent = Publ ishSubject .create() ;pr ivate Publ ishSubject<MviIntent> c learIntent = Publ ishSubject .create() ;
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { addIntent.onNext(Mvi Intent.wi th(TodoAct ions.TYPE_CREATE) .data(TodoAct ions.KEY_TEXT, inputTodo.getText() . toStr ing())) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> c learIntent.onNext(Mvi Intent.wi th(TodoAct ions.TYPE_CLEAR))) ;}
pr ivate void in i t ia l izeIntent() { presenter.processIntents( intents()) ;}
publ ic Observable<MviIntent> intents() { return Observable.merge(addIntent, c learIntent) ;}
View
pr ivate Publ ishSubject<MviIntent> addIntent = Publ ishSubject .create() ;pr ivate Publ ishSubject<MviIntent> c learIntent = Publ ishSubject .create() ;
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { addIntent.onNext(Mvi Intent.wi th(TodoAct ions.TYPE_CREATE) .data(TodoAct ions.KEY_TEXT, inputTodo.getText() . toStr ing())) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> c learIntent.onNext(Mvi Intent.wi th(TodoAct ions.TYPE_CLEAR))) ;}
pr ivate void in i t ia l izeIntent() { presenter.processIntents( intents()) ;}
publ ic Observable<MviIntent> intents() { return Observable.merge(addIntent, c learIntent) ;}
View
Intent 보내기
pr ivate Publ ishSubject<MviIntent> addIntent = Publ ishSubject .create() ;pr ivate Publ ishSubject<MviIntent> c learIntent = Publ ishSubject .create() ;
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { addIntent.onNext(Mvi Intent.wi th(TodoAct ions.TYPE_CREATE) .data(TodoAct ions.KEY_TEXT, inputTodo.getText() . toStr ing())) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> c learIntent.onNext(Mvi Intent.wi th(TodoAct ions.TYPE_CLEAR))) ;}
pr ivate void in i t ia l izeIntent() { presenter.processIntents( intents()) ;}
publ ic Observable<MviIntent> intents() { return Observable.merge(addIntent, c learIntent) ;}
View
Intent 처리
publ ic c lass MviPresenter<S extends State> { pr ivate S in i t ia lState; pr ivate MviReducer<S> reducer;
pr ivate BehaviorSubject<S> stateSubject = BehaviorSubject .create() ;
publ ic void processIntents(Observable<MviIntent> intents) { intents .scan( in i t ia lState, (s, intent) -> reducer.reduce(s, intent)) .doOnNext(state -> in i t ia lState = state) .subscr ibe(stateSubject) ; }
publ ic Observable<S> states() { return stateSubject ; }}
Presenter
publ ic c lass MviPresenter<S extends State> { pr ivate S in i t ia lState; pr ivate MviReducer<S> reducer;
pr ivate BehaviorSubject<S> stateSubject = BehaviorSubject .create() ;
publ ic void processIntents(Observable<MviIntent> intents) { intents .scan( in i t ia lState, (s, intent) -> reducer.reduce(s, intent)) .doOnNext(state -> in i t ia lState = state) .subscr ibe(stateSubject) ; }
publ ic Observable<S> states() { return stateSubject ; }}
Presenter
비지니스 로직 수행( init ialState로부터
newState 생성 )
Intent 처리
publ ic c lass MviPresenter<S extends State> { pr ivate S in i t ia lState; pr ivate MviReducer<S> reducer;
pr ivate BehaviorSubject<S> stateSubject = BehaviorSubject .create() ;
publ ic void processIntents(Observable<MviIntent> intents) { intents .scan( in i t ia lState, (s, intent) -> reducer.reduce(s, intent)) .doOnNext(state -> in i t ia lState = state) .subscr ibe(stateSubject) ; }
publ ic Observable<S> states() { return stateSubject ; }}
Presenter
pr ivate void in i t ia l izeIntent() { presenter.states() .subscr ibe(this: : render) ;}
@Overr idepubl ic void render(TodoState state) { adapter.set I tems(state.getTodos()) ;}
View
pr ivate void in i t ia l izeIntent() { presenter.states() .subscr ibe(this: : render) ;}
@Overr idepubl ic void render(TodoState state) { adapter.set I tems(state.getTodos()) ;}
View
Store의 State 참조
MVI 정리
MVI 정리- View -> Intent -> Model -> View
MVI 정리- View -> Intent -> Model -> View- Intent는 Presenter 등에 있다.- Multiple Store (Presenter)- Reducer가 비지니스 로직을 실행한다.- Immutable State
Flux Redux
Action 명시
Dispatcher 있음
Multiple Store
Business logic in Store
Mutable State
Action 명시
Dispatcher 없음
Single Store
Business logic in Reducer
Immutable State
MVI
Action 없음
Dispatcher 없음
Multiple Store(Presenter)
Business logic in Reducer
Immutable State
UDA 정리
Seoul
- View에 영향을 주는 State는 한 방향으로만 수정할 수 있다. 단방향- 앞의 액션이 끝나지 않으면 뒤 액션을 실행하지 않는다. 동기적 실행- Model은 State를 변화시키고, View는 State를 참조만 한다. View와 State 분리
Uni-Directional Architecture (UDA) Core 복습
UDA 장점
UDA 장점단방향, View와 State 분리, State 저장소 일원화 등으로 State 관리에 이점을 갖는다.
UDA 장점단방향, View와 State 분리, State 저장소 일원화 등으로 State 관리에 이점을 갖는다.
-여러 입력(다른화면,Service 등)으로 일어나는 State 변화를 한곳에서 쉽게 관리할 수 있다-State 변경지점이 명확하여 디버깅이 쉽다
UDA 장점
어느 경우에 UDA를 사용할까?
단방향, View와 State 분리, State 저장소 일원화 등으로 State 관리에 이점을 갖는다.
-여러 입력(다른화면,Service 등)으로 일어나는 State 변화를 한곳에서 쉽게 관리할 수 있다-State 변경지점이 명확하여 디버깅이 쉽다
UDA 장점
다양한 Input으로(입력종류,비동기 등) 변화가 일어나는 복잡한 State에 대해 사용
어느 경우에 UDA를 사용할까?
단방향, View와 State 분리, State 저장소 일원화 등으로 State 관리에 이점을 갖는다.
-여러 입력(다른화면,Service 등)으로 일어나는 State 변화를 한곳에서 쉽게 관리할 수 있다-State 변경지점이 명확하여 디버깅이 쉽다
UDA 장점
다양한 Input으로(입력종류,비동기 등) 변화가 일어나는 복잡한 State에 대해 사용
-복잡한 State가 없으면 사용하지 않는 것이 좋다.-복잡한 State에 대해서만 부분적으로 사용할 수 있다.
어느 경우에 UDA를 사용할까?
단방향, View와 State 분리, State 저장소 일원화 등으로 State 관리에 이점을 갖는다.
-여러 입력(다른화면,Service 등)으로 일어나는 State 변화를 한곳에서 쉽게 관리할 수 있다-State 변경지점이 명확하여 디버깅이 쉽다
간단 응용
Seoul
UDA 개념을 MVP에서 간단하게 응용해보자
데이터 흐름을 interface에서 Store로 변경
UDA 개념을 MVP에서 간단하게 응용해보자
데이터 흐름을 interface에서 Store로 변경1. Presenter 로직 후 Store로 dispatch 한다.2. View에서 Store를 구독한다.
UDA 개념을 MVP에서 간단하게 응용해보자
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { presenter.create( inputTodo.getText() . toStr ing()) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> presenter.c lear()) ;}
View
pr ivate void setButton() { btnAdd.setOnCl ickListener(v -> { presenter.create( inputTodo.getText() . toStr ing()) ; inputTodo.setText("") ; } ) ; btnClear.setOnCl ickListener(v -> presenter.c lear()) ;}
View
publ ic c lass TodoPresenter { pr ivate TodoReposi tory reposi tory = new TodoReposi tory() ; pr ivate TodoStore store = TodoStore.get() ;
publ ic void create(Str ing text) { Todo todo = reposi tory.create(text) ; store.create(todo); }
publ ic void c lear() { reposi tory.c lear() ; store.c lear() ; }}
Presenter
publ ic c lass TodoPresenter { pr ivate TodoReposi tory reposi tory = new TodoReposi tory() ; pr ivate TodoStore store = TodoStore.get() ;
publ ic void create(Str ing text) { Todo todo = reposi tory.create(text) ; store.create(todo); }
publ ic void c lear() { reposi tory.c lear() ; store.c lear() ; }}
Presenter
비지니스 로직 후Store로 Dispatch
publ ic c lass TodoStore { pr ivate List<Todo> todos = new ArrayList<>() ; pr ivate BehaviorSubject<List<Todo>> stateSubject = BehaviorSubject .create() ;
publ ic void create(Todo todo) { todos.add(todo); stateSubject .onNext( todos); }
publ ic void c lear() { todos.clear() ; stateSubject .onNext( todos); }}
Store
publ ic c lass TodoStore { pr ivate List<Todo> todos = new ArrayList<>() ; pr ivate BehaviorSubject<List<Todo>> stateSubject = BehaviorSubject .create() ;
publ ic void create(Todo todo) { todos.add(todo); stateSubject .onNext( todos); }
publ ic void c lear() { todos.clear() ; stateSubject .onNext( todos); }}
Store
State 변경 및뷰 갱신 알림
pr ivate void subscr ibeStore() { store.states() .subscr ibe(todos -> adapter.set I tems(todos)) ;}
View
Store의 State 참조
UDA 개념을 MVP에서 간단하게 응용해보자
큰 변경 없이 MVP에 UDA를 부분적으로 적용하였다!
데이터 흐름을 interface에서 Store로 변경1. Presenter 로직 후 Store로 dispatch 한다.2. View에서 Store를 구독한다.
마무리
Seoul
Flux, Redux, MVI 잊어버려도 괜찮아요
- View에 영향을 주는 State는 한 방향으로만 수정할 수 있다. 단방향- 앞의 액션이 끝나지 않으면 뒤 액션을 실행하지 않는다. 동기적 실행- Model은 State를 변화시키고, View는 State를 참조만 한다. View와 State 분리
Uni-Directional Architecture (UDA) Core 복습!!!
UDA 아이디어를 통해 여러분의 앱을
더 쉽게 안정적으로 관리해보세요!
Flux: http://facebook.github.io/fluxRedux: https://redux.js.org역시 Redux: https://www.slideshare.net/dalinaum/redux-55650128
MVI: http://hannesdorfmann.com/android/model-view-intentUDA: https://academy.realm.io/kr/posts/eric-maxwell-uni-directional-architecture-android-using-realmSample Code: https://github.com/maryangmin/GDG-Flux-Redux-MVI
Reference
감사합니다!
이승민, GDE Android Korea@maryangmin
Seoul
Q&A
Seoul