Announcement

Collapse
No announcement yet.

Delphi Datentyp und/oder LookUp-Tabelle?

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Delphi Datentyp und/oder LookUp-Tabelle?

    <b>Folgende Problemstellung:<br>
    </b>Es gibt eine Artikeltabelle. Jeder Artikel soll einer Serie angehören.
    Anhand der Serie sollen im Programm verschiedene Aktionen durchgeführt werden. z.B.: Steuerung der Formularanzeige, andere Berechnungsgrundlage für Preise usw. Es soll voreingestellt z.B.: 3 Serien geben. Bei einem späteren Update kann es passieren, das eine Serie wegfällt bzw. eine Serie hinzukommt. Außerdem soll der Benutzer frei definierbare Serien anlegen/löschen können.
    <br>
    <b>Lösungsansätze:<br>
    </b>Sollte ich einen neuen Typ in Delphi erstellen (z.B.: TSerie = (Serie1, Serie2, Serie3)?
    <u>Vorteil: </u>Ich kann bsp. dem Formular eine Variable vom Typ TSerie
    übergeben und anhand dieser Serie die Anzeige des Formulars bzw. die Berechnung des Artikel steuern; Ich muss dann halt nur alle Varianten abfragen;
    <u>Nachteil:</u> Ich habe keinen wirklichen Klartext für die Serie (könnte über ein Array gesteuert werden); Ich kann in SQL Anweisungen nur mit &quot;loser Integer Zuordnung&quot; hantieren und auch keinen Klartext in Selectanweisungen ausgeben; Wegfallen einer Serie verändert die Aufzählungsreihenfolge; Der Benutzer kann die Serien nicht erweitern.
    <br>
    Sollte ich eine Lookuptabelle erstellen, die eine ID und einen Namen der Serie enthält und diese mit der Artikeltabelle verknüpfen?
    <u>Vorteil: </u>Ich habe einen Klartext zu jeder Serie; Klartext bei
    Selectanweisungen; Benutzer kann Serien erweitern; Durch ForeignKey kann keine Serie gelöscht werden, der noch Artikel angehören;
    <u>Nachteil:</u> Ich kann im Formular (Delphi) nur mit losen ID's hantieren um die Ausgabe/Berechnung zu steuern!
    <br>
    Sollte ich eine Mischung aus beiden verwenden?
    <u>Nachteil:</u> Ich muss die Tabelle immer mit dem Datentyp abgleichen.
    <br>
    Weitere Probleme:
    Was passiert, wenn der Endbenutzer selber Serien eingeben darf und ich später Datentyp erweitern muss?
    Dann stimmt die ID (Tabelle) zu Datentyp-Index zuordnung nicht mehr.<br>
    Was passiert, wenn eine Serie wegfällt und ich einen Delphitypen verwende?
    Dann stimmt die Aufzählungsreihenfolge nicht mehr!
    <br>
    Was ist nun besser bzw. wie kann man es am besten lösen?
    <br>
    Grüße
    M. Pannier
    <br>
    P.S.: Ich hoffe die Problemstellung ist einigermaßen verständlich formuliert.

  • #2
    Hallo!

    Ändert sich denn die Datenstruktur von Serie zu Serie?
    Dann wird das heftig... Hast Du Dir mal objektorientierte Datebanken angesehen?? Vielleicht hilft das dann eher weiter..

    Wenn die Datenstruktur immer gleich ist ist es doch leicht möglich einzelne Felder ein oder auszublenden.
    Eine Ansteuerungstabelle
    DBSerie
    ID_DBSerie integer
    Bezeichnung varchar(30)
    Feld1Sichtbar varchar(1)
    etc..
    ist dann ja kein Problem.

    Den "Rechenweg" koppelt man dann komplett vom Formular ab und erzeugt verschiedene "RechenObjekte" für die einzelnen Serien.

    BYE BERN

    Comment


    • #3
      Die Datenstruktur ändert sich nicht von Serie zu Serie. Ich versuche es nochmal etwas besser zu erläutern.

      Als erstes schreibe ich in eine ComboBox alle Serien. (Entweder aus einer LookupTabelle oder halt aus einem Array DelphiTyp Konstrukt) Je nachdem was für Serie ausgewählt wurde, ändere ich das Formular bzw. die Berechnungsart. An dieser Stelle muss ich dann die Serie Abfragen (Siehe PseudoCode)
      <br>
      Variante über Deplhi Typ und Array:
      <br>
      TSerie = (s1, s2, s3); //Der Index im Array entspricht dem ordinalen Wert von TSerie
      ArraySerie : array[0..2] of string ('Serie1', 'Serie2', 'Serie3');

      for i := low(ArraySerie) to high(ArraySerie) do
      ComboBox.Add(ArraySerie[i]);

      var Serie: TSerie;
      Serie := TSerie(ComboBox.ItemIndex); //ItemIndex = ArrayIndex = ordinal Wert von TSerie
      if Serie = s1 then //an dieser Stelle kann ich schön den Datentyp abfragen
      doSomething
      else if (Serie = s2) or (Serie = s3) then
      doSomething
      else doSomethingElse
      <br>
      Variante über LookUpTabelle
      <br>
      DataSet.Open; //(Select * from LookUp)
      while not(DataSet.EoF) do
      ComboBox.Add(DataSet.Fields[1].AsString); //als Value wird Field[0] (ID aus Tabelle) verwendet

      var Serie: Integer;
      Serie := ComboBox.Value; //Index muss nicht gleich ID aus Tabelle sein
      if Serie = 0 then //an dieser Stelle kann ich nur über "lose" ID's abfragen
      doSomething
      else if (Serie = 1) or (Serie = 2) then
      doSomething
      else doSomethingElse
      <br>
      Ich hoffe das veranschaulicht das Problem etwas besser.
      Vermutlich ist eine Mischung der beiden Varianten das Beste, aber wie löse ich dann die Probleme:
      1. Doppelter Aufwand der Datenpflege (DelphiTyp, Array und Tabelle synchronisieren)
      2. Wenn Benutzerdef. Serien möglich sind (durch Benutzereingabe)
      3. Entfernen bzw. hinzufügen einer Serie (DelphiTyp zu TabellenIndex stimmt dann nicht mehr, ID's in Tabelle nicht mehr in einer Reihenfolge

      Comment


      • #4
        Die Konstruktion mit den If Abfragen zeigt schon das Problem...

        So kannst Du eine Verallgemeinerung nicht angehen.

        Die Daten in der LookupTabelle müssen sämtliche Informationen darüber enthalten, welche Felder im Form sichtbar sind und welche nicht.
        Jetzt kannst du das Erscheinungsbild des Formulares schon komplett neu gestalten, in dem Du nur eine weitere Zeile in die LookUp Tabelle einfügst.

        Jetzt wirds etwas schwieriger....
        Statt Deiner if Konstruktionen musst Du für jede Berechnungsart ein Objekt schreiben:
        Basis Klasse TStandardberechnung
        function Addieren (a, b : float ) : float; //Addiert einfach beide Zahlen und liefert das Ergebnis zurück

        Serie1 TSerie1Berechnung
        function Addieren (a, b : float ) : float; //Addiert beide Zahlen und rechnet die MwSt drauf (oder sonstwas)

        Somit bestimmt die Implementierung in der Klasse das Verhalten bei der Berechnung.

        Um jetzt noch für jede Serie die richtige Klasse zu erzeugen brauchst Du eine "Abstract Factory" Erklärungen dazu z.B. bei http://delphi.about.com/gi/dynamic/offsite.htm?site=http://www.obsof.com/delphi%5Ftips/pattern.html

        Wenn Du solche Projekte abwickeln willst musst Du dich mit Dingen wie Design Patterns auseinandersetzen... Sonst erzeugst Du nur if if if Code, denn weder Du noch jemand anderes später warten kann!!!

        Das mit den IFs würde ich definitiv sein lassen. In unserer eigenen Warenwirtschaft haben wir ähnlich gelagerte Probleme, die wir aber durch Ausnutzung des obigen Systems komplett gelöst haben.
        Wenn ein Kunde die hundertste Serie haben möchte, bei der wieder alles anders berechnet wird... KEIN PROBLEM einfach ein neues Objekt mit den Rechenfunktionen schreiben eine Zeile in der Lookup einfügen und fertig....

        BYE BERN

        Comment


        • #5
          OK danke erst mal.
          Aber ich muss ja trotzdem abfragen, welche Serie denn der Artikel angehört, um dann das jeweilige Objekt zu erzeugen oder

          Comment


          • #6
            Ja klar aber das ist dann ja nur eine weitere Spalte in der Lookup Tabelle... RechenObjekt : String
            Der Witz an der Abstract factory ist, dass man nur mit dem Namen des Objektes ein fertig erzeugtes Objekt bekommt.
            Also
            var MeinRechenobjekt : TRechenBasisKlasse;
            MeinRechenobjekt := MeineAbstractFactory( lookuptable.Rechenobjekt);
            und fertig (wieder keine IFs).

            Jetzt noch die einzelnen Berechnungsklassenin ein Package auslagern und ich muss für eine neue Serie das Basisprogramm noch nicht mal neu kompilieren!!! Einfach nur das Package ausliefern und in der Datenbank den Namen der neuen Berechnungsklasse einsetzen..

            Comment


            • #7
              Hallo,
              Danke nochmal für die Antworten.
              <br>
              Ich hab mir die Internet Seite mit der Abstract Factory angeschaut. So wie ich das sehe, ist das normale OOP Technik - Vererbung mit Überschreiben von geerbten Methoden! Oder?
              -->MeinRechenobjekt := MeineAbstractFactory( lookuptable.Rechenobjekt); --> Kommt aus dem Beispiel nicht so recht rüber wie das gemeint ist!
              Was ist wenn die Berechnung von mehreren Faktoren abhängig ist. In meinem o.g. Bsp. Wenn Serie = XX und Berechnungsart = XX und dieses = jenes. Sollte ich dann für jede Möglichkeit ein abgeleites Objekt erstellen?
              <br>
              Da werd ich mich nochmal etwas genauer belesen (zu Pattern bzw. Abstract Factory).
              <br>
              Nochmal ganz zurück zum Anfang: Warum mit Kanonen auf Spatzen schießen...? OK, bei dem oben beschriebenen Problem sehe ich die Komplexität ein, aber was ist bei einfacheren Sachen, bei denen nur an einer Stelle die Werte abgefragt werden müssen? Oder wenn man eine Datenbank Domain hat, die nur 2 oder 3 Werte zulässt? Wie bekommt man dort zum einen eine anständige Abfrage in Delphi hin und zum anderen eine Klartextanzeige bei Select Abfragen? Oder aus welchen Werten fülle ich bsp. eine ComboBox, um einen solchen Wert eingeben zu können?
              <br>
              Danke nochmal
              Grüße
              M.Pannie

              Comment


              • #8
                Das einzige was bei einem Projekt sicher ist ist die Veränderung.
                Jeder Lösungsansatz, der in einer Kette von IFs endet fährt früher oder später gegen die Wand.
                Wir machen in Verlagssoftware und da haben wir es bei jeder einzelnen Installation IMMER mit Abweichungen zu tun, an die vorher keiner gedacht hat. Da werden aus 2 oder 3 übergebenen Werten mal ganz schnell komplette Geschäftsprozesse.

                Wenn in kleinen Teilbereichen die Konfiguration mit eigenen Datentypen oder LookupTables ausreicht dann würde ich den Lookups immer den Vorzug geben....
                Ein Programm, dass bei Änderungen nicht neu kompiliert werden muss ist in der Regel leichter zu pflegen und mit weniger "spannenden" Seiteneffekten gesegnet.

                >Sollte ich dann für jede Möglichkeit ein abgeleites Objekt erstellen?
                Klar warum nicht. TRechnenMitMwst TRechnenOhneMwst TRechnenMitRabatt etc.....

                Wenn der Anwender die Serien selbst erweitern können soll kommst Du doch um eine Lookup nicht herum?!? Er kann ja schlecht die IFs im Programm erweitern.

                Die Anzeige und Abfrage ist mit einer Lookup ja kein Problem. Im Artikel habe ich einen Verweis auf die Serie (Serie_ID). Im Delphi-TTable Objekt kann man ja dann sogar per Dialog Nachschlagefelder hinzufügen.
                ArtikelTabelle:
                ID_Artikel
                Artikelnummer
                Bezeichnung
                Serie_id <=Verweis auf die entsprechende Serie

                SerienTabelle:
                ID_Serie
                Bezeichnung

                Jetzt kann ich per Abfrage den Namen der Serie in die Artikelliste mit einbeziehen. select a.*, s.bezeichnung as Serie from artikel a, serie s where s.id_serie=a.serie_id (ungetestet)
                Das Ganze wird etwas komplizierter, wenn ein Artikel zu mehreren Serien gehören kann (n:m Verbindung)

                Oder habe ich Dein Problem jetzt falsch verstanden?!?!???

                PS:Welche Datenbank eigentlich?

                BYE BERN

                Comment


                • #9
                  Hallo,
                  Ich denke ich versteh es langsam... Und Du hast mein Problem auch soweit richtig verstanden.
                  Ich verwende InterBase bzw. FireBird.
                  Hast Du evtl. eigene/bessere Codebeispiele für die Abstract Factory? Und das evtl noch in Delphi?
                  <br>
                  Nochmal zum Thema Domain: Ein besseres Beispiel ist das Feld Berechnungsart aus der Artikeltabelle. Dort sollen "zur Zeit" folgende Werte möglich sein: Listenpreiskalkulation und Aufschlagskalkulation. Würdest Du dafür eine LookUpTabelle oder eine Domain mit zwei festen Werten verwenden? In Delphi würd ich jetzt ein Objekt TArtikel bauen und davon T_L_Artikel und T_A_Artikel ableiten. (Nach deinen Ausführungen!) Zu klären wäre noch wie ich dann mit der Abstract Factory welches Objekt (Produkt) erzeuge.
                  Vorher habe ich halt überlegt, wie ich diese Unterscheidung am besten in Delphi vornehmen kann. Und da viel mir erst mal nur ein eigener Datentyp ein.(Siehe vorherige Beiträge mit Serie). Nur ist das halt nicht das einzige Unterscheidungsmerkmal eines Artikels...
                  <br>
                  Danke nochmal und Gruß
                  M.Pannie

                  Comment


                  • #10
                    Erstmal ganz klein:
                    Wichtig ist die gemeinsame Basisklasse für alle Berechnungsobjekte
                    TBerechnung
                    function GetPreis : real; virtual;
                    property ArtikelNummer : integer....

                    TListenPreis (TBerechnung)
                    function GetPreis : real; override;
                    TAufschlagPreis (TBerechnung)
                    function GetPreis : real; override;
                    Diese beiden Klassen berechnen mit Hilfe der überschriebenen Funktionen GetPreis den jeweilig benötigten Preis.

                    Eine Minilösung für Dich wäre
                    function GetBerechnungsklasse (Art : String): TBerechnung
                    if art = 'LISTE' then result := TListenpreis.create;
                    if art = 'AUFSCHLAG' then result := TAufschlagPreis.create;

                    So jetzt kannst Du schon anhand des Namens das jeweils korrekte Berechnungsobjekt erzeugen. Jedes neue Berechnungobjekt erzwingt aber eine Neukompilierrung des Gesamtprogrammes.

                    Wenn die Berechnungen komplizierter werden und unterschiedliche Felder für die Berechnung an das Berechnungsobjekt übergeben werden müssen kannst Du das berücksichtigen, in dem Du einen Verweis auf den TTable an das Objekt übergibst. Dann kann sich jedes Objekt die Felder nehmen, die es braucht. Alternativ kann das Objekt auch selbst eine Verbindung zur Datenbank aufbauen um noch komplett andere Daten abzufragen. (Sowas haben wir z.B. bei der Preisfindung realisieren müssen)

                    Ein einfaches Delphibeispiel habe ich leider nicht, da wir in unserem Programm das Problem weiter abstrahiert haben und nur noch mit Interfaces arbeiten. Aber: http://www.obsof.com/delphi_tips/pattern.html macht einen vernünftigen Eindruck. (Verwendung von Interfaces siehe z.B. <a href="/webx?13@@.1dd04003/12">Bernd Schulz "Polymorphie geht nicht Hilfe!" 07.03.2003 21:11</a>)

                    Wenns trotzdem nicht funzt erstmal minimal implementieren und dann in aller Ruhe die "XXL Spatzenkanone" nachrüsten...

                    Zum Thema Delphi und Patterns gibt es auch recht vernünftige Literatur z.B. fand ich http://www.software-support.biz/sus/sus_buch/psecom,id,50,nodeid,11,_language,de.html recht hilfreich.

                    BYE BERN

                    Comment


                    • #11
                      Guten Morgen,
                      Das mit den Objekten hab ich verstanden und wie ich ja bereits festgestellt habe, sind das normale OOP Techniken (Vererbung). Die Beispiel-Seite kenne ich bereits. Nur ist die Abteilung Abstract Factory etwas knapp ausgefallen. Danke auch für den Literaturvorschlag.
                      Wie treffend von Dir gesagt, werd ich erst mal klein anfangen und dann die Kanonen bauen...
                      Gruß
                      M.Pannie

                      Comment

                      Working...
                      X