reactive model-view-viewmodel architecture
Post on 14-Jul-2015
3.116 Views
Preview:
TRANSCRIPT
Reactive Model-View-ViewModel모바일응용프로그램아키텍쳐
이규원
https://www.facebook.com/gyuwon.yi
@styletigger
MVVMModel-View-ViewModel
MVVM(Model-View-ViewModel)
• In 2005, John Gossman(Microsoft)
• Separation of Presentation and Presentation Logic
• Two-way Binding
• Properties and Commands
• Testability
• WPF, Silverlight, Xamarin Forms, AngularJS, EmberJS, KnockoutJS, RoboBinding
• Introduction to Model/View/ViewModel pattern for building WPF apps
• WPF Apps With The Model-View-ViewModel Design Pattern
ViewModel
Presentation Logic
View
User Interface
Model
Business Logic and Data
2-way Binding
MVVM 응용프로그램
ViewModel
Presentation Logic
View
User Interface
API
Service
2-way Binding
Model
Server Client
서비스클라이언트응용프로그램
상태동기화State Synchronization
Showcase …
Showcase 15
no envy
Showcase …
…
… …
Showcase 15
no envy
Profile
Showcase 15
no envy
Showcase …
…
New Feed Explore Notifications
단일컨텐트에대한다중뷰
Showcase …
Showcase 15
1 envy
Showcase …
…
… …
Showcase 15
1 envy
New Feed Explore Notifications
Profile
Showcase 15
1 envy
Showcase …
…
뷰상태동기화
동기화흐름Flow of Synchronization
Showcase …
Showcase 15
1 envy
Showcase …
…
… …
Showcase 15
1 envy
New Feed Explore Notifications
Profile
Showcase 15
1 envy
Showcase …
…
뷰모델사이의데이터흐름
VM
VM VM
VM VM
VM
뷰모델사이의데이터흐름
ViewModelRepository
One-way
Push
Publish
One-way
단방향데이터흐름
Scale
Complexity
상태동기화복잡도
불변모델개체Immutable Model Objects
ShowcaseViewModel
IsEnvied: bool
EnvyCount: int
뷰모델속성을통한데이터노출
<Button BackgroundColor="{Binding IsEnvied, Converter={...}}" />
<Label Text="{Binding EnvyCount, StringFormat='...'}" />
뷰모델속성바인딩
IsEnvied EnvyCount PropertyChanged Validity
False 0 Valid
True 0 "IsEnvied" Invalid
True 1 "EnvyCount" Valid
뷰모델속성상태변경
뷰에바인딩된속성에대한 PropertyChanged 이벤트는레이아웃계산과그리기작업을촉발한다
모델속성을통한데이터노출
ShowcaseViewModel ShowcaseModel
IsEnvied: bool
EnvyCount: int
Model
<Button BackgroundColor="{Binding Model.IsEnvied, Converter={...}}" />
<Label Text="{Binding Model.EnvyCount, StringFormat='...'}" />
모델속성바인딩
Model PropertyChanged Validity
{ IsEnvied: False, EnvyCount: 0 } Valid
{ IsEnvied: True, EnvyCount: 1 } "Model" Valid
모델상태변경
단, 모델개체는불변성을가져야한다
Immutable Objects
• Cannot be modified after initialization
• Thread-safe
• Readability
• Runtime efficiency
• Functional programming
모델속성을통한데이터노출
ShowcaseViewModel ShowcaseModel<< immutable >>
IsEnvied: bool
EnvyCount: int
Model
불변모델반응형스트림Reactive Streams of Immutable Model Objects
데이터흐름
•컨텐트상태는불변성을가지는모델개체로캡슐화
•뷰모델은모델개체를속성으로뷰에노출
•컨텐트상태가변경되면새로운모델인스턴스발행
‘불변성을가진모델개체의반응형스트림’
…Model
Revision n + 2Model
Revision n + 1Model
Revision n…
스트림저장소
Rx(Reactive Extensions)
• Observables + LINQ + Schedulers
• Asynchronous and event-based programming
• C#, JavaScript, C, C++, Ruby, Python, Java
• Microsoft Open Tech(*RxJava by Netflix)
• https://rx.codeplex.com/
반응형스트림
public interface IObservable<out T>
{
IDisposable Subscribe(IObserver<T> observer);
}
public interface IObserver<in T>
{
void OnCompleted();
void OnError(Exception error);
void OnNext(T value);
}
모델
public abstract class Model<TModel, TId>
where TModel : Model<TModel, TId>
where TId : IEquatable<TId>
{
private readonly TId _id;
public TId Id { get { return _id; } }
protected Model(TId id)
{
_id = id;
}
}
모델
public sealed class ShowcaseModel : Model<ShowcaseModel, long>
{
private readonly bool _isEnvied;
private readonly int _envyCount;
public ShowcaseModel(long id, bool isEnvied, int envyCount) : base(id)
{
_isEnvied = isEnvied;
_envyCount = envyCount;
}
public bool IsEnvied { get { return _isEnvied; } }
public int EnvyCount { get { return _envyCount; } }
}
스트림저장소
public static class StreamStore<TModel, TId>
where TModel : Model<TModel, TId>
where TId: IEquatable<TId>
{
public static IObservable<TModel> GetStream(TId id);
public static void Push(TModel model);
}
모델-뷰모델
public abstract class ModelViewModel<TModel, TId> : ViewModel
{
private TModel _model = null;
public TModel Model
{
get { return _model;}
set { SetProperty(ref _model, value); }
}
protected virtual void OnNext(TModel next)
{
Model = next;
}
}
스트림구독Stream Subscription
약한스트림구독
ModelViewModel
Observer WeakSubscription
Stream
Reference
Weak Reference
약한스트림구독
public abstract class ModelViewModel<TModel, TId> : ViewModel
{
private readonly TId _id;
private readonly IObserver<TModel> _observer;
private readonly IDisposable _subscription;
protected ModelViewModel(TId id)
{
_id = id;
var stream = StreamStore<TModel, TId>.GetStream(id);
_observer = Observer.Create<TModel>(onNext: OnNext);
_subscription = new WeakSubscription<TModel>(stream, _observer);
}
~ModelViewModel() { _subscription.Dispose(); }
}
스위치연산자Switch Operator
비동기데이터조회
•응용프로그램반응성향상
• Futures Pattern
중복된비동기데이터조회
•불필요한무효화(invalidation)
•시작과종료순서의불일치
Switch()
“Transforms an observable sequence of observable sequences into an observable sequence producing values only from the most recent observable sequence.”
- from MSDN
스위치
스위치
public abstract class ModelViewModel<TModel, TId> : ViewModel
{
private readonly Subject<IObservable<TModel>> _spout = new Subject<IObservable<TModel>>();
protected ModelViewModel(TId id)
{
_spout.Switch()
.Subscribe(next => StreamStore<TModel, TId>.Push(next));
}
protected virtual void Push(IObservable<TModel> next)
{
_spout.OnNext(next);
}
}
Task to Observable
public abstract class ModelViewModel<TModel, TId> : ViewModel
{
protected void Push(Task<TModel> next)
{
Push(next.ToObservable());
}
protected void Push(Func<Task<TModel>> next)
{
Push(next.Invoke().ToObservable());
}
}
Model to Observable
public abstract class ModelViewModel<TModel, TId> : ViewModel
{
protected void Push(TModel next)
{
Push(Task.FromResult(next).ToObservable());
}
}
병합연산자Coalescing Operator
UserModel
Id : "user1"
UserName
ProfilePhotoUri
UserModel
Id: "user1"
UserName
ProfilePhotoUri
FolloweeCount
FollowerCount
Followees: 10
Followers: 24
목록조회와상세정보조회
데이터유실
UserModel
Id : "user1"
UserName
ProfilePhotoUri
UserModel
Id: "user1"
UserName
ProfilePhotoUri
FolloweeCount
FollowerCount
Followees: 10
Followers: 24
UserModel
Id: "user1"
UserName
ProfilePhotoUri
FolloweeCount
FollowerCount
Followees: 10
Followers: 24
Push User Detail Page Push User List Page Pop
Id: "user1" UserName ProfilePhotoUri FolloweeCount FollowerCount
Id: "user1" UserName ProfilePhotoUri FolloweeCount FollowerCount
Id: "user1" UserName ProfilePhotoUri FolloweeCount FollowerCount
Coalesce
=
병합연산
병합연산
public static class ModelExtensions
{
public static UserModel Coalesce(this UserModel user, UserModel other)
{
if (user == null) throw new ArgumentNullException("user");
if (other == null) return user;
if (other.Id != user.Id) throw new ArgumentException();
if (user.Equals(other)) return user;
return new UserModel(user.Id,
user.UserName,
user.ProfilePhotoUri,
user.FolloweeCount ?? other.FolloweeCount,
user.FollowerCount ?? other.FollowerCount);
}
}
병합연산
public class UserViewModel : ModelViewModel<UserModel, string>
{
protected override void OnNext(UserModel next)
{
var current = Model;
base.OnNext(next.Coalesce(current));
}
}
같음확인Equality Comparisons
뷰모델속성설정
public class ViewModel : INotifyPropertyChanged
{
protected bool SetProperty<T>(ref T field,
T value,
[CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
NotifyPropertyChanged(propertyName);
return true;
}
}
Changed!Changed…?
ShowcaseId: 15
IsEnvied: trueEnvyCount: 10
ShowcaseId: 15
IsEnvied: trueEnvyCount: 10
ShowcaseId: 15
IsEnvied: falseEnvyCount: 9
Changed…?
개체참조비교
같음확인논리
public sealed class ShowcaseModel : Model<ShowcaseModel, long>, IEquatable<ShowcaseModel>
{
public bool Equals(ShowcaseModel other)
{
if (other == null)
return false;
if (object.ReferenceEquals(this, other))
return true;
return Id == other.Id &&
_isEnvied == other._isEnvied &&
_envyCount == other._envyCount;
}
}
Changed!Changed…?
ShowcaseId: 15
IsEnvied: trueEnvyCount: 10
ShowcaseId: 15
IsEnvied: trueEnvyCount: 10
ShowcaseId: 15
IsEnvied: falseEnvyCount: 9
Not Changed
모델데이터비교
정리
Reactive MVVM Architecture
•단방향데이터흐름
•불변모델
•반응형스트림
•약한스트림구독
•스위치연산
•병합연산
•같음확인
Reactive MVVM Architecture
ViewModel Model<< 1-way binding >>
Model<< push >>
Co
alescin
g
Equ
ality C
om
pariso
n
Stream Store
Switch
We
ak Su
bscrip
tion
View
Commands,Entry Fields
and UI States
Model<< publish >>
Reactive MVVM 아키텍쳐를적용한안드로이드앱을함께개발하실멋쟁이
프로그래머느님을애타게찾고있습니다.
public async Task<IEnvicase> CreateAndroidAsync(){
var team = await JoinUsAsync(new You());return team.CreateAndroidApp();
}
https://www.facebook.com/gyuwon.yi@styletigger
gyuwon@tidcorp.com
감사합니다
top related