Notify list refactoring

This commit is contained in:
Michael Schimmel
2025-07-15 09:46:16 +02:00
parent ce0cba720a
commit 1a07468ad8
4 changed files with 325 additions and 281 deletions
+182 -21
View File
@@ -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.