AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

OOP wirklich nicht möglich?

Ein Thema von Delbor · begonnen am 12. Okt 2017 · letzter Beitrag vom 20. Okt 2017
Antwort Antwort
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.358 Beiträge
 
Delphi 11 Alexandria
 
#1

AW: OOP wirklich nicht möglich?

  Alt 18. Okt 2017, 19:07
@Delbor

Das, was Du beschreibst, klingt schon ganz gut, aber Dein Quelltext lässt das nicht ganz erkennen.

Du könntest es Dir deutlich einfacher machen, aber müsstest dafür sicher Dein Projekt neu aufbauen.
Mal zwei Ansätze.

Empfehlenswert wäre m.E. folgender neuer Ansatz (mal am Beispiel Personen und Autos):

Du baust 2 Klassen:

Delphi-Quellcode:
TPerson = class
  Vornme: string;
  Nachname: string;
  Autos: TList<TAuto>;
  Kilometer: Integer;
end;

TAuto = class
  Typ: string;
  Farbe: string;
  Fahrer: List<TPerson>;
  Kilometer: Integer;
  procedure Fahren(aFahrer: TPerson; aKM: Integer);
end;
Jetzt kannst Du Objekte erzeugen, im Speicher verwalten, in Listen zuordnen und Kilometer für Fahrer und Autos zählen.

Das sind die Businessklassen. Nur das brauchst Du, um die Kilometer für die Fahrer und Autos zu ermitteln.

Jetzt kannst Du schon mal testweise per Code einige Testpersonen und Autos anlegen und einige Strecken fahren.
Also zumindest kannst Du schon mal ein paar Testobjekte erzeugen.

Nun baust Du die GUI, und Du kannst sehen, ob die Testdaten korrekt angezeigt werden und ob Du über die GUI neue erzeugen kannst.
Bis hierher spielt die Datenbank noch keine Rolle!

Aber jetzt wollen wir die Daten natürlich doch irgendwie speichern.
Also müssen die Objekte irgendwie die Fähigkeit haben, gespeichert und gelesen zu werden.
Wenn es dafür eine einheitliche Regelung gibt ist das sicher sinnvoll.
Daher könnte man eine Basisklasse einführen, die das unterstützt:


Delphi-Quellcode:
TSaveLoad = class
  procedure Save; virtual;
  procedure Load; virtual;
end;

TPerson = class(TSaveLoad)
  Vornme: string;
  Nachname: string;
  Autos: TList<TAuto>;
  Kilometer: Integer;
  procedure Save; override;
  procedure Load; override;
end;

TAuto = class(TSaveLoad)
  Typ: string;
  Farbe: string;
  Fahrer: List<TPerson>;
  Kilometer: Integer;
  procedure Fahren(aFahrer: TPerson; aKM: Integer);
  procedure Save; override;
  procedure Load; override;
end;

Wenn man die Daten einfach mal in einer Ini speichern will, kann man das in den überschriebenen Methoden Save und Load realisieren.
Wenn man dann auf eine Datenbank welchselt und später nochmal auf eine andere, ändert man einfach die Methoden wieder ab.


Eine andere Variante wäre, die Basisklasse und die Methoden Save und Load weg zu lassen und statt dessen einen DBManager zu bauen:
Also man erzeugt eine weitere Klasse

Delphi-Quellcode:
TDBManager = class
  function GetAllPersons_WithoutCars: TList<TPerson>;
  function LoadPerson(aPersonId: Integer): TPerson;
  procedure SavePerson(aPerson: TPerson);
end;
Dann weiß nur der DBManager, wie die Verbindung zu den Datenbanktabellen herzustellen ist.
Man kann dann unterschiedliche BDManager für unterschiedliche Datenbanken erstellen und die jederzeit austauschen.



In ALLEN FÄLLEN sollte aber TPerson.Vorname IMMER TPerson.Vorname heißen, selbst wenn der ersten Datenbank die Personentabelle People und das Feld FirstName und in der neuen Datenbank die Tabelle Leute und das Feld Name_1 heisst.
NUR in den Methoden Save und Load bzw. in der anderen Variante in dem DBManager müssen die Anpassungen an die Datenbankänderungen vorgenommen werden.


Um wieder auf Dein Beispiel zurück zu kommen:
Statt z.B. in der Buinessklasse mit FTblBild_IdBild: integer; bzw. FBildTabelleIdBild: integer; zu arbeiten, wäre dort fBildID: integer; sinnvoll, weil die bei der Businesslogik eben interessiert.
Wie die passenden Daten aus einer Ini-Datei, aus Datenbank A, Datenbank B oder von Google geholt werden, ist für die Buinessklasse selbst und für die Benennung der Properties völlig uninteressant.


Genau das ist ja auch ein Ziel der OOP, das man verhalten kapselt und auch überschreiben kann.


Sorry, falls das etwas chaotisch ist. Aber vielleicht hilft das ja dennoch mal auf einen besser strukturierten Ansatz.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)

Geändert von stahli (18. Okt 2017 um 19:11 Uhr)
  Mit Zitat antworten Zitat
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.196 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 11:11
Hi zusammen

@MichaelT

Zitat:
In einem Satz formuliert. Ist dein Problem, dass du in der TCMQueryClass SQL Statements drinnen hast?
Nein. Beide Klassen können nur die von einem Query gelieferten Ergebnisse jeweils für einen Datensatz über drei 3 Tabellen speichern.

Zitat:
Deine gecashten Objekte schreiben können sich selbst in DB zurückschreiben, etwas platt formuliert ala object.SetDirty und schon rasseln die SQL Statements.
Diese Klassen sind nicht für Insert-Vorgänge vorgesehen, sondern speichern nur die Ergebnisse von Select-Abfragen. Wobei die Bilder erst dann abgefragt werden, wenn sie dargestellt werden sollen.

Zitat:
Jetzt hast du eine Applikation die auf beide DBs gleichzeitg zugreift? Soll das so bleiben?
Oder du willst in Zukunft willst du nurmehr auf SQLite gehen oder Wahlweise entweder auf MySQL XOR MySQLite zugreifen?
Wenn die Geschichte mit SQLite funktioniert, in diesem Fall nicht mehr. Zu einem (viel?) späteren Zeitpunkt kann es interessant sein, wenn die Anwendung auch auf andere DBMSe zugrreifen kann. Das ist aber vorerst nicht gefragt.
Gleichwohl - die MySQL-DB bleibt vorerst bestehen.

Zitat:
Falls nur eine Datenbank in Zukunft gefragt ist migriere den Datenbestand.
Die Datenbank enthält zur Zeit keine Daten. Und bis zur Fertigstellung des Programms wird sie auch nur Testdaten enthalten.

@stahli:
Zitat:
In ALLEN FÄLLEN sollte aber TPerson.Vorname IMMER TPerson.Vorname heißen, selbst wenn der ersten Datenbank die Personentabelle People und das Feld FirstName und in der neuen Datenbank die Tabelle Leute und das Feld Name_1 heisst.
In diesem Beispiel kann auch ich mit meinen katastrophalen Englischkenntnissen nicht gross irren.
Aber sobald Abkürzungen mit im Spiel sind - zum Bleistift 'Tbl_BildText' als (Teil)-String einer Objekt-Unterkasse und 'BildDesribeTabelle' als Tabellenname in der DB wird mE. die Verwechslungsgefahr grösser und somit auch die Fehleranfälligkeit. Das Objekt oder eine Methode desselben sollten also eher möglichst gleichnamige Bezeichner wie die korrespondierenden Tabellen/Felder in der DB haben.

TTblBildText; ist eine Unterklasse von TQueryCMResultclass(Speichert Ergebnisse aus der SQLite-DB).
TBildDescribeTabelle;ist eine Unterklasse von TQueryResultclass(Speichert Ergebnisse aus der MySQL-DB).

Diese beiden Bezeichner werden sich innerhalb ihrer Klasssen nicht ändern.

Gruss
Delbor
Angehängte Grafiken
Dateityp: jpg CM-Datamodell.JPG (171,0 KB, 21x aufgerufen)
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch
  Mit Zitat antworten Zitat
Benutzerbild von DeddyH
DeddyH

Registriert seit: 17. Sep 2006
Ort: Barchfeld
27.666 Beiträge
 
Delphi 12 Athens
 
#3

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 11:17
Je mehr ich lese, desto mehr frage ich mich: wozu das Ganze? Soll das ein ORM werden/sein? Dann haben IMO die Tabellen- bzw. Feldnamen in den Objekten nichts zu suchen, die Objekte sollte es nicht interessieren, woher die Daten kommen oder wohin sie gehen, dafür ist eine Schicht zuständig. So wie ich es sehe bringt der ganze Aufwand momentan 0% Nutzen (sofern ich das richtig überblicke, ich kann mich auch irren), sondern sorgt eher für Verwirrung.
Detlef
"Ich habe Angst vor dem Tag, an dem die Technologie unsere menschlichen Interaktionen übertrumpft. Die Welt wird eine Generation von Idioten bekommen." (Albert Einstein)
Dieser Tag ist längst gekommen
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.358 Beiträge
 
Delphi 11 Alexandria
 
#4

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 11:49
Aber sobald Abkürzungen mit im Spiel sind - zum Bleistift 'Tbl_BildText' als (Teil)-String einer Objekt-Unterkasse und 'BildDesribeTabelle' als Tabellenname in der DB wird mE. die Verwechslungsgefahr grösser und somit auch die Fehleranfälligkeit. Das Objekt oder eine Methode desselben sollten also eher möglichst gleichnamige Bezeichner wie die korrespondierenden Tabellen/Felder in der DB haben.
Das sehe ich ganz gegensätzlich.
Die Probleme, die Du hier hast, kommen m.E. nur daher, dass Du Deine Klassenmember entsprechend Deiner Tabellen benennen willst.
Wie die Tabellen ausssehen und wie die heißen, hat aber die Businessklassen letztlich nicht zu interessieren.

Was Du tust, ist ein neues Projekt mit neuen Klassen aufzubauen, weil Du eine andere Datenbank anbinden willst. Das ist definitiv der falsche (aufwändigste) Ansatz.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.196 Beiträge
 
Delphi 11 Alexandria
 
#5

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 15:14
Hi zusammen

TQueryResultClass arbeitet schon länger zur vollkomenen Zufriedenheit - das Problem, das ich mit dem Threadtitel angesprochen habe, war: Gäbe es eine OOP-Lösung, um TQueryResultClass zu ändern? Änderungsziel: Gleiches Funktionieren, wie ursprünglich implementiert, aber mit andern Bezeichnern. Aber erstmal, was TQueryResultClass tut (und mehr hat diese Klasse auch nicht zu tun):
Delphi-Quellcode:
procedure TFDMySQLDml.SelectBildDaten(FCategoryKey : Integer);
  var SQLString: String; Kath_Id : integer;
begin
  Kath_Id := FCategoryKey;
  SQLString := DefineBildSQL3(Kath_Id); // Liefert den SQL-String zurück
  FDQueryMain.Connection := Self.FDConnectionMySql;
  FDQueryMain.Close;
  FDQueryMain.SQL.Text := SQLString;
  FDQueryMain.Params.CreateParam(ftInteger, 'Kath_Id', ptOutput);
  FDQueryMain.Params[0].AsInteger := Kath_Id;
  FDQueryMain.Open;
  FDQueryMain.First;
  While (Not FDQueryMain.Eof) do
  begin
    FQueryResult := TQueryResultClass.Create(Self);
    FQueryResult.IdBild := (FDQueryMain.FieldByName('BildId').AsInteger);
    [QUOTE]FQueryResult.BildDescribeTabelle.BilddesribeID := FDQueryMain.FieldByName('BildDescribeId').AsInteger;[/QUOTE]
    FQueryResult.BildDescribeTabelle.bildkatID := FDQueryMain.FieldByName('bildkatID').AsInteger;
    FQueryResult.BildDescribeTabelle.BildName := FDQueryMain.FieldByName('BildName').AsString;
    FQueryResult.BildDescribeTabelle.BildBeschreibung := FDQueryMain.FieldByName('Bildbeschreibung').AsString;
    FQueryResult.BildDescribeTabelle.BildLegende := FDQueryMain.FieldByName('Bildlegende').AsString;
    FQueryResult.KategoryTabelle.Kategory := FDQueryMain.FieldByName('Kategorie').AsString;
    FDQueryMain.Next;
    FCategoryBildlist.Add(FQueryResult); // Die Records in TQueryResultClass müssen überprüft werden.
  end;
  FDQueryMain.Close;
end;
Wie ersichtlich ist: Weder das Query noch TQueryResultClass verfügen in dieser Abfrage über einen Stream für Blobdaten. Interesieren tun hier nur gerade mal die Begleitdaten zum Bild.
Das besondere, weswegen es TQueryResultClass überhaupt gibt, sind seine Bilder-Propertis:
Delphi-Quellcode:
    property Thumbnail: TMemoryStream read GetThumbnail write SetThumbnail;
    property Bitmap: TMemoryStream read GetBitmap write SetBitmap;
Die beiden Getter, aam Beispiel von GetThumbnail:
Delphi-Quellcode:
function TQueryResultClass.GetThumbnail: TMemoryStream;
  var AUser, APass : String;
begin
  if not Assigned(FThumbnail) then
  begin
    FThumbnail := TMemorystream.Create;
    Result := FillThumbnail(FThumbnail);
  end else
    Result := FThumbnail;
end;
Die Funktion Fillthumbnail:
Delphi-Quellcode:
function TQueryResultClass.FillThumbnail(var Thumbnail: TMemoryStream):TMemoryStream;
var
  BildID: Integer;
  BlobStream: TStream;
  LNull: string;
  SQLString: string;
begin
    BildID := Self.FidBild;
    SQLString := 'SELECT Bildtabelle.Thumbnail as Thumbnail FROM Bildtabelle WHERE Bildtabelle.idBild = :BildID';
    FDMySQLDml.FDQueryMain.SQL.Text := SQLString;
    FDMySQLDml.FDQueryMain.Params.CreateParam(ftInteger, 'BildID', ptInput);
    FDMySQLDml.FDQueryMain.Params[0].AsInteger := BildID;
    FDMySQLDml.FDQueryMain.Open;
    FDMySQLDml.FDQueryMain.First;
    while not FDMySQLDml.FDQueryMain.Eof do
    begin
      if not FDMySQLDml.FDQueryMain.FieldByName('Thumbnail').IsNull then
      begin
        BlobStream := FDMySQLDml.FDQueryMain.CreateBlobStream(FDMySQLDml.FDQueryMain.FieldByName('Thumbnail'), bmread);
        BlobStream.Position := 0;
        FThumbnail.CopyFrom(BlobStream, Blobstream.Size);
        BlobStream.Free;
        FDMySQLDml.FDQueryMain.Next;
        Result:= FThumbnail;
      end
      else
      begin
        LNull := 'Kein Thumbnail vorhanden';
        FThumbnail.WriteBuffer(LNull, SizeOf(LNull));
      end;
    end;
    FDMySQLDml.FDQueryMain.Close;
end;
Das ist das, was geschieht, wenn zur Darstellung der Datensätze auch auf die Bildpropertys von TQueryResultClass zugegriffen wird.

Und nochmal zur Übereinstimmung von Klassen-/Feld und Tabellen/Feldnamen: Der Klasse tuts nicht weh, wenn ihre Felder gleich heissen, wie diejenigen der Tabelle, und zu Fehlfunktionen kann es auch nicht kommen. Aber was liest sich besser:
Delphi-Quellcode:
FQueryResult.BildDescribeTabelle.bildkatID := FDQueryMain.FieldByName('bildkatID').AsInteger;
// oder
FQueryResult.Id := FDQueryMain.FieldByName('bildkatID').AsInteger;
Von den drei beteiligten Tabellen hat jede eine Id als Primärschlüssel und eine als Fremdschlüssel,und alle sind sie wichtig. Bei übereinstimmenden Namen kannst du in der Zuweisung nicht irren, bei grundsätzlich verschiedenen Namen hingegen schon.

Zitat von DeddyH:
Zitat:
Je mehr ich lese, desto mehr frage ich mich: wozu das Ganze? Soll das ein ORM werden/sein? Dann haben IMO die Tabellen- bzw. Feldnamen in den Objekten nichts zu suchen, die Objekte sollte es nicht interessieren, woher die Daten kommen oder wohin sie gehen, dafür ist eine Schicht zuständig. So wie ich es sehe bringt der ganze Aufwand momentan 0% Nutzen (sofern ich das richtig überblicke, ich kann mich auch irren), sondern sorgt eher für Verwirrung.
Gewissermassen ja. Das ganze, insbesondere der Teil mit dem Nachladen der Bilddaten, ist unter anderem mit deiner tatkräftigen Mithilfe (auf Delphi Treff) entstanden. Dein Kommentar damals: "Ein Mini-Orm".
Ein ORM bildet ja eine komplette Datenbank mit Hilfe von zB. Delphi-Objekten ab. Aber eben eine komplette Datenbank. Meine Klasse gestattet das Nachladen der Bilddaten bei Bedarf und das Iterieren durch die Ergebnismenge bei geschlossenem Query oder sogar geschlossener Verbindung, mehr nicht.


Gruss
Delbor
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.358 Beiträge
 
Delphi 11 Alexandria
 
#6

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 16:53
Gäbe es eine OOP-Lösung, um TQueryResultClass zu ändern? Änderungsziel: Gleiches Funktionieren, wie ursprünglich implementiert, aber mit andern Bezeichnern.
Ok, also ist Deine Kernfrage, wie Du die Properties der Klasse und die Klasse selbst am einfachsten umbenennen kannst?
Da wäre zu sagen: Rechtsklick / Refactoring / Umbenennen.
Das kannst Du für die Properties sowie Getter und Setter machen.

Was Du in diesem Zusammenhang mit OOP meinst, kann ich nicht nachvollziehen.

Insgesamt würde ich aber weiterhin zu einem anderen Ansatz raten.
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Delbor

Registriert seit: 8. Okt 2006
Ort: St.Gallen/Schweiz
1.196 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 19:02
Hi stahli

Zitat:
Da wäre zu sagen: Rechtsklick / Refactoring / Umbenennen.
In etwa so ist QueryCMResultClass entstanden, wobei ich in den meisten Fällen den Sync-Arbeitsmodus verwendet habe.
Die Frage zielte eigentlich hauptsächlich darauf ab, wie ich das nächste mal vorgehen muss, um die Sache möglichst einfach durchzuziehen.
Vor kurzem habe ich ein Interface-Testprogrämmchen begonnen.
Wenn ich die Sache richtig verstanden habe, müsste ich TQueryResultClass statt von TPersistent von TInterfacedobject, IMeinInterface abbleiten, letzteres inklusive der Member deklarieren und die Member in TQueryResultClass implementieren. Somit hätte ich, wenn ich das richtig verstehe, durchaus eine OOP-Lösung.

Gruss
Delbor
Angehängte Grafiken
Dateityp: jpg 19_49_52-Klassen_QueryCMResultUnit.pas.jpg (78,5 KB, 12x aufgerufen)
Roger
Man muss und kann nicht alles wissen - man muss nur wissen, wo es steht.
Frei nach Albert Einstein
http://roase.ch

Geändert von Delbor (19. Okt 2017 um 19:07 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.358 Beiträge
 
Delphi 11 Alexandria
 
#8

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 19:55
Irgendwie verstehen wir uns nicht so richtig...

Also mit OOP arbeitest Du ja schon, da Du mit Klassen und Objekten arbeitest.

Mit Interfaces zu arbeiten ist schon auch sinnvoll, bedingt aber einige Einarbeitungszeit und erhöht den Schreibaufwand, da man die Klassenmember immer zwei mal schreiben muss und Delphi dabei wenig unterstützt.
Mit der automatischen Referenzzählung hat man ggf. einen Vorteil, weil man sich nicht um die Lebenszeit der Objekte kümmern muss. Das braucht aber auch einige Zeit, bis man damit richtig umgehen kann und das verinnerlicht.

Was auf jeden Fall eine Hilfe ist, ist die Abstraktion, die man durch Interfaces erhält.

Aber genau gegen diese Abstraktion wehrst Du Dich schon bei Deinen Klassen, weil Du die völlig abhängig von Deinen Datenbanktabellen gestalten willst. Das ist wirklich nicht sinnvoll. Wenn Du jetzt mit Deinen Datenbankabhängigen Klassen noch Interfaces unterstützen willst, dann hast Du noch mehr Aufwand und noch weniger Nutzen.

Was ich oben schon versucht habe zu vermitteln ist, dass die Businessklassen ihren Kram so erledigen sollen, dass sie optimal und logisch arbeiten, ohne irgendeinen Bezug zur Datenbank zu haben.
Dann baust Du eine Datenbank, die Ihre Aufgabe, Daten zu verwalten, gut erledigt. Da eine relationale Datenbank keine Delphi-Klasse ist, wird es in der Datenschicht irgendwie anders aussehen, als in der Business-Schicht. Wurscht!!!!!!
Jetzt brauchst Du einen Vermittler, der Daten aus den Objekten in die Datenbank schreibt oder in der anderen Richtung in die Objekte lädt.

Wenn Du das versuchen würdest, hättest Du eine gute Projektstruktur mit einer (relativ) guten Entkopplung der einzelnen Schichten.


Wenn das getan ist - und wirklich erst dann! - könnte man die Entkopplung noch weiter treiben und Interfaces einführen.
Das wäre aber der I-Punkt auf eine saubere Trennung der einzelnen Schichten.


Also mein Tipp fürs nächste Mal:

1) Businessklassen für die Buisnesslogik (ohne Abhängigkeit und zwanghafte Namensgleichheit zur Datenbank).
2) Datenbank in sich sinnvoll aufbauen (ohne Abhängigkeit auf die Klassen)
3) Datenbankmanager zum Speichern und Laden von Daten.
4) GUI nur mit Bezug auf die Business-Schicht
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:55 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz