From d427d2a87effdf77f4604d54513cd42a550fadea Mon Sep 17 00:00:00 2001 From: Stefan Glienke Date: Fri, 26 Jun 2015 13:14:27 +0200 Subject: [PATCH] changed IObservable to (an overloaded!) anonymous method type --- MainViewModel.pas | 38 +++++++++--- ObservableTests.pas | 46 +++++++------- SimpleMVVM.Binding.pas | 2 +- SimpleMVVM.Observable.pas | 125 ++++++++++++++++++-------------------- TournamentViewModel.pas | 14 ++--- 5 files changed, 119 insertions(+), 106 deletions(-) diff --git a/MainViewModel.pas b/MainViewModel.pas index 9fde9e2..0845234 100644 --- a/MainViewModel.pas +++ b/MainViewModel.pas @@ -29,9 +29,12 @@ TViewModel = class(TComponent) fTickets: TList; fAvailableCountries: TArray; fCountry: IObservable; + fActive: IObservable; function GetLastName: string; procedure SetLastName(const value: string); + function GetActive: Boolean; + procedure SetActive(const value: Boolean); public constructor Create(const firstName, lastName: string); reintroduce; destructor Destroy; override; @@ -53,6 +56,8 @@ TViewModel = class(TComponent) property AvailableCountries: TArray read fAvailableCountries; property Country: IObservable read fCountry; + + property Active: Boolean read GetActive write SetActive; end; implementation @@ -77,19 +82,19 @@ constructor TViewModel.Create(const firstName, lastName: string); fFullName := TDependentObservable.Create( function: string begin - Result := fFirstName.Value + ' ' + fLastName.Value; + Result := fFirstName + ' ' + fLastName; end); // Example 2 - fNumberOfClicks := TObservable.Create; + fNumberOfClicks := TObservable.Create(); fHasClickedTooManyTimes := TDependentObservable.Create( function: Boolean begin - Result := fNumberOfClicks.Value >= 3; + Result := fNumberOfClicks >= 3; end); // Example 3 - fChosenTicket := TObservable.Create; + fChosenTicket := TObservable.Create(); fTickets := TObjectList.Create; fTickets.AddRange([ TTicket.Create('Economy', 199.95), @@ -98,7 +103,10 @@ constructor TViewModel.Create(const firstName, lastName: string); // Example 4 fAvailableCountries := TArray.Create('AU', 'NZ', 'US'); - fCountry := TObservable.Create; + fCountry := TObservable.Create(); + + // Example 5; + fActive := TObservable.Create(True); end; destructor TViewModel.Destroy; @@ -107,29 +115,39 @@ destructor TViewModel.Destroy; inherited; end; +function TViewModel.GetActive: Boolean; +begin + Result := fActive; +end; + function TViewModel.GetLastName: string; begin - Result := fLastName.Value; + Result := fLastName; end; procedure TViewModel.RegisterClick; begin - fNumberOfClicks.Value := fNumberOfClicks.Value + 1; + fNumberOfClicks(fNumberOfClicks + 1); end; procedure TViewModel.ResetClicks; begin - fNumberOfClicks.Value := 0; + fNumberOfClicks(0); end; procedure TViewModel.ResetTicket; begin - fChosenTicket.Value := nil; + fChosenTicket(nil); +end; + +procedure TViewModel.SetActive(const value: Boolean); +begin + fActive(value); end; procedure TViewModel.SetLastName(const value: string); begin - fLastName.Value := value; + fLastName(value); end; end. diff --git a/ObservableTests.pas b/ObservableTests.pas index 75d14d5..2b21df2 100644 --- a/ObservableTests.pas +++ b/ObservableTests.pas @@ -40,23 +40,23 @@ procedure TObservableTests.DependentObservableClearsOldDependencies; function: string begin Inc(count); - if a.Value then - Result := b.Value + if a then + Result := b else - Result := c.Value + Result := c end); count := 0; - CheckEquals('false', o.Value); + CheckEquals('false', o); CheckEquals(0, count); - b.Value := 'TRUE'; + b('TRUE'); CheckEquals(0, count); - c.Value := 'FALSE'; - CheckEquals('FALSE', o.Value); + c('FALSE'); + CheckEquals('FALSE', o); CheckEquals(1, count); - a.Value := True; - CheckEquals('TRUE', o.Value); + a(True); + CheckEquals('TRUE', o); CheckEquals(2, count); - c.Value := 'false'; + c('false'); CheckEquals(2, count); end; @@ -67,8 +67,8 @@ procedure TObservableTests.DependentObservableEvaluatesOnlyOnceAfterChange; begin count := 0; o := TDependentObservable.Create(function: string begin Inc(count); Result := 'test' end); - CheckEquals('test', o.Value); - CheckEquals('test', o.Value); + CheckEquals('test', o); + CheckEquals('test', o); CheckEquals(1, count); end; @@ -77,7 +77,7 @@ procedure TObservableTests.DependentObservableReturnsValue; o: IObservable; begin o := TDependentObservable.Create(function: string begin Result := 'test' end); - CheckEquals('test', o.Value); + CheckEquals('test', o); end; procedure TObservableTests.DependentObservableUpdatesValueWhenDependencyChanges; @@ -85,13 +85,13 @@ procedure TObservableTests.DependentObservableUpdatesValueWhenDependencyChanges; o1, o2: IObservable; called: Boolean; begin - o1 := TObservable.Create; - o2 := TDependentObservable.Create(function: string begin Result := o1.Value; called := True; end); - CheckEquals('', o2.Value); + o1 := TObservable.Create(); + o2 := TDependentObservable.Create(function: string begin Result := o1; called := True; end); + CheckEquals('', o2); called := False; - o1.Value := 'test'; + o1('test'); Check(called); - CheckEquals('test', o2.Value); + CheckEquals('test', o2); end; procedure TObservableTests.ObservableReturnsValue; @@ -99,17 +99,17 @@ procedure TObservableTests.ObservableReturnsValue; o: IObservable; begin o := TObservable.Create('test'); - CheckEquals('test', o.Value); + CheckEquals('test', o); end; procedure TObservableTests.ObservableSetValueChangesValue; var o: IObservable; begin - o := TObservable.Create; - CheckEquals('', o.Value); - o.Value := 'test'; - CheckEquals('test', o.Value); + o := TObservable.Create(); + CheckEquals('', o); + o('test'); + CheckEquals('test', o); end; initialization diff --git a/SimpleMVVM.Binding.pas b/SimpleMVVM.Binding.pas index a38a5a4..9166d08 100644 --- a/SimpleMVVM.Binding.pas +++ b/SimpleMVVM.Binding.pas @@ -164,7 +164,7 @@ function CreateObservable(instance: TObject; if StartsText('IObservable<', prop.PropertyType.Name) then begin Result := prop.GetValue(instance).AsInterface as IObservable; - typ := prop.PropertyType.GetMethod('GetValue').ReturnType; + typ := prop.PropertyType.BaseType.GetMethod('Invoke').ReturnType; end else begin diff --git a/SimpleMVVM.Observable.pas b/SimpleMVVM.Observable.pas index 9cb859c..f120b64 100644 --- a/SimpleMVVM.Observable.pas +++ b/SimpleMVVM.Observable.pas @@ -25,32 +25,27 @@ interface function GetValue: TValue; procedure SetValue(const value: TValue); {$ENDREGION} -// procedure Subscribe(const action: TAction); -// procedure Unsubscribe(const action: TAction); property Value: TValue read GetValue write SetValue; end; - IObservable = interface(IInvokable) + {$M+} + IReadOnlyObservable = reference to function: T; + IObservable = interface(IReadOnlyObservable) {$REGION 'Property Accessors'} - function GetValue: T; - procedure SetValue(const value: T); + procedure Invoke(const value: T); overload; {$ENDREGION} -// procedure Subscribe(const action: TAction); -// procedure Unsubscribe(const action: TAction); - property Value: T read GetValue write SetValue; end; + {$M-} TObservableBase = class(TInterfacedObject, IObservable) private fDependencies: TList; fSubscribers: TList; {$REGION 'Property Accessors'} - function GetValueNonGeneric: TValue; virtual; abstract; - procedure SetValueNonGeneric(const value: TValue); virtual; abstract; - function IObservable.GetValue = GetValueNonGeneric; - procedure IObservable.SetValue = SetValueNonGeneric; + function GetValue: TValue; virtual; abstract; + procedure SetValue(const value: TValue); virtual; abstract; {$ENDREGION} - strict protected + protected class var ObservableStack: TStack; constructor Create; procedure ClearDependencies; @@ -69,8 +64,8 @@ TObservable = class(TObservableBase) fGetter: TFunc; fSetter: TAction; {$REGION 'Property Accessors'} - function GetValueNonGeneric: TValue; override; final; - procedure SetValueNonGeneric(const value: TValue); override; final; + function GetValue: TValue; override; final; + procedure SetValue(const value: TValue); override; final; {$ENDREGION} public constructor Create(const getter: TFunc); overload; @@ -85,8 +80,8 @@ TDependentObservable = class(TObservableBase) fIsNotifying: Boolean; fNeedsEvaluation: Boolean; {$REGION 'Property Accessors'} - function GetValueNonGeneric: TValue; override; final; - procedure SetValueNonGeneric(const value: TValue); override; final; + function GetValue: TValue; override; final; + procedure SetValue(const value: TValue); override; final; {$ENDREGION} procedure Evaluate; protected @@ -102,10 +97,10 @@ TObservable = class(TObservableBase, IObservable) class var Comparer: IEqualityComparer; class constructor Create; {$REGION 'Property Accessors'} - function GetValueNonGeneric: TValue; override; final; - procedure SetValueNonGeneric(const value: TValue); override; final; - function GetValue: T; - procedure SetValue(const value: T); + function GetValue: TValue; override; final; + procedure SetValue(const value: TValue); override; final; + function Invoke: T; overload; + procedure Invoke(const value: T); overload; {$ENDREGION} public constructor Create; overload; @@ -120,10 +115,10 @@ TDependentObservable = class(TObservableBase, IObservable) fIsNotifying: Boolean; fNeedsEvaluation: Boolean; {$REGION 'Property Accessors'} - function GetValueNonGeneric: TValue; override; final; - procedure SetValueNonGeneric(const value: TValue); override; final; - function GetValue: T; - procedure SetValue(const value: T); + function GetValue: TValue; override; final; + procedure SetValue(const value: TValue); override; final; + function Invoke: T; overload; + procedure Invoke(const value: T); overload; {$ENDREGION} procedure Evaluate; protected @@ -212,19 +207,19 @@ destructor TObservableBase.Destroy; procedure TObservableBase.ClearDependencies; var - dependency: TObservableBase; + i: Integer; begin - for dependency in fDependencies do - dependency.fSubscribers.Remove(Self); + for i := fDependencies.Count - 1 downto 0 do + fDependencies[i].fSubscribers.Remove(Self); fDependencies.Clear; end; procedure TObservableBase.Notify; var - observable: TObservableBase; + i: Integer; begin - for observable in fSubscribers do - observable.Notify; + for i := 0 to fSubscribers.Count - 1 do + fSubscribers[i].Notify; end; procedure TObservableBase.RegisterDependency; @@ -259,13 +254,13 @@ constructor TObservable.Create(const getter: TFunc; fSetter := setter; end; -function TObservable.GetValueNonGeneric: TValue; +function TObservable.GetValue: TValue; begin RegisterDependency; Result := fGetter; end; -procedure TObservable.SetValueNonGeneric(const value: TValue); +procedure TObservable.SetValue(const value: TValue); begin fSetter(value); Notify; @@ -291,12 +286,6 @@ constructor TDependentObservable.Create(const getter: TFunc; Evaluate; end; -procedure TDependentObservable.Notify; -begin - Evaluate; - inherited; -end; - procedure TDependentObservable.Evaluate; begin if fIsNotifying then Exit; @@ -314,14 +303,20 @@ procedure TDependentObservable.Evaluate; end; end; -function TDependentObservable.GetValueNonGeneric: TValue; +function TDependentObservable.GetValue: TValue; begin if fNeedsEvaluation or (ObservableStack.Count > 0) then Evaluate; Result := fValue; end; -procedure TDependentObservable.SetValueNonGeneric(const value: TValue); +procedure TDependentObservable.Notify; +begin + Evaluate; + inherited; +end; + +procedure TDependentObservable.SetValue(const value: TValue); begin if fIsNotifying then Exit; if Assigned(fSetter) then @@ -350,18 +345,18 @@ constructor TObservable.Create(const value: T); fValue := value; end; -function TObservable.GetValue: T; +function TObservable.GetValue: TValue; begin - RegisterDependency; - Result := fValue; + Result := TValue.From(Invoke); end; -function TObservable.GetValueNonGeneric: TValue; +function TObservable.Invoke: T; begin - Result := TValue.From(GetValue); + RegisterDependency; + Result := fValue; end; -procedure TObservable.SetValue(const value: T); +procedure TObservable.Invoke(const value: T); begin if not Comparer.Equals(fValue, value) then begin @@ -370,9 +365,9 @@ procedure TObservable.SetValue(const value: T); end; end; -procedure TObservable.SetValueNonGeneric(const value: TValue); +procedure TObservable.SetValue(const value: TValue); begin - SetValue(value.ToType); + Invoke(value.ToType); end; {$ENDREGION} @@ -395,12 +390,6 @@ constructor TDependentObservable.Create(const getter: TFunc; Evaluate; end; -procedure TDependentObservable.Notify; -begin - Evaluate; - inherited; -end; - procedure TDependentObservable.Evaluate; begin if fIsNotifying then Exit; @@ -418,19 +407,19 @@ procedure TDependentObservable.Evaluate; end; end; -function TDependentObservable.GetValue: T; +function TDependentObservable.GetValue: TValue; begin - if fNeedsEvaluation or (ObservableStack.Count > 0) then - Evaluate; - Result := fValue; + Result := TValue.From(Invoke); end; -function TDependentObservable.GetValueNonGeneric: TValue; +function TDependentObservable.Invoke: T; begin - Result := TValue.From(GetValue); + if fNeedsEvaluation or (ObservableStack.Count > 0) then + Evaluate; + Result := fValue; end; -procedure TDependentObservable.SetValue(const value: T); +procedure TDependentObservable.Invoke(const value: T); begin if fIsNotifying then Exit; if Assigned(fSetter) then @@ -438,9 +427,15 @@ procedure TDependentObservable.SetValue(const value: T); inherited Notify; end; -procedure TDependentObservable.SetValueNonGeneric(const value: TValue); +procedure TDependentObservable.Notify; +begin + Evaluate; + inherited; +end; + +procedure TDependentObservable.SetValue(const value: TValue); begin - SetValue(value.ToType); + Invoke(value.ToType); end; {$ENDREGION} @@ -450,7 +445,7 @@ procedure TDependentObservable.SetValueNonGeneric(const value: TValue); class function Observable.Create: IObservable; begin - Result := TObservable.Create; + Result := TObservable.Create(); end; class function Observable.Create(const value: T): IObservable; diff --git a/TournamentViewModel.pas b/TournamentViewModel.pas index f76ca96..325633e 100644 --- a/TournamentViewModel.pas +++ b/TournamentViewModel.pas @@ -100,21 +100,21 @@ constructor TMatch.Create(const matchName: string; begin fHomeTeam := homeTeam; fAwayTeam := awayTeam; - fHomeScore := TObservable.Create; - fAwayScore := TObservable.Create; - fWinner := TObservable.Create; + fHomeScore := TObservable.Create(); + fAwayScore := TObservable.Create(); + fWinner := TObservable.Create(); end; procedure TMatch.SaveScore; var homeScore, awayScore: Integer; begin - homeScore := fHomeScore.Value; - awayScore := fAwayScore.Value; + homeScore := fHomeScore; + awayScore := fAwayScore; if homeScore > awayScore then - fWinner.Value := fHomeTeam.Value + fWinner(fHomeTeam) else if homeScore < awayScore then - fWinner.Value := fAwayTeam.Value; + fWinner(fAwayTeam); end; end.