Delphi-PRAXiS
Seite 1 von 4  1 23     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Record im Array nutzen? - Sinnvollere Wege? (https://www.delphipraxis.net/185842-record-im-array-nutzen-sinnvollere-wege.html)

Asura 10. Jul 2015 15:05

Record im Array nutzen? - Sinnvollere Wege?
 
Guten Tag,
zurzeit benutze ich ein Array, welches ein Element eines Records ist.
Nur habe ich gerade bei meinen Recherchen erfahren, dass dies keine schöne Methode ist.
Also mein Record sieht so aus:

Delphi-Quellcode:
    TEmployee = Record                          
                  CardID       : Integer;
                  Prename,
                  Surname,
                  Workstation,
                  Position     : String[100];
                  Birthdate    : TDate;
                  AvailAbility : Array[1..7] of String;
                End;
Das Array besteht aus 40 Feldern, die jeweils ja diese Record Anordnung haben.

Nun Frage ich mich, ist dieser Weg, wie ich ihn gewählt habe, in Ordnung, oder gibt es bessere Methoden dies umzusetzen?

MfG
Asura

jaenicke 10. Jul 2015 15:38

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Wie wäre es mit Objekten in einer TObjectList?
Also TEmployee = class und die Liste je nach Delphiversion deklariert als TObjectList oder wenn möglich besser TObjectList<TEmployee>.

Vorteile sind, dass du Teile der Objekte anders als bei Records auch direkt setzen kannst über die Liste und dass du eine beliebige Anzahl an Elementen hast ohne das Array manuell in der Größe verändern zu müssen.

p80286 10. Jul 2015 16:26

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Eine Liste ist zunächst einmal eine gute Alternative.
Ich würde aber erst einmal tiefer ansetzen und die Elemente des Records kritisch hinterfragen.
a) ist die CardId wirklich numerisch? oder sieht sie nur so aus?
b) was verbirgt sich hinter Position? wenn dies festgelegte Inhalte sind, dann wäre es nicht schlecht z.B. mit ENUM-Typen zu arbeiten. sonst läuft die Suche nach "Hausmeister" ins Leere weil da jetzt der "Facility Manager" steht.
c) Wenn Du
Delphi-Quellcode:
  Position : String[100];
nutzt dann wenigstens auch
Delphi-Quellcode:
AvailAbility : Array[1..7] of AnsiString;
oder
Delphi-Quellcode:
AvailAbility : Array[1..7] of String[255];
Sonst ist das Character-Chaos vorprogrammiert.

Gruß
K-H

Union 10. Jul 2015 16:42

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Ich habe die schreckliche Vermutung dass sich hinter Availability die Verfügbarkeit an einem bestimmten Wochentag verbirgt. Hier wäre ja dann wohl eher so etwas sinnvoll (abgesehen dass es dann in 8 Bit statt in 8 Byte gespeichert werden kann):

Delphi-Quellcode:
TWeekDays = (wdMonday, wdTuesday, wdWednesday, wdThursday, wdFriday, wdSaturday, wdSunday);
TAvailability = set of TWeekDays;

Asura 10. Jul 2015 17:36

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Hallo,
Dankeschön erstmal für die Antworten. Ich habe mal im Anhang das ganze Projekt angehängt, sodass ihr euch mal ein Bild machen könnt (Im Source Ordner zu finden).
Ich habe es zurzeit über meine Methode gelöst - zwar mit mehreren Stunden Ärgernissen, aber schlussendlich gelöst. Aber auch nur die Mitarbeiterverwaltung zum großteil.

Zu den Availability: Ja das sind Tage. Ich habe das so gelöst, dass der Index 1 Beispielsweise für Montag steht und der hintergelegte Text "17:00-23:00" beispielsweise gesplittet und aufgeteilt wird.

Alle Werte sollten Variabel gehalten werden, sprich in meiner aktuellen Version lassen sich alle bearbeiten. Auch Position.

Und Card ID benutze ich zum Teil dafür um meine Items im Array zu finden, sie dient aber auch für den Anwender Informationsgrundlage, die bei der Eintragung wichtig ist.


Ich würde mich auch sehr freuen, wenn ihr generell paar Kritik Äußerungen zu den Programmcode mir schreiben könntet und was ich da verbessern kann oder komplett verändern muss. Würde mich sehr freuen!

MfG
Asura

Sir Rufo 10. Jul 2015 18:33

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Ich hätte das mit Klassen so aufgebaut
Delphi-Quellcode:
unit Unit1;

interface

uses
  System.Classes,
  System.DateUtils,
  System.Generics.Collections,
  System.TimeSpan;

type
{$SCOPEDENUMS ON}
  TWeekDay = ( Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday );
{$SCOPEDENUMS OFF}

  TAvailibility = class( TPersistent )
  private
    FFrom: TTime;
    FDuration: TTimeSpan;
    FIsAvailable: Boolean;
    procedure SetFrom( const Value: TTime );
    procedure AssignToAvailibility( Dest: TAvailibility );
    procedure SetIsAvailable( const Value: Boolean );
  protected
    procedure AssignTo( Dest: TPersistent ); override;
  public
    function Equals( Obj: TObject ): Boolean; override;

    property IsAvailable: Boolean read FIsAvailable write SetIsAvailable;
    property From: TTime read FFrom write SetFrom;
    property Duration: TTimeSpan read FDuration write FDuration;
  end;

  TEmployee = class( TPersistent )
  private
    FAvailibility: TObjectDictionary<TWeekDay, TAvailibility>;
    FCardID: Integer;
    FPrename: string;
    FSurname: string;
    FWorkstation: string;
    FPosition: string;
    FBirthday: TDate;
    function GetAvailibility( Weekday: TWeekDay ): TAvailibility;
    procedure SetBirthday( const Value: TDate );
    procedure AssignToEmployee( Dest: TEmployee );
    procedure SetAvailibility( Weekday: TWeekDay; const Value: TAvailibility );
  protected
    procedure AssignTo( Dest: TPersistent ); override;
  public
    constructor Create;
    destructor Destroy; override;

    function Equals( Obj: TObject ): Boolean; override;

    property CardID: Integer read FCardID write FCardID;
    property Prename: string read FPrename write FPrename;
    property Surname: string read FSurname write FSurname;
    property Workstation: string read FWorkstation write FWorkstation;
    property Position: string read FPosition write FPosition;
    property Birthday: TDate read FBirthday write SetBirthday;
    property Availibility[ Weekday: TWeekDay ]: TAvailibility read GetAvailibility write SetAvailibility;
  end;

implementation

{ TEmployee }

procedure TEmployee.AssignTo( Dest: TPersistent );
begin
  if ( Dest is TEmployee ) then
    AssignToEmployee( Dest as TEmployee )
  else
    inherited;
end;

procedure TEmployee.AssignToEmployee( Dest: TEmployee );
var
  LWeekDay: TWeekDay;
begin
  Dest.FCardID := Self.FCardID;
  Dest.FPrename := Self.FPrename;
  Dest.FSurname := Self.FSurname;
  Dest.FWorkstation := Self.FWorkstation;
  Dest.FPosition := Self.FPosition;
  Dest.FBirthday := Self.FBirthday;
  for LWeekDay := low( TWeekDay ) to high( TWeekDay ) do
    Dest.SetAvailibility( LWeekDay, Self.GetAvailibility( LWeekDay ) );
end;

constructor TEmployee.Create;
var
  LWeekDay: TWeekDay;
begin
  inherited Create;
  FAvailibility := TObjectDictionary<TWeekDay, TAvailibility>.Create( [ doOwnsValues ] );
  for LWeekDay := low( TWeekDay ) to high( TWeekDay ) do
  begin
    FAvailibility.Add( LWeekDay, TAvailibility.Create );
  end;
end;

destructor TEmployee.Destroy;
begin
  FAvailibility.Free;
  inherited;
end;

function TEmployee.Equals( Obj: TObject ): Boolean;
begin
  Result := ( Self = Obj ) or Assigned( Obj ) and ( Obj is TEmployee ) and ( TEmployee( Obj ).FCardID = Self.FCardID );
end;

function TEmployee.GetAvailibility( Weekday: TWeekDay ): TAvailibility;
begin
  Result := FAvailibility[ Weekday ];
end;

procedure TEmployee.SetAvailibility( Weekday: TWeekDay; const Value: TAvailibility );
begin
  FAvailibility[ Weekday ].Assign( Value );
end;

procedure TEmployee.SetBirthday( const Value: TDate );
begin
  FBirthday := DateOf( Value );
end;

{ TAvailibility }

procedure TAvailibility.AssignTo( Dest: TPersistent );
begin
  if ( Dest is TAvailibility ) then
    AssignToAvailibility( Dest as TAvailibility )
  else
    inherited;
end;

procedure TAvailibility.AssignToAvailibility( Dest: TAvailibility );
begin
  Dest.FFrom := Self.FFrom;
  Dest.FDuration := Self.FDuration;
end;

function TAvailibility.Equals( Obj: TObject ): Boolean;
begin
  Result := ( Self = Obj ) or Assigned( Obj ) and ( Obj is TAvailibility ) and (
    {} ( TAvailibility( Obj ).FIsAvailable = Self.FIsAvailable ) or
    {} ( ( TAvailibility( Obj ).FFrom = Self.FFrom ) and ( TAvailibility( Obj ).FDuration = Self.FDuration ) ) );

end;

procedure TAvailibility.SetFrom( const Value: TTime );
begin
  FFrom := TimeOf( Value );
end;

procedure TAvailibility.SetIsAvailable( const Value: Boolean );
begin
  FIsAvailable := Value;
end;

end.

Namenloser 10. Jul 2015 18:39

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Also zunächst mal ist zu sagen, dass es in Delphi seit Version 4 bereits dynamische Arrays gibt. Es gibt also keinen Grund, eine Maximalgröße wie „1000“ fest vorzugeben. Feste Größen sind schlecht, weil sie erstens beliebig sind (warum gerade 1000? Was ist, wenn es 1001 Mitarbeiter gibt?) und zweitens, weil sie Platz verschwenden. Schreibe statt
Delphi-Quellcode:
Employees: array[1..1000] of TEmployee
also besser
Delphi-Quellcode:
Employees: array of TEmployee
. Die Größe kannst du mit SetLength zur Laufzeit verändern.

Da es aber mühsam ist, jedes mal beim Hinzufügen eines Elements (vom Löschen fangen wir gar nicht erst an) folgendes zu schreiben
Delphi-Quellcode:
SetLength(Employees, length(Employees)+1);
Employees[length(Employees)-1] := NewEmployee;
verwendet man besser eine TObjectList oder TList.

Das mit der festen Höchstlänge gilt auch für Strings. Wenn es nicht wirklich einen guten Grund gibt, dann sollte man nicht
Delphi-Quellcode:
String[100]
schreiben, sondern einfach
Delphi-Quellcode:
String
.

Als Grundproblem sehe ich bei dir die aber die mangelnde Modellierung an.

Zunächst mal solltest du die Geschäftslogik strikt von der Darstellung trennen. Du solltest also Termine, Uhrzeiten etc. z.B. intern niemals als String speichern. Statt einen einzelnen Termin ("15:00-17:30") als String zu repräsentieren, der immer wieder geparst werden muss, bastel dir einen speziellen Typen, der genau die Information abbildet:
Delphi-Quellcode:
TAppointment = record
  StartTime: TTime;
  EndTime: TTime;
end;
[...]

Availability : Array[1..7] of TAppointment;
Dann der nächste Punkt:
Delphi-Quellcode:
Array[1..7]
. Zunächst mal fangen Arrays normalerweise bei 0 an und nicht bei 1. Davon abzuweichen führt eher zu Verwirrungen und Fehlern. Noch wichtiger: Die Zahlen sagen mir nichts über die Bedeutung. Ich kann nur mutmaßen, dass es sich hier um Wochentage handelt. Aber dann ist immer noch die Frage, welche Zahl welchem Tag entspricht. Entspricht die 1 z.B. dem Montag oder dem Sonntag?

Besser wäre:

Delphi-Quellcode:
TWeekday = (wdMonday, wdTuesday, wdWednesday, wdThursday, wdFriday, wdSaturday, wdSunday);

Availability: Array[TWeekday] of TAppointment;
Das sieht schon besser aus.

Allerdings ist diese Modellierung jetzt natürlich immer noch darauf eingeschränkt, dass es pro Wochentag nur einen Termin gibt. Da kann ich jetzt ohne nähere Informationen zu deinem Anwendungsfall nicht sagen, ob das zu deinem Anwendungsfall passt oder nicht.

Aber selbst dann wäre es eventuell besser, einfach eine flache Liste von Terminen zu speichern, die nicht nach Wochentagen gruppiert ist:

Delphi-Quellcode:
TAppointment = record
  StartTime: TDateTime; // Beachte: Nun DateTime
  EndTime: TDateTime;
end;

[...]

Availability: array of TAppointment;
// &#959;der bei neueren Delphis
Availability: TList<TAppointment>;
So ist man, falls sich die Anforderungen später doch mal ändern, flexibler mit der Art von Terminen, die möglich sind. Man könnte z.B. mehrere Termine an einem Tag haben. Durch hinzufügen eines zusätzlichen Feldes zu TAppointment könnte man sogar unterscheiden zwischen einmaligen Terminen und solchen, die sich jede Woche wiederholen.

Asura 10. Jul 2015 23:46

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Erstmal vielen Dank für die bis jetzt aufgebrachte Mühe!

Ich muss vorab sagen, ich mach das alles nur als Hobby, bin nebenbei noch Schüler und interessiere mich in den Bereich und würde auch gerne in der Richtung studieren wollen und will mir bereits fürs Studium mal schon ein gewisses Basiswissen schaffen.

Nun wo fange ich erstmal an. Ich habe mir mal den Programmaufbau von Sir Rufo angeschaut. Ich muss sagen, dass ist für mein Anfängerwissen echt zu hoch. Also da fallen mir viele Fragen ein, wobei ich das mal nur auf das allgemeinste begrenze:
  1. Diese Darstellung ist nicht mit TObjects zu vergleichen, oder ist genau das eine Form von den bereits genannten TObjects?
  2. Spricht man von anderen Units dann genau diese einzelne Unterklasse an und die damit verbundenen Prozeduren und Funktionen?
  3. Was hat es mit der Funktion "Equals" auf sich?
  4. Warum muss man TEmployee im Destruktor wieder freigeben?
  5. Was hat es mit diesem {$SCOPEDENUMS ON} auf sich?

Ich muss sagen, dass dieses Codebeispiel, nochmals danke für diese Mühe extra auf mein Fallbeispiel angepasst eine Unit zu verfassen, für mich eine zu Hohe Hausnummer ist, man vergleiche mal meine Version (Um mal auf Deutsch zu sagen "ein Scheiß dagegen" ist.)

Ich habe mich mal desweiteren informiert und muss sagen, dass TObjects sich auf jedenfall besser eignet als meine Method, die wie von euch genannt, zu stark auf die Darstellung ausrichtet ist.

Nun zu Namenloser:
Bezüglich dem Array, ich gebe dir absolut Recht bezüglich dem Dynamischen Array und würde auch hier lieber TObjectlist nutzen. Also an meinem Anwendungsfall, wird pro Wochentag nur ein Termin genutzt. Denn später werde ich mit der Zeit schauen, ob die betroffene Person an dem Wochentag in einem bestimmten Zeitraum verfügbar ist und dabei wird nur dieser Zeitwert für den einen Wochentag genutzt.

Bezüglich 0 und 1 im Array. Ich habe das eig benutzt, damit ich später noch weiß, dass damit von Montag bis Sonntag gemeint ist (7 Tage). Aber die vorgeschlagene Methode ist um einiges besser.
Aber ich muss auch später Urlaubstage einfügen, also ich habe mir einfach gedacht, dass ich diese Tage in eine TStringlist reinschreibe und das Programm einfach die Liste durchgeht und wenn der Tag mit dem Tag der Überprüfung übereinstimmt, wird dieser als nicht verfügbar angezeigt.

Eine schlussendliche Frage habe ich noch: Als Grundproblem soll meine mangelnde Modellierung schuld sein, kann ich dies irgendwo nachlesen bzw. lernen? Eventuell habt ihr dazu passendes Informationsmaterial zur Hand? Würde gerne daran arbeiten wollen.

MfG
Asura

Namenloser 11. Jul 2015 11:17

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Zitat:

Zitat von Asura (Beitrag 1308362)
Eine schlussendliche Frage habe ich noch: Als Grundproblem soll meine mangelnde Modellierung schuld sein, kann ich dies irgendwo nachlesen bzw. lernen? Eventuell habt ihr dazu passendes Informationsmaterial zur Hand? Würde gerne daran arbeiten wollen.

Hmm, ich kann dir nicht wirklich etwas bestimmtes empfehlen, aber generell Bücher über Objektorientierung. Muss nicht unbedingt ein Buch zum Kaufen sein, habe selber auch nie eins besessen (wobei das Buch „Delphi für Kids“, mit dem ich damals den Einstieg in die Programmierung gefunden habe, schon etwas Objektorientierung mitbehandelte). Im Internet gibt es sehr viel Material dazu. Kann leider wie gesagt kein bestimmtes empfehlen, ist bei mir selber zu lange her. Aber vielleicht hat ja jemand anders einen Tipp.

Wenn du Objektorientierung verinnerlicht hast, schau dir als nächstes Design Patterns an.

Bjoerk 11. Jul 2015 11:38

AW: Record im Array nutzen? - Sinnvollere Wege?
 
Zur Not kannst du auch erst mal beim Record bleiben, dann mußt du dein Programm nicht komplett neu machen. Records sind aus mehren Gründen allerdings nicht zu empfehlen. Der String[100] kann auch irgendwann mal ein string werden, wenn du satt typisierte Dateien (Annahme) z.B. einen FileStream oder zur Not auch eine MemIniFile verwendest.
Delphi-Quellcode:
  TEmployees = class
  private
    function GetCount: integer;
    procedure SetCount(const Value: integer);
  public
    Items: array of TEmployee;
    property Count: integer read GetCount write SetCount;
    function Add(const Value: TEmployee): integer;
    procedure Delete(const Index: integer);
    procedure Assign(Value: TEmployees);
    procedure Clear;
    procedure LoadFromFile(const FileName: string);
    procedure SaveToFile(const FileName: string);
    destructor Destroy; override;
  end;

{ TEmployees }

destructor TEmployees.Destroy;
begin
  Clear;
  inherited;
end;

function TEmployees.GetCount: integer;
begin
  Result := Length(Items);
end;

procedure TEmployees.SetCount(const Value: integer);
begin
  SetLength(Items, Value);
end;

procedure TEmployees.Clear;
begin
  SetCount(0);
end;

function TEmployees.Add(const Value: TEmployee): integer;
begin
  Result := Count;
  SetCount(Result + 1);
  Items[Result] := Value;
end;

procedure TEmployees.Delete(const Index: integer);
var
  I: integer;
begin
  for I := Index to Count - 2 do
    Items[I] := Items[I + 1];
  SetCount(Count - 1);
end;

procedure TEmployees.Assign(Value: TEmployees);
var
  I: integer;
begin
  SetCount(Value.Count);
  for I := 0 to Count - 1 do
    Items[I] := Value.Items[I];
end;

procedure TEmployees.LoadFromFile(const FileName: string);
begin
end;

procedure TEmployees.SaveToFile(const FileName: string);
begin
end;


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:54 Uhr.
Seite 1 von 4  1 23     Letzte »    

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz