Notify list refactoring
This commit is contained in:
@@ -3,12 +3,57 @@ unit Myc.Trade.DataPoint.Impl;
|
||||
interface
|
||||
|
||||
uses
|
||||
System.SysUtils,
|
||||
Myc.Signals,
|
||||
Myc.Trade.Types,
|
||||
Myc.Trade.DataPoint;
|
||||
Myc.Trade.DataPoint,
|
||||
Myc.Core.Notifier;
|
||||
|
||||
type
|
||||
// Null object implementation for IMycConverter
|
||||
// Abstract base class for data consumers.
|
||||
TMycProcessor<T> = class abstract(TInterfacedObject, IMycProcessor<T>)
|
||||
protected
|
||||
function ProcessData(const Value: T): TState; virtual; abstract;
|
||||
end;
|
||||
|
||||
// Concrete data provider that manages a list of processors (listeners).
|
||||
TMycDataProvider<T> = class abstract(TContainedObject, TDataProvider<T>.IDataProvider)
|
||||
private
|
||||
FListeners: TMycNotifyList<IMycProcessor<T>>;
|
||||
public
|
||||
constructor Create(const Controller: IInterface);
|
||||
destructor Destroy; override;
|
||||
// Notifies all linked processors.
|
||||
function Broadcast(const Value: T): TState;
|
||||
// Link a Processor
|
||||
function Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||
// Unlink a linked strategy
|
||||
procedure Unlink(Tag: TDataProvider<T>.TTag);
|
||||
end;
|
||||
|
||||
// Null object implementation for IDataProvider.
|
||||
TNullDataProvider<T> = class(TInterfacedObject, TDataProvider<T>.IDataProvider)
|
||||
public
|
||||
function Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||
procedure Unlink(Tag: TDataProvider<T>.TTag);
|
||||
end;
|
||||
|
||||
// Abstract base class for components that process data of type S and provide data of type T.
|
||||
TMycConverter<S, T> = class abstract(TMycProcessor<S>, TConverter<S, T>.IConverter)
|
||||
private
|
||||
FSender: TMycDataProvider<T>;
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
protected
|
||||
function ProcessData(const Value: S): TState; override; abstract;
|
||||
// Broadcasts the given data to all linked processors.
|
||||
function Broadcast(const Value: T): TState;
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
||||
end;
|
||||
|
||||
// Null object implementation for IConverter.
|
||||
TNullConverter<S, T> = class(TInterfacedObject, TConverter<S, T>.IConverter)
|
||||
private
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
@@ -16,22 +61,7 @@ type
|
||||
function ProcessData(const Value: S): TState;
|
||||
end;
|
||||
|
||||
TMycConverter<S, T> = class abstract(TMycProcessor<S>, TConverter<S, T>.IConverter)
|
||||
private
|
||||
FSender: TMycDataProvider<T>;
|
||||
function GetSender: TDataProvider<T>.IDataProvider;
|
||||
protected
|
||||
function ProcessData(const Value: S): TState; override; abstract;
|
||||
|
||||
// Broadcasts the given data to all linked processors.
|
||||
function Broadcast(const Value: T): TState;
|
||||
|
||||
public
|
||||
constructor Create;
|
||||
destructor Destroy; override;
|
||||
property Sender: TDataProvider<T>.IDataProvider read GetSender;
|
||||
end;
|
||||
|
||||
// A generic converter that uses a function reference for the conversion logic.
|
||||
TMycGenericConverter<S, T> = class(TMycConverter<S, T>)
|
||||
private
|
||||
FFunc: TConstFunc<S, T>;
|
||||
@@ -41,12 +71,14 @@ type
|
||||
constructor Create(const AFunc: TConstFunc<S, T>);
|
||||
end;
|
||||
|
||||
// A converter specialized for calculating indicators.
|
||||
TMycIndicator<S, T> = class(TMycConverter<S, T>)
|
||||
protected
|
||||
function ProcessData(const Value: S): TState; override; final;
|
||||
function Calculate(const Value: S): T; virtual; abstract;
|
||||
end;
|
||||
|
||||
// A converter that counts incoming data points and outputs the current count.
|
||||
TMycDataCounter<T> = class(TMycConverter<T, Int64>)
|
||||
private
|
||||
FCount: Int64;
|
||||
@@ -56,27 +88,124 @@ type
|
||||
constructor Create;
|
||||
end;
|
||||
|
||||
// A converter that takes an array and broadcasts each element individually.
|
||||
TMycTicker<T> = class(TMycConverter<TArray<T>, T>)
|
||||
public
|
||||
function ProcessData(const Values: TArray<T>): TState; override;
|
||||
end;
|
||||
|
||||
// A converter that reads a specific field from a record using RTTI.
|
||||
TMycRecordFieldReader<S, T> = class(TMycConverter<S, T>)
|
||||
private
|
||||
FOffset: Integer;
|
||||
|
||||
public
|
||||
constructor Create(const AFieldName: String);
|
||||
function ProcessData(const Values: S): TState; override;
|
||||
end;
|
||||
|
||||
// A generic processor implementation that uses a function reference.
|
||||
TMycGenericProcessor<T> = class(TMycProcessor<T>)
|
||||
type
|
||||
TProc = reference to function(const Value: T): TState;
|
||||
private
|
||||
FProc: TProc;
|
||||
protected
|
||||
function ProcessData(const Value: T): TState; override; final;
|
||||
public
|
||||
constructor Create(const AProc: TProc);
|
||||
end;
|
||||
|
||||
// A processor implementation that is owned by a controller.
|
||||
TMycContainedProcessor<T> = class(TContainedObject, IMycProcessor<T>)
|
||||
type
|
||||
TProc = function(const Value: T): TState of object;
|
||||
private
|
||||
FProc: TProc;
|
||||
function ProcessData(const Value: T): TState;
|
||||
public
|
||||
constructor Create(const Controller: IInterface; const AProc: TProc);
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
System.TypInfo,
|
||||
System.SysUtils,
|
||||
System.RTTI;
|
||||
|
||||
{ TMycDataProvider<T> }
|
||||
|
||||
constructor TMycDataProvider<T>.Create(const Controller: IInterface);
|
||||
begin
|
||||
inherited Create(Controller);
|
||||
end;
|
||||
|
||||
destructor TMycDataProvider<T>.Destroy;
|
||||
begin
|
||||
FListeners.Finalize;
|
||||
inherited Destroy;
|
||||
end;
|
||||
|
||||
function TMycDataProvider<T>.Broadcast(const Value: T): TState;
|
||||
begin
|
||||
FListeners.Lock;
|
||||
try
|
||||
var item := FListeners.First;
|
||||
if item = nil then
|
||||
exit(TState.Null);
|
||||
|
||||
var i := 1;
|
||||
while item.Next <> nil do
|
||||
begin
|
||||
inc(i);
|
||||
item := item.Next;
|
||||
end;
|
||||
|
||||
var done := TLatch.CreateLatch(i);
|
||||
while item <> nil do
|
||||
begin
|
||||
item.Receiver.ProcessData(Value).Signal.Subscribe(done);
|
||||
item := item.Prev;
|
||||
end;
|
||||
|
||||
Result := done.State;
|
||||
finally
|
||||
FListeners.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
function TMycDataProvider<T>.Link(const Processor: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||
begin
|
||||
// Add the Processor to the notification list
|
||||
FListeners.Lock;
|
||||
try
|
||||
Result := FListeners.Advise(Processor);
|
||||
finally
|
||||
FListeners.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure TMycDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
|
||||
begin
|
||||
FListeners.Lock;
|
||||
try
|
||||
FListeners.Unadvise(Tag);
|
||||
finally
|
||||
FListeners.Release;
|
||||
end;
|
||||
end;
|
||||
|
||||
{ TNullDataProvider<T> }
|
||||
|
||||
function TNullDataProvider<T>.Link(const Receiver: IMycProcessor<T>): TDataProvider<T>.TTag;
|
||||
begin
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
procedure TNullDataProvider<T>.Unlink(Tag: TDataProvider<T>.TTag);
|
||||
begin
|
||||
// Do nothing in the null implementation.
|
||||
end;
|
||||
|
||||
{ TMycConverter<S, T> }
|
||||
|
||||
constructor TMycConverter<S, T>.Create;
|
||||
@@ -101,7 +230,7 @@ begin
|
||||
Result := FSender;
|
||||
end;
|
||||
|
||||
{ TNullConverter }
|
||||
{ TNullConverter<S, T> }
|
||||
|
||||
function TNullConverter<S, T>.GetSender: TDataProvider<T>.IDataProvider;
|
||||
begin
|
||||
@@ -133,6 +262,8 @@ begin
|
||||
Result := Broadcast(Calculate(Value));
|
||||
end;
|
||||
|
||||
{ TMycDataCounter<T> }
|
||||
|
||||
constructor TMycDataCounter<T>.Create;
|
||||
begin
|
||||
inherited Create;
|
||||
@@ -145,6 +276,8 @@ begin
|
||||
inc(FCount);
|
||||
end;
|
||||
|
||||
{ TMycTicker<T> }
|
||||
|
||||
function TMycTicker<T>.ProcessData(const Values: TArray<T>): TState;
|
||||
begin
|
||||
var done := TLatch.CreateLatch(Length(Values));
|
||||
@@ -156,6 +289,8 @@ begin
|
||||
Result := done.State;
|
||||
end;
|
||||
|
||||
{ TMycRecordFieldReader<S, T> }
|
||||
|
||||
constructor TMycRecordFieldReader<S, T>.Create(const AFieldName: String);
|
||||
begin
|
||||
inherited Create;
|
||||
@@ -193,4 +328,30 @@ begin
|
||||
Result := Broadcast(PT(fieldPtr)^);
|
||||
end;
|
||||
|
||||
{ TMycGenericProcessor<T> }
|
||||
|
||||
constructor TMycGenericProcessor<T>.Create(const AProc: TProc);
|
||||
begin
|
||||
inherited Create;
|
||||
FProc := AProc;
|
||||
end;
|
||||
|
||||
function TMycGenericProcessor<T>.ProcessData(const Value: T): TState;
|
||||
begin
|
||||
Result := FProc(Value);
|
||||
end;
|
||||
|
||||
{ TMycContainedProcessor<T> }
|
||||
|
||||
constructor TMycContainedProcessor<T>.Create(const Controller: IInterface; const AProc: TProc);
|
||||
begin
|
||||
inherited Create(Controller);
|
||||
FProc := AProc;
|
||||
end;
|
||||
|
||||
function TMycContainedProcessor<T>.ProcessData(const Value: T): TState;
|
||||
begin
|
||||
Result := FProc(Value);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user