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
Seite 3 von 5     123 45      
Benutzerbild von DeddyH
DeddyH

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

AW: OOP wirklich nicht möglich?

  Alt 18. Okt 2017, 10:50
Man kann in einer Interface-Deklaration überhaupt nichts implementieren, das ist ja gerade deren Sinn. Wenn Du von außen auf ein Interface zugreifst, weißt Du zunächst einmal nur, dass die dahinterliegende Objektinstanz garantiert alle Eigenschaften besitzt und Methoden implementiert, die im Interface vereinbart sind.
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.343 Beiträge
 
Delphi 11 Alexandria
 
#22

AW: OOP wirklich nicht möglich?

  Alt 18. Okt 2017, 13:18
@Delbor

Ich denke, Du musst Deinen Grundsatzüberlegungen nochmal etwas ordnen und strukturieren.

Nimm Dir mal ein Blatt Papier und zeichne Dir mal einen Plan, wo welche Zuständigkeiten geregelt werden sollen. Je klarer Dir das gelingt, je strukturierter wird Dein Programm aufgebaut sein.

Für Außenstehende ist vermutlich schwer nachzuvollziehen, was Du aktuell genau vorliegen hast und was Du ändern willst.

Z.B. ist m.E. bisher nicht klar geworden, wie diese Komponenten
Delphi-Quellcode:
    FTblBildText : TTblBildText; // FBildDescribeTabelle : TBildDescribeTabelle;
     FTblAlbum : TTbl_Album; // FKategoryTabelle : TKategoryTabelle;
aufgebaut sind. Haben sie noch eine Verbindung zur Datenbank oder nicht?
Wenn nicht, warum sind es dann unterschiedliche Klassen und warum sind die Felder unterschiedlich benannt?
Wenn ja, warum nimmst Du nicht eine Klasse, die die Daten unabhängig von einer Datenbank verwaltet und bearbeitet?

Wie viele Daten verwaltest Du insgesamt in der Anwendung? Können alle Daten insgesamt im Speicher gehalten werden oder ist die Datenbank so groß, dass immer nur bestimmte Datensätze daraus abgeholt werden können?

Das sind viele grundsätzliche Fragen, die eigentlich erst mal geklärt werden müssten und zu entsprechend unterschiedlichen Lösungen führen werden.

Wie in #12 schon mal angesprochen, musst Du Dir eine übersichtliche Struktur überlegen, die klare Zuständigkeiten und Verbindungen verschiedener Projektmodule abbildet.

Dafür gibt es dann je nach den Gegebenheiten verschiedene Lösungsmöglichkeiten.


Was ich generell nicht verstehe ist, dass Du Deine Businessklassenstruktur änderst, wenn Du die Datenbank wechselst oder die Tabellen in der Datenbank andere Namen haben.

Nach meiner Überlegung müsstest Du in Deinem Projekt das "Datenbankmodul" anpassen, aber nicht die Businessklassen.


Interfaces zu verwenden kann sinnvoll sein, muss es aber nicht in jedem Fall. Wenn Du damit noch keine Erfahrungen hast, würde ich die Projektstruktur erst mal überarbeiten und auf Interfaces verzichten. Da hängt noch einiges an notwendigen Anpassungen dran, die jetzt vielleicht unnötig verwirren würden.

Statt dessen solltest Du eine Datenbankunit oder Datenbankklasse einführen, die alle Zugriffe auf die alte Datenbank kapselt - so dass Deine Anwendung nicht mehr die Datenbank selbst kennt, sondern nur noch Deine Datenbankunit oder Datenbankklasse.
Dann kannst Du eine neue Datenbankunit oder -Klasse aufbauen, die die selben Schnittstellen nach außen hat, aber intern auf eine andere Datenbank geht.

So hättest Du schon mal eine gute Trennung der Zuständigkeiten.
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.186 Beiträge
 
Delphi 11 Alexandria
 
#23

AW: OOP wirklich nicht möglich?

  Alt 18. Okt 2017, 17:10
Hi zusammen

@ stahli
Zitat:
FTblBildText : TTblBildText; // FBildDescribeTabelle : TBildDescribeTabelle;
FTblAlbum : TTbl_Album; // FKategoryTabelle : TKategoryTabelle;
Grundsätzlich trennt mein Programm GUI und Datenhaltung. Letzteres fällt in die Zuständigkeit zweier Datenmodule; je eines für eine Verbindung zu MySQL und eines, relativ neu in Arbeit, zu SQLite.
Die Datenbank selbst beinhaltet 12 Tabellen, wovon zurzeit nur 4 für Bild- und Textinhalte zuständig sind. Von drei dieser Tabellen gibt es innerhalb von TQueryresultClass Entsprechungen gleichnamiger Klassen.
Das obige Zitat gibt 2 Klassenfelder meiner gleichaufgebauten Klassen TQueryCMresultClass(FTblBildText ,FTblAlbum) und TQueryresultClass(FBildDescribeTabelle,FKategoryTa belle) wider.
Ich hänge hier mal noch TQueryCMresultClass an, um die Unterschiede zu verdeutlichen; diese bestehen ausschlieslich aus den in TQueryCMresultClass verwendeten kürzeren Tabellen- und Feldbezeichnern.
Natürlich wurde die SQLite_Datenbank auch mit den verkürzten Tabellennamen erstellt.
Zitat:
Haben sie noch eine Verbindung zur Datenbank oder nicht?
Wie bereits gesagt, handelt es sich hierbei um Klassenfelder, bzw. Unterklassen, die selbst keine Verbindung zur Datenbank haben.
Bei Select-Abfragen der TQuery-Komponente werden deren Resultate jeweils pro Datensatz über 3 Tabellen (ohne Zwischentabelle) in einer TQueryCMresultClass - Instanz festgehalten und diese einer Objectlist hinzugefügt. Dabei gibt es eine Besonderheit: Bilddaten werden bei der ursprünglichen Abfrage über mehrere Datensätze mit Ausnahme des Thumbnails vorerst nicht abgefragt, sondern erst, wenn sie zur Darstellung des Bildes benötigt werden.

Ein Ziel meiner Umstellung auf SQLite: von mir so genannte "Satelliten-DBs" zu erstellen(*). Das hat folgende Grund: Zur Zeit speichere ich die Bilder noch in einem Ordner auf Festplatte. Wenn die Dinger schliesslich in die DB geschrieben werden, werden aus den Rohbildern nicht nur die Thumbnails, sondern auch gleichzeitig Bitmaps in Originalgrösse erstellt. Dabei sollten auch die Rohdaten ursprünglich mit in die DB - und blähen diese zu gigantischer Grösse auf.
Und hier kommen dann meine "Satelliten-DBs" zum Zug: In ihnen werden die Rohdaten und Originalbitmaps zusammen mit einem Textfeld für den Namen und einem FK-Integer für das Album gespeichert. Diese Dinger bringen es auf Grössen von in etwa 25GB oder weniger, sind also eigentlich sogar als eine Art Rohdatenbackup zu gebrauchen und können Extern an beliebigen Orten gespeichert werden.
Wo sich diese "Satelliten-DBs" befinden, bestimmt letzlich der User über ein Optionen-Fenster.

Zitat:
Wie viele Daten verwaltest Du insgesamt in der Anwendung? Können alle Daten insgesamt im Speicher gehalten werden oder ist die Datenbank so groß, dass immer nur bestimmte Datensätze daraus abgeholt werden können?
Als ich das letzte mal gezählt habe, waren es ca. 13 000 Fotos.
Zitat:
Was ich generell nicht verstehe ist, dass Du Deine Businessklassenstruktur änderst, wenn Du die Datenbank wechselst oder die Tabellen in der Datenbank andere Namen haben.
An den Strukturen der Klassen ändert sich ja nichts - nur die verwendeten Namen sind kürzer geworden. Am Beispiel eines SQL-Statements:
Delphi-Quellcode:
function TFDMySQLDml.DefineBildSQL3(Kath_Id: Integer) : String;
begin
Result := 'SELECT Bildtabelle.idBild as BildID, ' +
            'bilddescribetabelle.BilddesribeID as BildDescribeId, ' +
            'bilddescribetabelle.bildkatID as BildkatID, '+
            'bilddescribetabelle.bildname as Bildname, ' +
            'bilddescribetabelle.bildbeschreibung as Bildbeschreibung, '+
            'bilddescribetabelle.bildlegende as bildlegende, ' +
            'kategorien_tabelle.Kath_ID as KathID, ' +
            'kategorien_tabelle.Kategorie as Kategorie, '+
            'kategorien_tabelle_has_bilddescribetabelle.kategorien_tabelle_Kath_ID as TblKat_Id, '+
            'kategorien_tabelle_has_bilddescribetabelle.BildDescribeTabelle_BilddesribeID as BildDesc_Id '+
          'FROM ' +
            'bildtabelle, bilddescribetabelle, ' +
            'kategorien_tabelle_has_bilddescribetabelle, ' +
            'kategorien_tabelle '+
          'WHERE '+ {erste Tabelle}
            'Kategorien_tabelle.Kath_Id = :Kath_Id '+
          'AND '+ {zweite (Selektionstabelle) Tabelle wird mit erster verglichen}
            'kategorien_tabelle_has_bilddescribetabelle.kategorien_tabelle_Kath_ID = Kategorien_tabelle.Kath_Id ' +
          'AND '+ //--------------------
            'kategorien_tabelle_has_bilddescribetabelle.BildDescribeTabelle_BilddesribeID = bilddescribetabelle.BilddesribeID ' +
          'AND '+
            'Bildtabelle.idBild = bilddescribetabelle.bildtabelle_idbild';
// Showmessage(Result);
end;
Das dürfte viel schwerer zu lesen sein als:
Delphi-Quellcode:
function TFDMySQLDml.DefineBildSQL3(Kath_Id: Integer) : String;
begin
Result := 'SELECT Tbl_Bild.idBild as BildID, ' +
            'TblBildText.BilddesribeID as BildDescribeId, ' +
            'TblBildText.bildkatID as BildkatID, '+
            'TblBildText.bildname as Bildname, ' +
            'TblBildText.bildbeschreibung as Bildbeschreibung, '+
            'TblBildText.bildlegende as bildlegende, ' +
            'TblAlbum.Kath_ID as AlbumId, ' +
            'TblAlbum.Kategorie as Kategorie, '+
            'TblAlbum_has_TblBildText.TblAlbum_Album_Id as TblKat_Id, '+
            'TblAlbum_has_TblBildText.TblBildText_BilddesribeID as BildDesc_Id '+
          'FROM ' +
            'bildtabelle, TblBildText, ' +
            'TblAlbum_has_TblBildText, ' +
            'TblAlbum '+
          'WHERE '+ {erste Tabelle}
            'TblAlbum.Album_Id = :Album_Id '+
          'AND '+ {zweite (Selektionstabelle) Tabelle wird mit erster verglichen}
            'TblAlbum_has_TblBildText.TblAlbum_Album_Id = TblAlbum.Album_Id ' +
          'AND '+ //--------------------
            'TblAlbum_has_TblBildText.TblBildText_BilddesribeID = TblBildText.BilddesribeID ' +
          'AND '+
            'Bildtabelle.idBild = TblBildText.bildtabelle_idbild';
// Showmessage(Result);
end;
Wobei ich jetzt nur mal einiges ersetzen lassen habe - zu Demozwecken in einer neuangelegten, aber nicht gespeicherten Unit.
Zitat:
Nach meiner Überlegung müsstest Du in Deinem Projekt das "Datenbankmodul" anpassen, aber nicht die Businessklassen.
Hmmm - was verstehst du genau unter Businessklassen? In der Schichtenprogrammierung gibt es meines Wissens folgende Ebenen//Schichten:
  1. Die Gui
  2. Die Logik- oder BusinessSchicht und schliesslich die
  3. Datenschicht (Datenmodul(e), DB-Verbindungen)
wobei die oberen nach unten durchgreifen dürfen, aber nicht umgekehrt. Wobei die Daten von der Gui in einem Stringgrid dargestellt werden und das daher auf die Objectliste mit meinen Datenklassen zugreifen muss - das aber entspricht dem Modell(von oben nach unten).

Zitat:
Interfaces zu verwenden kann sinnvoll sein, muss es aber nicht in jedem Fall. Wenn Du damit noch keine Erfahrungen hast, würde ich die Projektstruktur erst mal überarbeiten und auf Interfaces verzichten. Da hängt noch einiges an notwendigen Anpassungen dran, die jetzt vielleicht unnötig verwirren würden.
Deshalb habe ich TQueryCMresultClass zuerst mal analog TQueryresultClass "händisch erstellt, indem ich TQueryresultClass als TQueryCMresultClass abgespeichert und verschiedene Namen durch <Suchen-Ersetzen> geändert habe.
Für Interfaces gibts erstmal ein (umfangreiches?) Testprogramm. Und erst Sachen, die da funktionieren und mir klar ist, warum sie das tun, können in meinem Programm eingebaut werden.

Zitat:
Statt dessen solltest Du eine Datenbankunit oder Datenbankklasse einführen, die alle Zugriffe auf die alte Datenbank kapselt - so dass Deine Anwendung nicht mehr die Datenbank selbst kennt, sondern nur noch Deine Datenbankunit oder Datenbankklasse.
Dann kannst Du eine neue Datenbankunit oder -Klasse aufbauen, die die selben Schnittstellen nach außen hat, aber intern auf eine andere Datenbank geht.
Das sind meine Datenmodule.

(*) abgesehen davon, dass es eigentlich unsinnig ist, als Anwendungsdatenbank einen Server wie MySQL zu verwenden (Ausnahme:EmbeddedServer)

Gruss
Delbor
Angehängte Dateien
Dateityp: pas QueryCMResultUnit.pas (15,9 KB, 3x 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
MichaelT

Registriert seit: 14. Sep 2005
Ort: 4020 Linz
555 Beiträge
 
Delphi 10.3 Rio
 
#24

AW: OOP wirklich nicht möglich?

  Alt 18. Okt 2017, 18:39
Mit OOP Mitteln ist interessante Frage.

In einem Satz formuliert. Ist dein Problem, dass du in der TCMQueryClass SQL Statements drinnen hast?

Deine gecashten Objekte schreiben können sich selbst in DB zurückschreiben, etwas platt formuliert ala object.SetDirty und schon rasseln die SQL Statements.

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?

Falls nur eine Datenbank in Zukunft gefragt ist migriere den Datenbestand.

---

Dein Problem anders definiert heißt der SQL String passt nicht zur DB. In dem Fall bietet sich als billige Variante an die Formatierung des String herauszulösen sofern die Logik für den Zugriff abseits der Basisdatentypen halbwegs indent ist.

Was du brauchst ist eine normiertes SQL. Am besten ist du machst eine 'globale' Variable in der die ZielDB drinnensteht und eine Funktion FormatSQL('SQLStatement....) : String, welche in Abhängigkeit der Ziel DB das richtige SQL zurückgibt.

Die Funktion kann dann die Methode einer Klasse genauso sein und ab dann kannst dahinter rumfuhrwerken wie du willst. Du wirst nicht umhinkommen den Code um zumindest das Klammern der SQL Statements zu erweitern.

SQL.Format(SELECT_VON_TABELLE);

So etwas in die Richtung oder FormatSQL(SELECT_VON_TABELLE) ist an sich pragmatischer.

Konstanten als 'Symbol' für die SQL Statements wirst du leider einführen müssen oder du verwendest String Variablen ... In dem Punkt kann man sich schon ausleben. Wesentlich ist die Behandlung der SQL Statements auf einer 'höheren' Ebene.

Die Prototypen für Formatierung und alles was dazu gehört lagerst du in eine Basisklasse aus. Auf dem Weg geht nicht mehr viel schief. Du kannst die Konstanten in der Klasse definieren usw...

Wenn die Programmlogik sich mit dem Lesen der Blobs massiv ändert müssten wir schauen. Billiger geht es nicht mehr.

Wo du die globale Variable für den Ziel DB Typ ansiedelst kann ich dir jetzt nicht sagen.

Geändert von MichaelT (18. Okt 2017 um 18:57 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

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

AW: OOP wirklich nicht möglich?

  Alt 18. Okt 2017, 20: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 20:11 Uhr)
  Mit Zitat antworten Zitat
Delbor

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

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 12: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
Miniaturansicht angehängter Grafiken
cm-datamodell.jpg  
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.619 Beiträge
 
Delphi 12 Athens
 
#27

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 12: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.343 Beiträge
 
Delphi 11 Alexandria
 
#28

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 12: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.186 Beiträge
 
Delphi 11 Alexandria
 
#29

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 16: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.343 Beiträge
 
Delphi 11 Alexandria
 
#30

AW: OOP wirklich nicht möglich?

  Alt 19. Okt 2017, 17: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
Antwort Antwort
Seite 3 von 5     123 45      


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 14:27 Uhr.
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