构造函数依赖注入的替代方法

时间:2018-11-13 11:22:04

标签: delphi dependency-injection

我正在一个员工计时项目中单独工作,该项目使用数据库存储时间表条目。我在该项目中使用的是Delphi Pro 10.2.3 Tokyo,并创建了一个包装类库,以方便处理普通类之类的数据集。例如,要访问Employee表中的FirstName字段,我可以编写LFirstName:= FEmployee.FirstName;。而不是LFirstName:= Dataset.FieldByName('FirstName')。AsString;

我的某些类具有大量的依赖项(多达八个),这些依赖项是通过类的构造函数注入的。我使用域对象来创建所需的接口,并将它们注入正在创建的类中。  本身被注入的某些接口也非常复杂,并且开始难以跟踪域对象中的所有内容。

要注入的依赖项包括用于其他表的包装器接口,这些接口提供计算字段的查找值,指向创建由类使用的创建对象的函数的指针或用于解析主/详细关系的回调函数。这些关系是静态的,需要在构造函数中进行设置,以便创建类时任何计算出的字段都将起作用。

是否存在任何替代构造函数的方法,它们可能会降低构造函数的复杂性,同时保持解耦的类。这是我其中一个模块中用于时间表输入的代码示例。

unit LevelPay.DbModel.TimesheetEntry;

interface

uses
    Data.Db
  , FireDAC.Comp.DataSet
  , MyLib.Model.Interfaces
  , LevelPay.Model.Types
  , LevelPay.Model.Constants
  , LevelPay.Model.Interfaces
  , LevelPay.DbModel.AppModel
  ;

type
  TDbCustomTimesheetEntry = class(
    TDbAppModel<ITimesheetEntry>,
    ITimesheetEntry
    )
  strict private
    FCopyFunc: TCopyFunc<ITimesheetEntry>;
    procedure ClearFilter;
    procedure FilterEntries(const ADate: TDate);
  strict protected
    FTimesheet: ITimesheet;
    FID: TField;
    FEmployeeID: TField;
    FPayPeriodEndDate: TField;
    FFiscalYearEndDate: TField;
    FFiscalYearStartDate: TField;
    FRowNbr: TField;
    FEntryTypeID: TField;
    FDateIn: TField;
    FTimein: TField;
    FDateOut: TField;
    FTimeOut: TField;
    FCreatedBy: TField;
    FCreatedTimestamp: TField;
    FLastModifiedBy: TField;
    FLastModifiedTimestamp: TField;
    FNote: TField;
    FClockable: TField;
    FClockableHours: TField;
    FDayOfWeek: TField;
    FDifference: TField;
    FEmployeeName: TField;
    FEntryTypeCaption: TField;
    FTimeElapsed: TField;
    FDateIndex: TFDIndex;
    FTimeScheduled: TField;
    FScheduledTimeIn: TField;
    FScheduledTimeOut: TField;
    FWeekOf: TField;
    function GetID: TIdentifier;
    function GetModel: ITimesheetEntry; override;
    function GetClockable: Boolean;
    function GetClockableHours: THours;
    function GetDateIn: TDate;
    function GetDateOut: TDate;
    function GetDifference: THours;
    function GetEmployeeID: TIdentifier;
    function GetEmployeeName: string;
    function GetPayPeriodEndDate: TDate;
    function GetFiscalYearStartDate: TDate;
    function GetFiscalYearEndDate: TDate;
    function GetEntryTypeID: TIdentifier;
    function GetEntryTypeCaption: TCaption;
    function GetPlaceholder: Boolean;
    function GetRowNbr: TRowNbr;
    function GetScheduledTimeIn: TTime;
    function GetScheduledTimeOut: TTime;
    function GetTimeElapsed: THours;
    function GetTimein: TTime;
    function GetTimeOut: TTime;
    function GetTimeScheduled: THours;
    function GetWeekOf: TDate;
    function GetWeekDay: string;
    function GetCreatedBy: TUserName;
    function GetCreatedTimestamp: TDateTime;
    function GetLastModifiedBy: TUserName;
    function GetLastModifiedTimestamp: TDateTime;
    function GetNote: AnsiString;
    function GetEntry: ITimesheetEntry;
    function GetTimesheet: ITimesheet;
    function GetHasEntries: Boolean;
    function Find(AModel: ITimesheetEntry): Boolean; override;
    procedure DoUpdate(AModel: ITimesheetEntry); override;
    procedure Load; virtual;
    procedure CreateFields; override;
    procedure CreateCalcFields; override;
    procedure CreateIndexes; override;
    procedure FormatFields; override;
    procedure OnCalcFields(Dataset: TDataset); override;
    procedure OnNewRecord(Dataset: TDataset); override;
  public
    constructor Create(
      ADataset: TFDDataset;
      AModelFunc: TModelFunc<ITimesheetEntry>;
      ACopyFunc: TCopyFunc<ITimesheetEntry>;
      ATimesheet: ITimesheet;
      ACreateFields: Boolean
    ); reintroduce;
    property ID: TIdentifier read GetID;
    property EmployeeID: TIdentifier read GetEmployeeID;
    property PayPeriodEndDate: TDate read GetPayPeriodEndDate;
    property FiscalYearEndDate: TDate read GetFiscalYearEndDate;
    property FiscalYearStartDate: TDate read GetFiscalYearStartDate;
    property EntryTypeID: TIdentifier read GetEntryTypeID;
    property EntryTypeCaption: TCaption read GetEntryTypeCaption;
    property RowNbr: TRowNbr read GetRowNbr;
    property Clockable: Boolean read GetClockable;
    property ClockableHours: THours read GetClockableHours;
    property DateIn: TDate read GetDateIn;
    property EmployeeName: string read GetEmployeeName;
    property ScheduledTimeIn: TTime read GetScheduledTimeIn;
    property ScheduledTimeOut: TTime read GetScheduledTimeOut;
    property TimeIn: TTime read GetTimein;
    property DateOut: TDate read GetDateOut;
    property TimeOut: TTime read GetTimeOut;
    property TimeElapsed: THours read GetTimeElapsed;
    property Placeholder: Boolean read GetPlaceholder;
    property TimeScheduled: THours read GetTimeScheduled;
    property Difference: THours read GetDifference;
    property WeekDay: string read GetWeekDay;
    property WeekOf: TDate read GetWeekOf;
    property CreatedBy: TUserName read GetCreatedBy;
    property CreatedTimestamp: TDateTime read GetCreatedTimestamp;
    property LastModifiedBy: TUserName read GetLastModifiedBy;
    property LastModifiedTimestamp: TDateTime read GetLastModifiedTimestamp;
    property Note: AnsiString read GetNote;
    property Timesheet: ITimesheet read GetTimesheet;
  end;

  TDbSourceEntry = class(TDbCustomTimesheetEntry, ISourceEntryList)
  strict private
    FLoadTimesheetEntries: TLoadTimesheetProc;
    FElectionList: ILevelPayElectionList;
    FPositionList: IHourlyPositionList;
  strict protected
    procedure BeforePost(Dataset: TDataset); override;
    procedure Load; override;
  public
    constructor Create(
      ADataset: TFDDataset;
      AModelFunc: TModelFunc<ITimesheetEntry>;
      ATimesheet: ITimesheet;
      ACopyFunc: TCopyFunc<ITimesheetEntry>;
      AProc: TLoadTimesheetProc;
      AElectionList: ILevelPayElectionList;
      APositionList: IHourlyPositionList;
      ACreateFields: Boolean = True
    ); reintroduce;
  end;

  TDbDummyEntry = class(TDbCustomTimesheetEntry, IDummyEntryList)
  strict private
    FPlaceholderID: TIdentifier;
    FClosureList: ISchoolClosureList;
    procedure EntryTypeIDOnChange(Sender: TField);
  strict protected
    procedure AddPlacedholder(ADate: TDate; ARowNbr: TRowNbr);
    procedure CreateFields; override;
    procedure DoAdd(AModel: ITimesheetEntry); override;
    property PlaceholderID: TIdentifier read FPlaceholderID write FPlaceholderID;
  public
    constructor Create(
      ADataset: TFDDataset;
      AModelFunc: TModelFunc<ITimesheetEntry>;
      ACopyFunc: TCopyFunc<ITimesheetEntry>;
      ATimesheet: ITimesheet;
      AClosureList: ISchoolClosureList;
      ACreateFields: Boolean
    ); reintroduce;
  end;

  TDbTimesheetEntry = class(TDbDummyEntry, ITimesheetEntryList)
  strict private
    FClone: TFDDataset;
    FSource: ISourceEntryList;
    function GetNextRowNbr: TRowNbr;
  strict protected
    function WorkweekList: IWorkweekList;
    procedure Clear;
    procedure Load; override;
  public
    procedure Add(AModel: ITimesheetEntry); //replaces inherited add
    procedure Delete(AModel: ITimesheetEntry); //replace inherited delete
    procedure Update(OldModel, NewModel: ITimesheetEntry);
    constructor Create(
      ADataset: TFDDataset;
      AModelFunc: TModelFunc<ITimesheetEntry>;
      ACopyFunc: TCopyFunc<ITimesheetEntry>;
      ATimesheet: ITimesheet;
      ASourceFunc: TSourceListFunc;
      AClosureList: ISchoolClosureList;
      ACreateFields: Boolean
    ); reintroduce;
  end;

implementation

uses
    System.SysUtils
  , System.Classes
  , System.Variants
  , System.DateUtils
  , FireDAC.Comp.Client
  , DateTimeHelper
  , LevelPay.Model.Helpers
  ;

{ TCustomShift }

procedure TDbCustomTimesheetEntry.ClearFilter;
begin
  CancelRange;
end;

constructor TDbCustomTimesheetEntry.Create(ADataset: TFDDataset;
  AModelFunc: TModelFunc<ITimesheetEntry>; ACopyFunc: TCopyFunc<ITimesheetEntry>;
  ATimesheet: ITimesheet; ACreateFields: Boolean);
begin
  inherited Create(ADataset, AModelFunc, ACreateFields);
  FCopyFunc := ACopyFunc;
  FTimesheet := ATimesheet;
end;

procedure TDbCustomTimesheetEntry.CreateCalcFields;
begin
  inherited;
  FClockable           := CreateCalcBooleanField(k_Clockable);
  FClockableHours      := CreateCalcFloatField(k_ClockableHours);
  FDayOfWeek           := CreateCalcStringField(k_WeekDay, 13);
  FDifference          := CreateCalcFloatField(k_Difference);
  FEmployeeName        := CreateCalcStringField(k_EmployeeName, 40);
  FEntryTypeCaption    := CreateCalcStringField(k_EntryTypeCaption, 20);
  FFiscalYearEndDate   := CreateCalcDateTimeField(k_FiscalYearEndDate);
  FFiscalYearStartDate := CreateCalcDateTimeField(k_FiscalYearStartDate);
  FScheduledTimeIn     := CreateCalcDateTimeField(k_ScheduledTimeIn);
  FScheduledTimeOut    := CreateCalcDateTimeField(k_ScheduledTimeOut);
  FTimeElapsed         := CreateCalcFloatField(k_TimeElapsed);
  FTimeScheduled       := CreateCalcFloatField(k_TimeScheduled);
  FWeekOf              := CreateCalcDateTimeField(k_WeekOf);
end;

procedure TDbCustomTimesheetEntry.CreateFields;
begin
  FID                    := CreateField(k_Id);
  FEmployeeID            := CreateField(k_EmployeeID);
  FEntryTypeID           := CreateField(k_EntryTypeID);
  FRowNbr                := CreateField(k_RowNbr);
  FTimeIn                := CreateField(k_TimeIn);
  FTimeOut               := CreateField(k_TimeOut);
  FID                    := CreateField(k_ID);
  FDateIn                := CreateField(k_DateIn);
  FDateOut               := CreateField(k_DateOut);
  FCreatedBy             := CreateField(k_CreatedBy);
  FCreatedTimestamp      := CreateField(k_CreatedTimeStamp);
  FLastModifiedBy        := CreateField(k_LastModifiedBy);
  FLastModifiedTimestamp := CreateField(k_LastModifiedTimestamp);
  FNote                  := CreateField(k_Note);
  FPayPeriodEndDate      := CreateField(k_PayPeriodEndDate);

end;

procedure TDbCustomTimesheetEntry.CreateIndexes;
const
  FIELD_LIST = k_DateIn + ';' + k_RowNbr;
begin
  inherited;
  FDateIndex := CreateIndex('ByDate', FIELD_LIST);
  FDateIndex.Selected := True;
  Dataset.IndexesActive := True;
end;

function TDbCustomTimesheetEntry.GetClockable: Boolean;
begin
  Result := Rules.Clockable;
end;

function TDbCustomTimesheetEntry.GetClockableHours: THours;
begin
  Result := Rules.ClockableHours;
end;

function TDbCustomTimesheetEntry.GetCreatedBy: TUserName;
begin
  Result := FCreatedBy.AsUserName;
end;

function TDbCustomTimesheetEntry.GetCreatedTimestamp: TDateTime;
begin
  Result := FCreatedTimestamp.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetDateIn: TDate;
begin
  Result := FDateIn.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetDateOut: TDate;
begin
  Result := FDateOut.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetDifference: THours;
begin
  Result := Rules.Difference;
end;

function TDbCustomTimesheetEntry.GetEmployeeID: TIdentifier;
begin
  Result := FEmployeeID.AsIdentifier;
end;

function TDbCustomTimesheetEntry.GetEmployeeName: string;
begin
  Result := Rules.EmployeeName;
end;

function TDbCustomTimesheetEntry.GetEntryTypeID: TIdentifier;
begin
  Result := FEntryTypeID.AsIdentifier;
end;

function TDbCustomTimesheetEntry.GetFiscalYearEndDate: TDate;
begin
  Result := FFiscalYearEndDate.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetFiscalYearStartDate: TDate;
begin
  Result := FFiscalYearStartDate.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetHasEntries: Boolean;
begin
  Result := RecordCount > 0;
end;

function TDbCustomTimesheetEntry.GetID: TIdentifier;
begin
  Result := FID.AsInteger;
end;

function TDbCustomTimesheetEntry.GetLastModifiedBy: TUserName;
begin
  Result := FLastModifiedBy.AsUserName;
end;

function TDbCustomTimesheetEntry.GetLastModifiedTimestamp: TDateTime;
begin
  Result := FLastModifiedTimestamp.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetModel: ITimesheetEntry;
var
  LResult: ITimesheetEntry;
begin
  LResult := FCopyFunc(Self);
  Result := LResult;
end;

function TDbCustomTimesheetEntry.GetNote: AnsiString;
begin
  Result := FNote.AsAnsiString;
end;

function TDbCustomTimesheetEntry.GetPayPeriodEndDate: TDate;
begin
  Result := FPayPeriodEndDate.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetPlaceholder: Boolean;
begin
  Result := Rules.Placeholder;
end;

function TDbCustomTimesheetEntry.GetEntry: ITimesheetEntry;
begin
  Result := Model;
end;

function TDbCustomTimesheetEntry.GetEntryTypeCaption: TCaption;
begin
  Result := Rules.EntryTypeCaption
end;

function TDbCustomTimesheetEntry.GetRowNbr: TRowNbr;
begin
  Result := FRowNbr.AsRowNbr;
end;

function TDbCustomTimesheetEntry.GetScheduledTimeIn: TTime;
begin
  Result := Rules.ScheduledTimeIn;
end;

function TDbCustomTimesheetEntry.GetScheduledTimeOut: TTime;
begin
  Result := Rules.ScheduledTimeOut;
end;

function TDbCustomTimesheetEntry.GetTimeElapsed: THours;
begin
  Result := Rules.TimeElapsed;
end;

function TDbCustomTimesheetEntry.GetTimein: TTime;
begin
  Result := FTimeIn.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetTimeOut: TTime;
begin
  Result := FTimeOut.AsDateTime;
end;

function TDbCustomTimesheetEntry.GetTimeScheduled: THours;
begin
  Result := Rules.TimeScheduled;
end;

function TDbCustomTimesheetEntry.GetTimesheet: ITimesheet;
begin
  Result := FTimesheet;
end;

function TDbCustomTimesheetEntry.GetWeekDay: string;
begin
  Result := Rules.WeekDay;
end;

function TDbCustomTimesheetEntry.GetWeekOf: TDate;
begin
  Result := Rules.WeekOf;
end;

procedure TDbCustomTimesheetEntry.Load;
begin
  //Stub procedure
end;

procedure TDbCustomTimesheetEntry.OnCalcFields(Dataset: TDataset);
begin
  inherited;
  if not Assigned(Rules) then Exit;

  FClockable.AsBoolean         := GetClockable;
  FClockableHours.AsHours      := GetClockableHours;
  FDayOfWeek.AsString          := GetWeekDay;
  FDifference.AsHours          := GetDifference;
  FEmployeeName.AsString       := GetEmployeeName;
  FEntryTypeCaption.AsCaption  := GetEntryTypeCaption;
  FTimeElapsed.AsHours         := GetTimeElapsed;
  FTimeScheduled.AsHours       := GetTimeScheduled;
  FScheduledTimeIn.AsDateTime  := GetScheduledTimeIn;
  FScheduledTimeOut.AsDateTime := GetScheduledTimeOut;
  FWeekOf.AsDateTime           := GetWeekOf;
end;

procedure TDbCustomTimesheetEntry.OnNewRecord(Dataset: TDataset);
begin
  inherited;

  FEmployeeID.AsIdentifier        := FTimesheet.EmployeeID;
  FFiscalYearEndDate.AsDateTime   := FTimesheet.FiscalYearEndDate;
  FFiscalYearStartDate.AsDateTime := FTimesheet.FiscalYearStartDate;
  FPayPeriodEndDate.AsDateTime    := FTimesheet.PayPeriodEndDate;
end;

procedure TDbCustomTimesheetEntry.DoUpdate(AModel: ITimesheetEntry);
begin
  inherited;

  FEmployeeID.AsIdentifier  := AModel.EmployeeID;
  FRowNbr.AsRowNbr          := AModel.RowNbr;
  FEntryTypeID.AsIdentifier := AModel.EntryTypeID;
  FDateIn.AsDateTime        := AModel.DateIn;
  FTimeIn.AsDateTime        := AModel.TimeIn;
  FDateOut.AsDateTime       := AModel.DateOut;
  FTimeOut.AsDateTime       := AModel.TimeOut;
  FNote.AsAnsiString        := AModel.Note;
end;

procedure TDbCustomTimesheetEntry.FilterEntries(const ADate: TDate);
begin
  FDateIndex.Selected := True;
  SetRange([ADate], [ADate]);
end;

function TDbCustomTimesheetEntry.Find(AModel: ITimesheetEntry): Boolean;
begin
  Result := Locate(k_ID, AModel.ID);
end;

procedure TDbCustomTimesheetEntry.FormatFields;
begin
  inherited;
  FTimeElapsed.OnGetText    := HoursFieldGetText;
  FClockableHours.OnGetText := HoursFieldGetText;
  FDifference.OnGetText     := HoursFieldGetText;
  FTimeScheduled.OnGetText  := HoursFieldGetText;

  SetTimeFieldDisplayFormat(FTimeIn);
  SetTimeFieldDisplayFormat(FTimeOut);
  SetDateFieldDisplayFormat(FDateIn);
  SetDateFieldDisplayFormat(FDateOut);
  SetDateFieldDisplayFormat(FPayPeriodEndDate);
  SetSQLTimestampFieldDisplayFormat(FCreatedTimestamp);
  SetSQLTimestampFieldDisplayFormat(FLastModifiedTimestamp);
end;

{ TDbDummyEntry }

procedure TDbSourceEntry.BeforePost(Dataset: TDataset);
var
  LTimestamp: TDateTime;
begin
  inherited;
  LTimestamp := Now;
  FLastModifiedBy.AsUserName        := FTimesheet.User.UserName;
  FLastModifiedTimestamp.AsDateTime := LTimestamp;
  if State in [dsInsert] then
  begin
    FCreatedBy.AsUserName        := FTimesheet.User.UserName;
    FCreatedTimestamp.AsDateTime := LTimestamp;
  end;
end;

constructor TDbSourceEntry.Create(ADataset: TFDDataset;
  AModelFunc: TModelFunc<ITimesheetEntry>; ATimesheet: ITimesheet;
  ACopyFunc: TCopyFunc<ITimesheetEntry>; AProc: TLoadTimesheetProc;
  AElectionList: ILevelPayElectionList;   APositionList: IHourlyPositionList;
  ACreateFields: Boolean);
begin
  inherited Create(ADataset, AModelFunc, ACopyFunc, ATimesheet, ACreateFields);

  FLoadTimesheetEntries := AProc;
  FElectionList         := AElectionList;
  FPositionList         := APositionList;
end;

procedure TDbSourceEntry.Load;
var
  LEmployeeID: TIdentifier;
  LFirstEntryDate: TDate;
  LLastEntryDate: TDate;
begin
  LEmployeeID     := FTimesheet.EmployeeID;
  LFirstEntryDate := FTimesheet.FirstEntryDate;
  LLastEntryDate  := FTimesheet.LastEntryDate;
  FLoadTimesheetEntries(LEmployeeID, LFirstEntryDate, LLastEntryDate);
end;


{ TDbDummyEntry }

procedure TDbDummyEntry.AddPlacedholder(ADate: TDate; ARowNbr: TRowNbr);
var
  LEntryTypeID: TIdentifier;
  LClosure: ISchoolClosure;
  LNote: AnsiString;
  LRowNbr: TRowNbr;
begin
  Dec(FPlaceholderID);
  LNote        := '';
  LEntryTypeID := 0;
  LRowNbr      := ARowNbr;

  if LRowNbr < 2 then //This is a first entry for the date
  begin
  if FClosureList.Find(ADate) then
    begin
      LClosure := FClosureList.Closure;
      LEntryTypeID := LClosure.EntryTypeID;
      LNote := AnsiString(LClosure.Caption);
    end
    else
    begin
      if TDateTime(ADate).DayOfWeek in [MONDAY..FRIDAY] then
        LEntryTypeID := k_Regular
      else
        LEntryTypeID := 0;
    end;
  end;

  Append;
  FId.AsIdentifier          := FPlaceholderID;
  FRowNbr.AsRowNbr          := LRowNbr;
  FDateIn.AsDateTime        := ADate;
  FDateOut.AsDateTime       := ADate;
  FEntryTypeID.AsIdentifier := LEntryTypeID;
  FNote.AsAnsiString        := LNote;
  Post;
end;

constructor TDbDummyEntry.Create(ADataset: TFDDataset;
  AModelFunc: TModelFunc<ITimesheetEntry>; ACopyFunc: TCopyFunc<ITimesheetEntry>;
  ATimesheet: ITimesheet; AClosureList: ISchoolClosureList; ACreateFields: Boolean);
begin
  inherited Create(ADataset, AModelFunc, ACopyFunc, ATimesheet, ACreateFields);
  FClosureList  := AClosureList;
end;

procedure TDbDummyEntry.CreateFields;
begin
  inherited;
  FId.ReadOnly := False;
  FId.AutoGenerateValue := arNone;
  FEntryTypeID.OnChange := EntryTypeIDOnChange;
end;

procedure TDbDummyEntry.DoAdd(AModel: ITimesheetEntry);
begin
  inherited;
  FId.AsIdentifier := AModel.ID;
end;

procedure TDbDummyEntry.EntryTypeIDOnChange(Sender: TField);
var
  LDateIn: TDate;
  LDateOut: TDate;
begin
  if Sender.AsIdentifier = k_LandSchool then
  begin
    LDateIn  := FDateIn.AsDateTime;
    LDateOut := LDateIn + 1;
    FDateOut.AsDateTime := LDateOut;
    FTimeIn.AsDateTime  := GetScheduledTimeOut;
    FTimeOut.AsDateTime := GetScheduledTimeIn;
  end;
end;

{ TDbTimesheetEntry }

procedure TDbTimesheetEntry.Add(AModel: ITimesheetEntry);
var
  LEntry: ITimesheetEntry;
begin
  FSource.Add(AModel);
  LEntry := FSource.Entry; //Get model with updated ID
  FTimesheet.WorkweekList.AddEntry(LEntry);
  inherited Add(LEntry);
end;

procedure TDbTimesheetEntry.Clear;
begin
  PlaceholderID := 0;
  Dataset.CancelUpdates;
  DisableControls;
  try
    First;
    while RecordCount > 0 do
      Dataset.Delete;
  finally
    EnableControls;
  end;
end;

constructor TDbTimesheetEntry.Create(ADataset: TFDDataset;
  AModelFunc: TModelFunc<ITimesheetEntry>;
  ACopyFunc: TCopyFunc<ITimesheetEntry>; ATimesheet: ITimesheet;
  ASourceFunc: TSourceListFunc; AClosureList: ISchoolClosureList;
  ACreateFields: Boolean);
begin
  inherited Create(ADataset, AModelFunc, ACopyFunc, ATimesheet, AClosureList, ACreateFields);
  FSource := ASourceFunc(FTimesheet);

  FClone := TFDMemTable.Create(ADataset);
  FClone.CloneCursor(ADataset);
end;

procedure TDbTimesheetEntry.Delete(AModel: ITimesheetEntry);
var
  LDate: TDate;
  LRowNbr: SmallInt;
  LEntry: ITimesheetEntry;
begin
  FSource.Delete(AModel);
  WorkweekList.DeleteEntry(AModel);
  DisableControls;
  try
    LDate := AModel.DateIn;
    LRowNbr := AModel.RowNbr;
    inherited Delete(AModel);
    if (LRowNbr = 1) then
    begin
      AddPlacedholder(LDate, 0);
      LEntry := Model; //Get placeholder
      WorkweekList.AddEntry(LEntry);
    end;
  finally
    EnableControls;
  end;
end;

function TDbTimesheetEntry.GetNextRowNbr: TRowNbr;
begin
  Result := 0;
  FClone.SetRange([GetDateIn], [GetDateIn]);
  while not FClone.Eof do
  begin
    Result := FClone.FieldByName(k_RowNbr).AsRowNbr + 1;
    FClone.Next;
  end;
  FClone.CancelRange;
end;

procedure TDbTimesheetEntry.Load;
var
  LDate: TDate;
  LFirstEntryDate: TDate;
  LCutoffDate: TDate;
  LEntry: ITimesheetEntry;
  LWeekOf: TDate;
  LWorkweekList: IWorkweekList;
begin
  LWorkweekList   := FTimesheet.WorkweekList;
  LFirstEntryDate := FTimesheet.FirstEntryDate;
  LCutOffDate     := FTimesheet.LastEntryDate;
  LWeekOf         := TDateTime(LFirstEntryDate).StartOfWeek;

  LWorkweekList.Init(LFirstEntryDate, LCutOffDate);
  FSource.Load;
  DisableControls;
  try
    Clear;

    LDate := LWeekOf;
    repeat
      FSource.FilterEntries(LDate);
      if FSource.HasEntries then
      begin
        for LEntry in FSource do
        begin
          if LDate >= LFirstEntryDate then
          begin
            inherited Add(LEntry);  //this must call inherited add to adding to FSource
          end;
          LWorkweekList.AddEntry(LEntry);
        end;
      end
      else
      begin
        if LDate >= LFirstEntryDate then
        begin
          AddPlacedholder(LDate, 0);
          LEntry := GetModel; //This retrieves the placeholder
          LWorkweekList.AddEntry(LEntry);
        end;
      end;
      LDate := LDate + 1;
    until LDate > LCutOffDate;
  finally
    FSource.ClearFilter;
    EnableControls;
    First;
  end;
end;

procedure TDbTimesheetEntry.Update(OldModel, NewModel: ITimesheetEntry);
var
  LEntry: ITimesheetEntry;
begin
  DisableControls;
  try
    if Assigned(OldModel) then
    begin
      if OldModel.Placeholder then
      begin
        FSource.Add(NewModel);
      end
      else
      begin
        FSource.Update(NewModel);
      end;
      inherited Delete(OldModel);
      WorkweekList.DeleteEntry(OldModel);
    end
    else
    begin
      FSource.Add(NewModel);
    end;

    LEntry := FSource.Entry; //Get entry with new ID
    inherited Add(LEntry);
    WorkweekList.AddEntry(LEntry);
  finally
    EnableControls;
  end;
end;

function TDbTimesheetEntry.WorkweekList: IWorkweekList;
begin
  Result := FTimesheet.WorkweekList;
end;

end.

2 个答案:

答案 0 :(得分:4)

通常的说法是,当您有许多构造函数参数(即依赖项)时,这表明您的类可能做得太多(请参见单一负责原则)。

如果某些依赖项在大多数情况下仅彼此交互,则表明这些依赖项可能需要将其重构为自己的组件/类,然后再将其注入。首先,这不仅减少了依赖性,而且降低了组件的复杂性。

我建议阅读Mark Seemann的博客,他在其中解释了许多领域,这些领域在正确实践依赖注入,软件设计和体系结构方面发挥了作用。

我只记得两个例子:

答案 1 :(得分:-1)

您在所有TDataSet根目录中都具有默认属性:

property FieldValues[const FieldName: string]: Variant read GetFieldValue write SetFieldValue; default;

您可以使用

aTest: TDataSet; //any descant
aValue := aTest['NameField'];

为什么使注射复杂化?
语法纯净吗?
就像1994年的Paradox ObjectPAL。