Datenstrukturen, Kreuzverlinkungen - wie lösen?

Diese Seite verwendet Cookies. Durch die Nutzung unserer Seite erklären Sie sich damit einverstanden, dass wir Cookies setzen. Weitere Informationen

  • Datenstrukturen, Kreuzverlinkungen - wie lösen?

    Hi!

    Ich möchte eine Verwaltung von Musikinstrumenten programmieren. Jedes Instrument beinhaltet eine Reihe von Klangfarben/Sounds (ich nenne sie presets). Ich hab also eine Klasse Instrument und eine Klasse Preset. In meinem App-Modell hab ich also ein Array instruments: [Instrument]. Die Klasse Instrument beinhaltet wiederum ein Array presets: [Preset] ... in etwa so:

    class Instrument {
    var name: String
    var UUID: String
    var presets: Preset[] = []
    var selectedPreset: Preset? = nil
    }

    class Preset {
    var name: String
    var UUID: String
    var category: Int? = nil
    var linkedPresets: [Preset] = []
    var button: UIButton? = nil
    }

    Nun verschiedene Fragen zur Umsetzung:

    1.) Ein Preset gehört ja zu einem Instrument. Angenommen, ich übergebe einer Funktion/Methode ein Preset. Diese Funktion weiß dann aber nicht, zu welchem Instrument das Preset gehört. Meine erste Überlegung wäre, für das Preset ein Feld "instrument" oder "parent" o.ä. vom Typ Instrument anzulegen, das auf das Instrument verlinkt, dem es gehört. Das wäre bequem - aber es bedeutet eine Kreuzverlinkung. Darf man sowas, ist das verboten, schlechter Stil oder speichermäßig gefährlich? Wie löst man das Problem am sinnvollsten? Alternativen wäre, dass jede Funktion, die ein Preset zuordnen will, das komplette instruments-Array durchläuft und nachschaut, welches Instrument das Preset enthält. Oder weitere Alternative: Jeder Funktion nicht nur Preset, sondern zusätzlich das zugehörige Instrument übergeben. Finde ich aber umständlich, wenn das oft vorkommt.

    2.) In dem Feld linkedPresets möchte ich weitere Presets anderer Instrumente speichern, die thematisch zu diesem Preset passen. Auch hier ist wieder das Problem: Wenn ich NUR die Presets speichere, fehlt hier wieder die Information, zu welchem Instrument diese Presets jeweils gehören. Wie löst man es?

    3.) Ich habe hier das Feld UUID eingeführt, um jedem Instrument oder Preset eine eindeutige ID zu verpassen. Das dient hauptsächlich der Möglichkeit, zwei Objekte (bzw. ein und das gleiche Objekt), die an verschiedenen Stellen gespeichert sind, zu vergleichen. Macht das Sinn und ist das überhaupt notwendig? Oder ist es in Swift gar kein Problem, mittels if preset1 == preset2 Objekte zu vergleichen (da in dem Fall ja vermutlich nur die Pointer-Adressen verglichen werden!?)?

    4.) Ich habe hier in der Klasse Preset ein Feld button vom Typ UIButton. Da ich, wenn ich Instrumente darstelle, für jedes Preset einen Button anlege, und beim Markieren/Löschen/Umbenennen eines Presets auch auf den zugehörigen Button zugreifen muss, speichere ich den Button in diesem Feld. Ist das legitim so oder ist das gegen die Gepflogenheiten von MVC? Falls dem so ist, wie würde man es sonst handhaben?

    5.) Ich möchte für Instrument- und Preset-Arrays verschiedene Hilfsfunktionen/-Methoden erstellen wie z.B. findPresetWithCategory(...) oder findPresetWithName(...), die aus dem Array entsprechende Elemente liefert. Wo sind diese Funktionen richtig aufgehoben? An besten in einer von diesen Klassen (z.B. instrument.findPresetWithName(...) oder eher alle zusammen gesammelt in nem eigenen Swift-File?

    6.) Oder gibt es die Möglichkeit, eine Extension für Arrays vom Typ X zu erstellen? So dass ich eine Mathede schreiben könnte, die nur bei Arrays vom Typ Instrument vorhanden ist, so dass ich dann direkt instrument.presets.findPresetWith... aufrufen könnte? Habe allerdings keine Infos dazu gefunden und vermute, dass es nicht geht.

    Wäre mich sehr freuen, wenn mir jemand hier bei ein paar Fragen Klarheit schaffen könnte :) Habe zwar schon einige Jahre Programmiererfahrung, aber nicht mit Swift/ObjC/iOS/MVC und auch nicht so sehr mit OOP :)

    Vielen Dank schon mal!

    LG Lukas
  • Ein grober Gedanke zu Deinen ersten Fragen:

    Für mich wäre ein Preset eine eigene Klasse, von der eine Instanz ein Property jedes Instrumentes wäre. Wenn diese Presets wirklich Instrumenten-spezifisch sind, fände ich es richtig, ihnen eine Eigenschaft "Instrument" zu geben. Bei Initialisierung gefüllt sparst Du Dir so die gesonderte Übergabe des Instrumentes, ähnlich eines Delegates.

    Aber ist ein Preset nicht eher spezifisch für eine Instrumentengruppe, weniger für das konkrete Instrument? Dann wäre dafür vielleicht eine eigene Klassendefinition angeraten.

    Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Ne, ein Preset gehört wirklich zu einem konkreten Instrument... sagen wir, ein bestimmtes Keyboard hat eine bestimmte Anzahl an Presets, z.B. hat Keyboard 1 die Presets Orgelsound 1, Streichersound, Klaviersound 2, Harfensound 3 etc. Deswegen Instrument, und dort ein Array an Presets. Jetzt möchte ich einer Funktion den Harfensound 3 übergeben. Und diese Funktion soll dann auch wissen, dass der Harfensound 3 zu Keyboard 1 gehört.
  • ... noch 1-2 Gedanken:

    Eine UUID würde ich nicht verwenden, sondern stattdessen eine Methode "isEqual:" implementieren, in der Du aufgrund der Charakteristika von Instrumenten entscheidest, ob zwei gleich sind. Wenn es nur bei der selben Instanz der Fall ist, kannst Du natürlich auch die Speicheradressen vergleichen.

    Es klingt für mich so, als solltest Du auch eine Klasse "Instrumentensammlung" nutzen, die zum einen Pointer auf alle Instrumente hält, zum anderen auch die Methoden zum Suchen etc. beinhaltet. Zusätzlich könnte sie auch für deren Speicherung u. ä. verantwortlich sein.

    Ich würde keine UI-Elemente im Datenmodell speichern. Button steuert ein Controller, das Modell liefert z. B. die Beschriftung oder auch die auszuführen Aktion.

    Nur meine Meinung, Mattes
    Diese Seite bleibt aus technischen Gründen unbedruckt.
  • Ich stimme @MyMattes zu. UUID macht keinen Sinnen. Du kannst Objekte über Speicheradressen vergleichen. Eine Klasse Instrumentensammlung macht Sinn. Dort hast du eine Collection mit den Instrumenten und halt Operation, die du auf die Liste anwenden möchtest.
    UIModelle macht hier keinen Sinn. Auch solltest du aufpassen, dass dein Controller nicht zu viel Code enthält.
    Evt kannst du in diesem Thread Anreize für deinen Use Case holen:
    Frage zum Modellieren von Objektbeziehungen und deren Speicherung

    Wenn ich den Text richtig interpretiert habe, würde ich eine zweite Liste Presets machen. Dort stehen alle Presets drinnen. Im Instrument hast du dann eine Liste mit PresetsToInstrumen.t (fehlt kein besserer Name ein)
    Die Klasse PresetsToInstrument enthält eine Referenz auf ein Presets Objekt in der Liste + eine weak Referenz zum Parent, denn ein Child sollte nicht ein parent Objekt besitzen.
  • Vielen Dank für eure Antworten und Ideen! Das ist schon mal sehr hilfreich. Den Thread guck ich mir an.

    MyMattes schrieb:

    Eine UUID würde ich nicht verwenden, sondern stattdessen eine Methode "isEqual:" implementieren, in der Du aufgrund der Charakteristika von Instrumenten entscheidest, ob zwei gleich sind. Wenn es nur bei der selben Instanz der Fall ist, kannst Du natürlich auch die Speicheradressen vergleichen.
    Alles klar. Ja, macht Sinn!


    msch schrieb:

    Eine Klasse Instrumentensammlung macht Sinn. Dort hast du eine Collection mit den Instrumenten und halt Operation, die du auf die Liste anwenden möchtest.
    Ok, also eine eigene Klasse, die das Array kapselt und die Funktionen zur Verwaltung des Arrays kapselt.


    msch schrieb:

    Wenn ich den Text richtig interpretiert habe, würde ich eine zweite Liste Presets machen. Dort stehen alle Presets drinnen. Im Instrument hast du dann eine Liste mit PresetsToInstrumen.t (fehlt kein besserer Name ein)
    Die Klasse PresetsToInstrument enthält eine Referenz auf ein Presets Objekt in der Liste + eine weak Referenz zum Parent, denn ein Child sollte nicht ein parent Objekt besitzen.
    Aber was spricht denn dafür, zwei Listen zu haben, eine Liste, wo alle Presets drinstehen und eine weitere Liste in der Klasse Instrument, die nochmal auf die einzelnen Presets der anderen Liste verweist? Was spricht gegen meine Umsetzung (Klasse Instrument und darin ein Array vom Typ Preset)? Also was wäre der Vorteil einer zweiten zentralen Liste?
  • Achso. Nein, genau, jedes Preset gehört nur zu einem Instrument.

    Also bedeutet das, wenn das Preset eine Referenz zum Parent hat und ich die weak definiere, dann ist das kein Problem und kann ich so machen? (sowohl retain-cycle- als auch speichermäßig via NSCoding)

    class Instrument {
    var presets: Preset[] = []
    }

    class Preset {
    weak var parentInstrument: Instrument
    }
  • Lies den Unterschied zwischen weak und strong Referenzen nach. Danach sollten zwei von deinen Fragen beantwortet sein.
    Eine weak Referenz musst du in Swift optional definieren. (kann ja nil werden)
  • Super, dankesehr. Weak/strong-Wissen aufgefrischt -> weak benutzen (nicht unowned in meinem Fall) und alles wird gut :)

    Dann bleibt für mich nur noch folgende Frage:

    midifreak schrieb:

    4.) Ich habe hier in der Klasse Preset ein Feld button vom Typ UIButton. Da ich, wenn ich Instrumente darstelle, für jedes Preset einen Button anlege, und beim Markieren/Löschen/Umbenennen eines Presets auch auf den zugehörigen Button zugreifen muss, speichere ich den Button in diesem Feld. Ist das legitim so oder ist das gegen die Gepflogenheiten von MVC? Falls dem so ist, wie würde man es sonst handhaben?
    Also ihr sagt, im Modell am besten keine Referenzen zu UI-Elementen. Wie halte ich dann die Verbindung fest, so dass ich vom Preset auf den entsprechenden Button oder Label schließen kann um z.B. einen Title zu ändern? Besser an anderer Stelle eine Tabelle mit Preset-Button-Zuordnungen? Ein Dictionary<Preset, UIButton> ist ja leider nicht ohne Weiteres machbar, dazu müsste ich die Klasse Preset konform zu Hashable machen, damit ich ein Preset als Key benutzen kann...
  • Ich würde eine TableView anlegen, die Cells mit den Presets enthält. Du solltest umgekehrt denken, wie kann ich von dem UIElement auf mein Objekt schließen. Da du ja ein Array von Presets hast, kannst über den Index darauf zugreifen. In der TableView steht dir glücklicherweise der NSIndexPath zur Verfügung.
    Eventuell solltest du dich in Bindings einlesen. Damit kannst du eleganter UI mit Model synchronisieren. In iOS gibt es leider keine Cocoa-Bindings wie in OS X. Hier solltest du unter dem Stichwort KVO fündig werden.
    Viel Erfolg und frohes Schaffen bei deinem Projekt!
  • Okay, dann versuche ich eher auf die UI-Elemente per Index zuzugreifen... also per TableView oder bei den Buttons via viewWithTag.

    msch schrieb:

    Eventuell solltest du dich in Bindings einlesen. Damit kannst du eleganter UI mit Model synchronisieren. In iOS gibt es leider keine Cocoa-Bindings wie in OS X. Hier solltest du unter dem Stichwort KVO fündig werden.
    In KVO hab ich mich mal eingelesen, aber wohl nicht alle Anwendungen durchschaut :)

    Vielen Dank für die Hilfe!