Datenhaltung und Darstellung - Hat jemand eine Idee?

Bitte aktiviere JavaScript!
Lass uns noch ein wenig bei dem Shop bleiben.

Wir haben ein Kapitel, das mehrere Artikel-Konfigurationen aufnehmen kann. Eine Artikel-Konfiguration besteht in unserer Vorstellung aus einer Menge von Attributen, kann also zu einem Attributnamen eine Liste möglicher Werte liefern.

Mal etwas Code als Skizze:
Java:
class Configuration {
    List<String> getValues(String attributeName);
    void setValues(String attributeName, List<String> values);
}
Als nächstes haben wir das Kapitel, also die Artikel.-Kategorie. Die verfügt über mehrere solcher Konfigurationen und kennt die Attributnamen aller Konfigurationen.
Java:
class Category {
    List<Configuration> configurations();
    Set<String> attributeNames();
    Configuration addConfiguration() {
        Configuration result = new Configuration();
        for (String name : attributeNames()) {
            result.setValues(name, new ArrayList<>());
        }
        configurations.add(result);
        return result;
    }
}
Java:
// folgendes würde Benutzer via UI erledigen:
// Kategorie anlegen
Category computer = new Category("Manufacturer", "FLOPS", "Color"); // Attributnamen
// Konfiguration anlegen
Configuration computerLieferantA = computer.addConfiguration();
// Werte der Konfiguration einstellen
computerLieferantA.setValues("Manufacturer", Collections.singletonList("HP"));
computerLieferantA.setValues("FLOPS", Arrays.asList("120 GFLOPS", "150 GFLOPS", "200 GFLOPS")); 
computerLieferantA.setValues("Color", Arrays.asList("schwarz", "alu", "rot"));
// Weitere Konfiguration anlegen
Configuration computerLieferantB = computer.addConfiguration();
// Werte der Konfiguration einstellen
computerLieferantB.setValues("Manufacturer", Collections.singletonList("Lenovo"));
computerLieferantB.setValues("FLOPS", Arrays.asList("80 GFLOPS", "120 GFLOPS", "140 GFLOPS")); 
computerLieferantB.setValues("Color", Arrays.asList("rot", "gelb", "grün", "blau"));
Damit hätten wir die Definition der "Stammdaten" abgeschlossen. Als nächtes wählt der Benutzer eine Kategorie aus, erhält dann die Attributnamen (Category#attributeNames()) angezeigt, wählt dort eine Menge aus und klickt auf "Erzeuge Tabelle".

Jetzt musst Du lediglich für jede Konfiguration der gewählten Kategorie aus den Werte-Listen das Kreuzprodukt erstellen und ausgeben; das könnte dann Dein Stamp-Objekt erledigen.
 
So wie ich den letzten Post verstanden habe, ist der Lieferant egal: Wenn Lieferant A T-Shirts in rot oder blau in X und L anbietet un Lieferant B T-Shirts in gelb und M, dann gibt es diese T-Shirts:
"rot in X", "rot in L", "blau in X" , "blau in L" und "gelb in M".
Diese Liste soll das Ergebnis sein, das er übergibt.
So habe ich das jetzt verstanden.
 
Das ist auch so, aber ich habe das ungünstig formuliert:
erhält dann die Attributnamen (Category#attributeNames()) angezeigt, wählt dort eine Menge aus und klickt auf "Erzeuge Tabelle".
Jetzt musst Du lediglich für jede Konfiguration der gewählten Kategorie aus den Werte-Listen das Kreuzprodukt erstellen
Hier sind nur die Werte-Listen gemeint, die zu den zuvor gewählten Attributen gehören.

D. h. in meinem Beispielfall bekäme der Benutzer zur Auswahl: "Manufacturer", "FLOPS" und "Color". Wählt er nun "FLOPS" und "Color" wird das Kreuzprodukt nur über die Mengen von "FLOPS" und "Color" (je Konfiguration) erstellt, das Ergebnis ist also

{"120 GFLOPS", "150 GFLOPS", "200 GFLOPS"} x {"schwarz", "alu", "rot"} vereinigt mit
{"80 GFLOPS", "120 GFLOPS", "140 GFLOPS"} x {"rot", "gelb", "grün", "blau"}
 
Wie laufen eigentlich bei Softwareentwicklern Meetings ab, wo einer dem anderen seine Ideen zu erklären versucht? :D

Ich versuche es mal anders: Und zwar, wie ich auf die Bezeichnung 'Stamp' gekommen bin.

Stellt euch die Dinge, von denen ihr die Daten auflisten wollt (in meinem Projekt sind das Components, im Beispiel vorhin Computer und danach waren es T-Shirts) vor, daß sie als leerer "Datensatzbehälter" auf einem Förderband liegen und darauf an Maschinen vorbeifahren.



Jede dieser Maschinen verpasst einem Datensatzbehälter einen Stempel, wobei ein Stempel einen Wert für ein Attribut darstellt. (In meiner aktuellen Version ist es strenggenommen ein Paar aus Attributswert und Attributsbezeichnung.)

Um viele Variantionsmöglichkeiten zuzulassen, können diese Stempelmaschinen unterschiedliches Verhalten annehmen: Manche arbeiten eine Liste ab (die Rechenleistung wäre so eine Liste gewesen), andere liefern ein oder mehrere Konstanten, es gibt aber auch welche, die bereits erzeugte Attribute auswerten. Ich hab eine Klasse, dich ich mit einer HashMap und einem Attributsnamen parametrisieren kann, damit ließe sich beispielsweise jeder Farbe ein anderer Preis zuordnen (ob das hier Sinn macht sei mal dahingestellt, es geht ja nur ums Prinzip). Es gibt auch eine Stampklasse, der kann ich eine mathematische Funktion übergeben, wobei die Funktionsparameter bereits existierende Attribute sind.

Ob man das einfach so als Kreuzprodukt darstellen kann...weiß ich nicht.

Jedenfalls, eine solche "Fließbandstrecke" ist dann das, was ich bisher als Set bezeichnet habe. Im Beispiel mit den Computern gab es zwei solcher Sets, jeweils eines für jeden Lieferanten.


Nachtrag:
Danke übrigens daß ich euch das bis hierher angetan habt, meiner Problemdarstellung zu folgen.
 
Wie laufen eigentlich bei Softwareentwicklern Meetings ab, wo einer dem anderen seine Ideen zu erklären versucht? :D
Das kommt darauf an, wer sich mit wem unterhält :p Meine Nachfragen und Analogien haben zwei Gründe: erstens bist Du in einem Bereich unterwegs, mit dem ich nichts zu tun habe. Ich muss also erst einmal verstehen, worum es geht. Zweitens versuche ich, Deine Lösung zu ignorieren, zumindest so lange es um die Anforderungen geht :)

Ob man das einfach so als Kreuzprodukt darstellen kann...weiß ich nicht.
Deine ursprünglich gestellte Anforderung schon. Das spielt aber auch keine Rolle, denn das ist nicht Aufgabe von Artikelkategorie/-konfiguration.

Jede dieser Maschinen verpasst einem Datensatzbehälter einen Stempel, wobei ein Stempel einen Wert für ein Attribut darstellt. (In meiner aktuellen Version ist es strenggenommen ein Paar aus Attributswert und Attributsbezeichnung.)
Mir fehlt hier die Erzeugung der Datensatzbehälter (das wären bei mir die Artikel). Du brauchst ja für jeden "Stempel" einen solchen.
 
Mir fehlt hier die Erzeugung der Datensatzbehälter (das wären bei mir die Artikel). Du brauchst ja für jeden "Stempel" einen solchen.
Ganz einfach:
Java:
public class Property{
    public Property(String name, Object value)
}

public class Article{
    public void addNewProperty(Property p){}
}

public abstract class ArticleStamp{
    public ArrayList<Article> generateArticles(ArrayList<Article>);
}


public void generateComponents(ArrayList<ArticleStamp> stamplist){
    ArrayList<Article> articles = new ArrayList<>(); //leere Liste, in der die Artikel gehalten werden.
    
    for(ArticleStamp stamp : stamplist){
        articles = stamp.generateArticles(articles); //Manche Stampklassen können aufgrund ihres Verhaltens Artikel einfach erzeugen, eine solche muß natürlich am Anfang stehen. Manche erzeugen eine völlig neue Liste und geben diese zurück, andere bearbeiten tatsächlich nur.
    }
    //...
}

Ich habe mir, seit ich den Thread eröffnet habe, nochmal gründlich Gedanken gemacht und ich glaube nun zu wissen, wo mein Problem schonmal gelöst wurde: überall dort, wo Tabellen benutzt werden.

Als Beispiel mal was Bekanntes wie Excel: Dort sind die Zellen ja auch mehr oder weniger komplexe Objekte, die in Zeilen und Spalten angeordnet sind.
Bezogen auf mein Problem: Das, was in Excel ein Worksheet ist, wär bei mir eine Library oder im Beispiel vorhin eine Katalogseite. In jede Zelle stecke ich ein fertig parametriertes Stamp-Objekt rein.
Jetzt kann ich hergehen, alle Stampobjekte einer vollständig gefüllten Zeile in eine ArrayList stopfen, den obigen Code ausführen, und erhalte für jede Zeile eine Anzahl von Artikeln, die mit den gleichen Attributen ausgestattet sind.
Und das mache ich jetzt für alle Zeilen hintereinander.

Die Zellen (bzw. Stampobjekte, die in einer "Zelle" sind) müssen wissen, was in dem jeweiligen Spaltenheader steht. Wenn der Benutzer den Spaltenheader ändert, dann soll das Objekt in der Zelle das mitbekommen. Es soll auch Vorlagen für einen Spaltenheader geben können.

Wird das jetzt vielleicht klarer, was ich da machen will und was ich suche?
 
überall dort, wo Tabellen benutzt werden.
Ja, das ist ja auch das Prinzip, das ich oben hatte, wollte jetzt aber nicht noch auf z. B. ResultSet und ResultSetMetaData umschwenken :) Die Artikeldefinition sind die Metadaten und der Artikel ist dann eine Zeile in der "Tabelle". Dein StampObjekt (bzw. die Stempelmaschine) erzeugt dann anhand der Metadaten die Zeilen.
 
Schön daß mein Problem jetzt etwas klarer wird. Hättest du eine Idee, wie ich so eine Tabelle aufziehen kann? Gibt es da vielleicht ein passendes Entwurfsmuster oder sowas?

Du Suchmaschine des geringstem Mißtrauens wirft mir leider nur Sachen im Zusammenhang mit Grafikoberflächen vor die Füße.
 
Hättest du eine Idee, wie ich so eine Tabelle aufziehen kann?
Ich dachte, das in Kommentar #21 gezeigt zu haben.

Category wäre die Tabelle, die enthält als Metadaten die Spalten und die Configurations sind dann die Zeilen dazu. Per addConfiguration wird eine neue Zeile erstellt. Das ist nur etwas flexibler, weil man ja keine starren Spalten hat (die Configurations können zusätzliche Attribute enthalten).

Entwurfsmuster... mom. In PoEE, S. 508: Record Set (An in-memory representation of tabular data). Entspricht im Wesentlichen dem "Muster" aus #21 bzw. dem JDBC-ResultSet von Java.
 
Hm...das muß ich mir wohl mal etwas länger ansehen. Ich sehe nicht, wie ich bestimmte Dinge aus meinem bisherigen Konzept umsetzen kann. Könntest du mir ein kleines Beispiel geben, wie Werte abhängig von anderen Attributswerten gesetzt werden?
Z.B.: Wenn der Computer silber ist, soll er 10% mehr kosten, bei allen anderen nur 5%.
 
Das Beispiel aus #21 setzt in erster Linie die Tabelle aus Deinem Kommentar #1 um. Wie Du daraus Artikel erzeugst/Preise ermittelst, sehe ich davon unabhängig. Zum Beispiel könntest Du eine Configuration verwenden, um eine Stempelmaschine zu konfigurieren. Die erzeugt dann die notwendigen Stempel und da kann dann natürlich ein Preisstempel dabei sein, der in Abhängigkeit von der Farbe den Preis berechnet.
 
Wie Du daraus Artikel erzeugst/Preise ermittelst, sehe ich davon unabhängig.
Ok, anderes Beispiel: Wir fügen das Attribut Gehäusematerial hinzu. Der Benutzer des Programms soll formulieren können: WENN die Farbe des Computers Alu ist, DANN hat er ein Metallgehäuse. SONST hat er ein Kunststoffgehäuse. Das Programm soll jetzt in den generierten Daten selbständig den richtigen Wert eintragen.

Oder: Wir fügen ein Attribut 'Zubehör' hinzu. Dieses füllen wir mit einer farbigen Laptoptasche, wir haben rote, blaue und schwarze Laptoptaschen. Diese will der Benutzer wie folgt auf die Rechner, basierend auf dem Attribut 'Laptopfarbe', verteilen:
weißer Laptop -> schwarze Tasche
alu Laptop -> schwarze Tasche
schwarzer Laptop -> rote Tasche
roter Laptop -> blaue Tasche
blauer Laptop -> blaue Tasche
...

Ansonsten: Ich bekomme die fertig parametrierten Stempelmaschinen bereits rein. Die Daten zur Parametrierung zu halten und später erst die Stempelmaschine zu erzeugen macht es mir eher schwerer. Es würde völlig reichen, fertige Stempelobjekte in eienr solchen Tabelle anordnen zu können.

Ich würde gerne mehr über das Record Set Pattern erfahren, aber so richtig finde ich da kaum etwas dazu-außer Hinweise auf das Buch, daß du bereits genannt hast. Und ein Video, aber da verstehe ich den Sprecher recht schlecht.
Kennst du vielleicht noch einen nützlichen Link dazu?
 
Mal von hinten nach vorne: https://martinfowler.com/eaaCatalog/recordSet.html, der für mich entscheidende Punkt ist der letzte Satz: "looks [exactly] like the result of an [SQL] query but can be generated and manipulated by other parts of the system" (für mich hier weniger relevante Dinge in eckigen Klammern).

Du bist gerade dabei, die eierlegende Wollmilchsau schreiben zu wollen. Vereinfacht gesprochen hast Du eine Eingabemenge, eine Funktion und eine Ausgabemenge.

Diese Funktionen in Code zu schreiben, wäre noch relativ harmlos, aber:
Der Benutzer des Programms soll formulieren können:
D. h. Du musst Dir ein System überlegen, wie der Benutzer das formulieren könnte, wie Du das auf Objekte abbildest.

Code:
Function<Configuration, Article> produce = ...
Function<Article, Article> changePrice = ...
...
produce.andThen(changePrice).andThen(doThis).andThen(schlagmichtot).apply(configuration);
 
Du bist gerade dabei, die eierlegende Wollmilchsau schreiben zu wollen.
Naja...so gesehen hab ich die dann schon geschrieben, denn wie gesagt: Stampobjekte parametrieren und erstellen geht, damit Daten zu erzeugen geht auch.

Es geht mir bloß nach darum, diese Stampobjekte passend zu halten und die Attribute synchron zu halten, wenn ich mich darauf beschränken würde, nur ein Datenset zu haben (also im Beispiel mit den Computern nur ein Hersteller) wäre ich schon längst fertig.
 
Ich sehe das momentan als Array aus Parameter und Hersteller(Wertesatz). Im Schnittpunkt daraus gibt es am einfachsten eine Liste aus möglichen Parametern oder eine Formel. Dabei variieren nicht alle Parameter, sondern es gibt auch Abhängigkeiten untereinander. Das Programm ergibt dann Tupel aus Werten der Parameter aus.

Ich würde das wirklich als Grid erstellen, und beim Klicken auf eine Zelle erzeugt sich ein Dialogfeld, wo man die Parameter eingeben oder Formel erstellen kann.

Problematisch finde ich zwar immer noch, dass viel mehr Werte erzeugt werden, als Variationen real vorhanden sind.
 
Es geht mir bloß nach darum, diese Stampobjekte passend zu halten und die Attribute synchron zu halten, wenn ich mich darauf beschränken würde, nur ein Datenset zu haben (also im Beispiel mit den Computern nur ein Hersteller) wäre ich schon längst fertig.
Naja, Du brauchst etwas, das dem Datenset "übergeordnet" ist, und das hält die Dinge synchron. Bei mir macht das die Category, dort z. B. mit #addConfiguration().

Wie gibt denn der Benutzer bei Dir die verschiedenen Wenn/Dann-Geschichten bzw. Formeln an?
 
Ich würde das wirklich als Grid erstellen, und beim Klicken auf eine Zelle erzeugt sich ein Dialogfeld, wo man die Parameter eingeben oder Formel erstellen kann.
So in etwa funktioniert das ja auch: Dem Benutzer wird eine Tabelle präsentiert, wo im Spaltenkopf die Attributbezeichnung steht. Dann abwechselnd eine (über die breite gemergte) Zeile, in der eine gewählte Bezeichnung steht, in der Zeile darunter steht in jeder Zelle, wie das Attribut zustande kommt. In diesen Zellen kann der Benutzer dann auswählen, wie er das Attribut erzeugen will (von einer Liste, basiernd auf existierenden Attributen, ...). Basierend auf dieser Auswahl wird eine neue Stempelmaschine erzeugt, die der Benutzer dann parametrieren kann.

Naja, Du brauchst etwas, das dem Datenset "übergeordnet" ist, und das hält die Dinge synchron. Bei mir macht das die Category, dort z. B. mit #addConfiguration().
Genau...aber so richtig verstehe ich das Beispiel von dir immer noch nicht. Vermutlich, weil mit den Bezeichnungen nicht so recht klarkomme, oder zu tief in meinem Problem drinhänge und das zu schlecht abstrahieren kann.

Ich denk ich hab aber mittlerweile den Ansatz einer Idee, um dieses "übergeordnete Etwas" zu stricken.
 
So...ich denke ich hab jetzt meine Lösung gefunden. Vielen Dank für das Beschäftigen mit meinem Problem und den Rubberduckersatz.

So sieht der Plan jetzt aus (leicht abgekürzt, es ist so schon lang genug), wer meint Codeteile verwenden zu wollen kann das gerne auf eigene Gefahr tun:

Java:
//Wir brauchen:

public interface Itemable {
   
    <T extends ItemModel> T getItemModel(Class<T> m);
   
    void takeTableinformation(RowHeader row, ColumnHeader col);
}

@ForceDeclaredGenerics //Siehe: https://www.java-forum.org/thema/generics-vererbung.185397/
abstract class TableLine<IT extends Itemable> {
   
    private ArrayList<IT> items;
    private RT rowheader;

    public TableLine() {
        this.items = new ArrayList<>();
    }

    void addElement(IT item){
        items.add(item);
    }
   
    void removeAllElements(){
        items.removeAll(items);
    }

    public int getLenght(){
        return items.size();
    }
   
    public IT getItem(int i){
        return (i >= items.size() || i < 0) ? null : items.get(i);
    }

    public ArrayList<IT> getItemlist(){
        ArrayList<IT> list = new ArrayList<>();
       
        for(IT item : items){
            if(item != null){list.add(item);}
        }
        return list;
    }
   
    public Row(RT rowheader){
        this.rowheader = rowheader;
    }
   
    public Row(){
        this.rowheader = null;
    }
   
    RT getRowheader(){
        return this.rowheader;
    }

    boolean hasHeader(){
        return this.rowheader != null;
    }
}

@ForceDeclaredGenerics
public class RowTable<CHT extends ColumnHeader, RT extends Row, IT extends Itemable> {

    //Define element order in table
    private ArrayList<Identifier> rowOrder;
    private ArrayList<Identifier> columnOrder;
    //Link table lines to header
    private HashMap<Identifier, RT> rows;
    private HashMap<Identifier, CHT> columns;
    //Link items to rows and columns
    private HashMap<Identifier, HashMap<Identifier, IT>> table;

    public RowTable() {
        rowOrder = new ArrayList<>();
        columnOrder = new ArrayList<>();
        rows = new HashMap<>();
        columns = new HashMap<>();
        this.table = new HashMap<>();
    }

    private boolean checkRowIdentifier(Identifier i) {
        return rows.containsKey(i);
    }

    private boolean checkColumnIdentifier(Identifier i) {
        return columns.containsKey(i);
    }

    public void addRow(RT row) {
        Identifier i = new Identifier();
        row.removeAllElements();
        rowOrder.add(i);
        rows.put(i, row);
        table.put(i, new HashMap<>());
    }

    public RT getRow(Identifier i) {
        RT row;
        CHT columnheader;
        RowHeader rowheader;

        if (!checkRowIdentifier(i)) {
            return null;
        }

        row = rows.get(i);
        rowheader = row.getRowheader();
        for (Map.Entry<Identifier, IT> entry : table.get(i).entrySet()) {
            IT item = entry.getValue();
            Identifier c = entry.getKey();
            columnheader = columns.get(c);

            item.takeTableinformation(rowheader, columnheader);
            row.addElement(item);
        }
        return row;
    }

    public RT removeRow(Identifier i) {
        RT row;
        CHT columnheader;
        RowHeader rowheader;

        if (!checkRowIdentifier(i)) {
            return null;
        }

        row = rows.get(i);
        rowheader = row.getRowheader();
        for (Map.Entry<Identifier, IT> entry : table.get(i).entrySet()) {
            IT item = entry.getValue();
            Identifier c = entry.getKey();
            columnheader = columns.get(c);

            item.takeTableinformation(rowheader, columnheader);
            row.addElement(item);
        }

        rowOrder.remove(i);
        rows.remove(i);
        table.remove(i);

        return row;
    }

    public void addColumn(CHT header) {
        Identifier i = new Identifier();
        columnOrder.add(i);
        for(Map.Entry<Identifier, HashMap<Identifier, IT>> e : table.entrySet()){
            e.getValue().put(i, null);
        }
        columns.put(i, header);
    }

    public void addColumn() {
        Identifier i = new Identifier();
        columnOrder.add(i);
        columns.put(i, null);
    }

    public void removeColumn(Identifier i) {
        if (!checkColumnIdentifier(i)) {
            return;
        }

        columnOrder.remove(i);
        columns.remove(i);
        for (Map.Entry<Identifier, HashMap<Identifier, IT>> e : table.entrySet()) {
            e.getValue().remove(i);
        }
    }

    public boolean addItem(IT item, Identifier row, Identifier column) {
        HashMap<Identifier, IT> rowMap;

        if (!(checkRowIdentifier(row) && checkColumnIdentifier(column))) {
            return false;
        }

        rowMap = table.get(row);
        if (rowMap.get(column) == null) {
            rowMap.put(column, item);
            return true;
        }
        else {
            return false;
        }
    }

    public IT removeItem(Identifier row, Identifier column) {
        IT item;
        HashMap<Identifier, IT> rowMap;

        if (!(checkRowIdentifier(row) && checkColumnIdentifier(column))) {
            return null;
        }

        rowMap = table.get(row);
        item = rowMap.remove(column);
        return item;
    }

    public IT replaceItem(IT item, Identifier row, Identifier column) {
        HashMap<Identifier, IT> rowMap;

        if (!(checkRowIdentifier(row) && checkColumnIdentifier(column))) {
            return null;
        }

        rowMap = table.get(row);
        return rowMap.replace(column, item);
    }

    public ArrayList<Identifier> getRowIdentifier() {
        return (ArrayList<Identifier>) rowOrder.clone();
    }

    public ArrayList<Identifier> getColumnIdentifier() {
        return (ArrayList<Identifier>) columnOrder.clone();
    }
}

public class Identifier {
   
    private final UUID id;

    Identifier() {
        this.id = UUID.randomUUID();
    }
   
    UUID getId(){
        return id;
    }
   
    @Override
    public boolean equals(Object o){
        if(o.getClass().equals(Identifier.class)){
            Identifier i = (Identifier)o;
            return this.id.equals(i.getId());
        }
        return false;
    }
}

public abstract class ColumnHeader{}
public abstract class RowHeader{}

Um den Code kurz zu erklären: Damit eine Klasse in der RowTable als Tabelle gehalten werden kann, muß es daß Interface Itemable implementieren. Dieses sieht zwei Methoden vor: Eine Methode ist dazu da, ein Modell mit vereinfachten Informationen (z.B. für die Realisierung eines MVCs) zu erstellen, das ist aber noch nicht implementiert. Die andere Methode ist dazu da, dem Item Informationen über den Spalten- und Zeilenheader seiner Position zukommen zu lassen, indem ihm das jeweilige ColumnHeader- und RowHeaderobjekt übergeben wird. Diese Methode wird z.B. vor dem Zugriff auf ein Item aufgerufen, ich erkläre das gleich an einem Beispiel.
Die Klassen ColumnHeader und RowHeader dienen dazu, benutzerspezifische Informationen an die jeweiligen Items zu liefern.

Ansonsten ist da noch die (abstrakte) Klasse Row. Abstrakt deswegen, weil ich die Tabellenartigkeit des Ganzen Gebildes eigentilch etwas in den Hintergrund stellen will. Diese Klasse kann/soll vom Benutzer um weitere Fähigkeiten ergänzt werden können, der Tabellenzeilencharakter kann dabei völlig im Hintergrund verschwinden.

Und die Klasse RowTable. Diese kümmert sich um die korrekte Zuordnung von allem. Mit diesem Mechanismus bin ich absolut noch nicht glücklich, da ich insgesamt zwei ArrayLists<Identifier>, zwei HashMaps(Identifier, RowHeader/ColumnHeader> und eine HashMap<Identifier, HashMap<Identifier, Itemable>> synchronzuhalten habe. Wenn dazu noch jemand eine bessere Idee hat-immer her damit.

Ich werde die Anwendung mal Laptopbeispiel demonstrieren. Dazu konstruiere man erstmal ein Problem:
Java:
//Klasse, um die Daten eines Laptops zu halten
public Class LapTop{
    HashMap<String, String> properties;
    
    public void addProperty(String name, String value){properties.put(name, value);}
    public String getValue(String name){ return properties.get(name);}
}

//Klassen, um Laptops zu erzeugen
abstract public Class LaptopPropertymaker implements Itemable{
    String name;
    public void setName(String name){this.name = name;}
    abstract public ArrayList<LapTop> getLaptops(ArrayList<LapTop> laptops);
}

public Class LaptopListMaker extends LaptopPropertymaker{
    private ArrayList<String> list;
    private Stiring name;
    
    public LaptopListMaker(ArrayList<String> list){
        This.list = list;
    }
    
    public ArrayList<LapTop> getLaptops(ArrayList<LapTop> laptops){
        //Implementiere die Methode so, daß allen Objekten in laptops eine
        //Eigenschaft mit Name und einem Wert aus list hinzugefügt wird.
    }
    
    void takeTableinformation(RowHeader row, ColumnHeader col){
        this.name = col.getName(); //getName liefert den Namen der Spalte zurück,
        //was in diesem Beispiel gleichzeitig der Name des Attributs ist, das hinzugefügt wird.
    }
}


public Class LaptopConstantsMaker extends LaptopPropertymaker{
    private ArrayList<String> list;
    private Stiring name;
    
    public LaptopListMaker(ArrayList<String> list){
        This.list = list;
    }
    
    public ArrayList<LapTop> getLaptops(ArrayList<LapTop> laptops){
        //Die Implementierung ist hier anders als in der anderen Klasse. Beispiel:
        //in laptops sind zwei LapTopobjekte, in list sind drei Einträge. Dann werden
        //insgesamt sechs LapTop-objekte erzeugt, wobei die bestehenden zwei LapTop-objekte dreimal
        //geklont werden und jedes Objekt (bzw. die Klone) mit jeweils einem Eintrag aus list ausgestattet werden.
    }
    
    void takeTableinformation(RowHeader row, ColumnHeader col){
        this.name = col.getName(); //getName liefert den Namen der Spalte zurück,
        //was in diesem Beispiel gleichzeitig der Name des Attributs ist, das hinzugefügt wird.
    }
}

Den Gedanken dahinter habe ich ja schon erklärt: Man reicht ein ArrayList<LapTop> an einer Reihe LaptopPropertymaker-Objekte durch, und jedes dieser Objekte fügt ein spezielles Attribut (Rechenleistung, Farbe, ...) hinzu und versieht es mit einem Wert (120 MFlOPS, Schwarz, ...).

Jetzt kann ich mit dem Konstrukt da ganz oben Folgendes machen:
Java:
//Mit dieser Klasse wird der Name des Attributs festgelegt
public Class LaptopAttribute extends ColumnHeader{
    String name;
    public LaptopAttribute(String name){this.name = name;}
    public String getName(){return name;}
}

public Class EmptyRowheader extends RowHeader{} //RowHeader brauchen wir in diesem Beispiel nicht

public LaptopSet extens Row<EmptyRowheader, LaptopProterymaker>{
    //Hier kann man jeder Gruppe von LapTops nach Belieben weitere Informationen
    //hinzufügen, wie Herstellername, irgendwelche Kommentare...
}

Damit haben wir zwei Dinge: Einmal kann ich ein Attribut definieren. Jedem Itemable-Objekt werden, bevor man darauf zugreifen kann, die zwei jeweiligen Header-Objekte zugänglich gemacht. In diesem Fall ist es der Attributsname, der so übergeben wird.

Mit der LaptopSet-Klasse kann man jetzt einen Satz Laptops erzeugen. Braucht man mehrere (z.B. verschiedene Hersteller, oder verschiedene Chargen eines Herstellers, oder ...) kann man mehrere LaptopSets erzeugen. Aber: Wenn man mehrere LaptopSets hat, haben am Ende des Tages alle erzeugten Laptops die gleichen Attribute:

Java:
RowTable<LaptopAttribute , LaptopSet, LaptopPropertymaker> LaptopTable = new RowTable<>();

LaptopAttribute calcspeed = new LaptopAttribute("Rechenleistung");
LaptopTable.addColumn(calcspeed);

LaptopAttribute color = new LaptopAttribute("Farbe");
LaptopTable.addColumn(color);

LaptopAttribute material = new LaptopAttribute("Material");
LaptopTable.addColumn(material);


LaptopSet LaptopsA = new LaptopSet();
//Hinzufügen was man an Informationen haben will, aber noch keine Propertymaker
LaptpTable.addRow(LaptopsA);

LaptopSet LaptopsB = new LaptopSet();
//Hinzufügen was man an Informationen haben will, aber noch keine Propertymaker
LaptpTable.addRow(LaptopsB);

So...und das wars im Prinzip. Jetzt können LaptopPropertymaker-Objekte instanziert, parametrisiert und in die LaptopTable-Klasse eingefügt werden. Mit dem (noch nicht implementiertem) Model-Objekt können die Daten der Tabelle dann in einer GUI dargestellt werden ohne jedesmal die ganze Tabelle übergeben zu müssen.
Wenn man dann aus der LaptopTable-Klasse ein Row-Objekt wieder rausholt, dann enthält es die jeweiligen LaptopPropertymaker und man kann an einem Rutsch Laptops erstellen.

Wie gesagt, mit den vielen ArrayLists und HashMaps bin ich noch nicht glücklich, wenn jemand da Vorschläge hat-immer her damit. Was haltet ihr sonst davon?
 
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben