MSC - MovieSeriesChecker

Bitte aktiviere JavaScript!
Hallo zusammen,

ich nun schon seit längerem dabei, mein erstes, größeres Java-Projekt zu starten.
Das Programm soll im fertigen Zustand folgendes beherrschen:
MSC kann Filme und Serien, die in Kodi-typischer Ordnerstruktur abgelegt sind, einlesen und erstellt daraus eine JSON mit einigen Infos. Das passiert auf dem Slave-System (erkläre ich später).
Diese JSON kann auch in MSC wieder eingelesen werden (passiert auf dem Master-System), um sie mit den lokalen Filmen/Serien zu vergleichen. Dabei wird dann der jeweilige Unterschied ausgespuckt und kann, wenn sie auf dem Master aber nicht auf dem Slave vorhanden sind, kopiert werden.

Der Master ist also der PC, bei dem gerade physisch die Medien liegen. Der Slave hat auch physisch Medien liegen, diese sind aber vom Master aus nicht zugreifbar.
Sinn des ganzen: Ich pendle und habe an beiden Wohnorten einen PC mit Medien. Da ich so auch mein Backup habe (Daten an zwei unterschiedlichen Standorten), will ich diese offline synchron halten. Also wenn ich an Wohnort A (Slave in diesem Fall) bin, erstelle ich über MSC so eine JSON und nehme sie mit zu Wohnort B. Bei Wohnort B (Master) lese ich dann die JSON von A wieder in MSC ein und mir werden die Differenzen angezeigt. Diese Differenzen lassen sich dann Kopieren, sofern sie halt bei B vorhanden sind (und somit bei A fehlen).

Hoffe, das ist soweit verständlich.

Da ich es gerne "richtig" angehen will, habe ich mir vor dem Coden bereits einige Gedanken gemacht.
Aus diesen Gedanken resultierte dann das hier abgebildete UML:
MSC_UML.png

Die graphische Oberfläche soll mittels JavaFX und mit dem Framework mvvmFX erstellt werden. Mein bisheriges Mockup des GUIs:
MSC_GUI.png

Der Vergleich der Medien läuft aus Zeitgründen nicht über Hashes, sondern über die Dateigröße und natürlich dem Namen.

Meine Fragen:
Was würdet ihr am UML optimieren?
Ideen zum GUI?
Generelle Optimierungen für mein Vorgehen?
Würde eine schneller Vergleich auch anders als über die Dateigröße gehen?

Ich bin offen für Anregungen und Kritik.
 
A

Anzeige




Schau mal hier —> (hier klicken)
UML: mal kurz überflogen. "FileUtil" würde ich nicht machen. Und man sieht sehr schön ein Problem, das ich mit JavaFX (in dem Fall der Doku) habe: dieses ganze FX-Property und FX-Collections-Gedöns verseucht das ganze Model.

Würde eine schneller Vergleich auch anders als über die Dateigröße gehen?
Das hängt von der Dateigröße ab und auch daran, wie sich die Dateien verändern (können). Wenn z. B. sehr große Dateien sind, an die nur angefügt wird, könntest Du einen Hash über die letzten x KB erzeugen. Nur mal als Idee.
 
Danke für die schnelle Antwort.
Das mit den Properties im Model nervt mich auch etwas, aber brauche ich wohl um die Verknüpfung zwischen ViewModel und Model über die Bindings machen zu können.

Was spricht gegen die FileUtil Klasse? Habe das ehrlich gesagt aus meinem Buch "Der Weg zum Java-Profi" so aufgeschnappt. Das ist bisher auch eine der wenigen Klassen, die ich schon angefangen habe.
Der Code dazu:
Java:
package de.nedsong.msc.model.util;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;

public class FileUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(FileUtil.class.getName());

    public static void writeTxtToFile(final File file, final String string) throws IOException {
        FileWriter fileWriter = new FileWriter(file);
        fileWriter.write(string);
    }

    public static void writeGsonToFile(final File file, final Object object) throws IOException {
        GsonBuilder builder = new GsonBuilder();
        builder.setPrettyPrinting().serializeNulls();

        Gson gson = builder.create();
        String gstring = gson.toJson(object);
        writeTxtToFile(file, gstring);
    }

    public static Object readGsonFromFile(final File file, final Class classToConstruct) throws FileNotFoundException {
        Object object = new Object();

        BufferedReader br = new BufferedReader(new FileReader(file));
        Gson gson = new Gson();
        object = gson.fromJson(br, classToConstruct);
        return object;
    }

    public static boolean directoryExists(final File directoryToCheck) {
        if (directoryToCheck == null) return false;

        return directoryToCheck.exists() && directoryToCheck.isDirectory();
    }

    public static boolean fileExists(final File fileToCheck) {
        if (fileToCheck == null) return false;

        return fileToCheck.exists() && fileToCheck.isFile();
    }

    public static File[] listDirContents(final File directoryToList) {
        if(directoryExists(directoryToList)) {
            final File[] fileList = directoryToList.listFiles();
            if (fileList != null) {
                return fileList;
            }
        }

        return new File[0];
    }

    public static File[] listDirContentsFiltered(final File directoryToList, final String prefix, final String postfix) {
        if(directoryExists(directoryToList)) {
            final File[] fileList = directoryToList.listFiles(createPreAndPostFixFilter(prefix, postfix));
            if (fileList != null) {
                return fileList;
            }
        }

        return new File[0];
    }

    public static FilenameFilter createPreAndPostFixFilter(final String prefix, final String postfix) {
        return (dir, filename) -> { return filename.startsWith(prefix) && filename.endsWith(postfix); };
    }
}
 
Das mit den Properties im Model nervt mich auch etwas, aber brauche ich wohl um die Verknüpfung zwischen ViewModel und Model über die Bindings machen zu können.
Das Model würde man unabhängig von JavaFX halten, ein dazu passendes ViewModel würde dann die entsprechenden Properties enthalten und die Verbindung zwischen Model und View herstellen.

Kurz zu deinem UML-Diagramm:
* Interfaces mit „I“-Prefix sind grausam...
* Die Felder hast du alle noch mal mit dem Klassennamen geprefixt (bei TV-Show und Season) das würde ich auch weg lassen.
* die populate-Methoden find ich merkwürdig, was sollen die machen?
 
Was spricht gegen die FileUtil Klasse?
Klassen mit Util im Namen sind Ausdruck dafür, dass einem nichts besseres eingefallen ist :) Schau mal in die Standard Java-APIs wie viele Klassen Du dort mit Util im Namen findest (der Name für das Paket java.util ist auch nicht besser, das sehe ich aber "historisch" bedingt. Heute würde es mit Sicherheit ein Paket java.collections geben)

Abgesehen davon formieren sich Util-Klassen meist zu Klassen, in denen sich zusammenhangslose Funktionen sammeln: schreibe Textdatei, schreibe Gson, dann plötzlich ein Dateifilter usw. Ja, hat alles irgendwie mit Dateien zu tun, mehr aber auch nicht. Und warum eigentlich Gson und nicht Json? Ist doch völlig egal, womit das JSON geschrieben/gelesen wird.
 
Das Model würde man unabhängig von JavaFX halten, ein dazu passendes ViewModel würde dann die entsprechenden Properties enthalten und die Verbindung zwischen Model und View herstellen.
Und wie muss ich mir das dann in Code vorstellen? Dabei besteht ja dann die Gefahr, dass das Model nicht den gleichen Status wie das ViewModel hat. Ich kenne bisher nur die Bindings, um das wirklich sicherzustellen. Habt ihr/du da ein kleines Beispeilcodeschnipsel oder etwas, wo ich mir das Anlesen kann?

* Interfaces mit „I“-Prefix sind grausam...
Wie würdest du die Interfaces benennen? Mit "able" am Ende passt ja nicht wirklich zu meinen Interfaces.

* die populate-Methoden find ich merkwürdig, was sollen die machen?
Mir fiel da ehrlich gesagt kein besserer Name ein. Die Methoden sollen die Listen mit Inhalt füllen. Sollte ich das direkt über die setXyz machen? Wäre dann halt ein Setter, der keinen Übergabeparameter hat.

Abgesehen davon formieren sich Util-Klassen meist zu Klassen, in denen sich zusammenhangslose Funktionen sammeln: schreibe Textdatei, schreibe Gson, dann plötzlich ein Dateifilter usw.
Wo würdet ihr denn dann die bisher in meiner Util-Klasse abgebildeten Funktionalitäten abbilden? Dabei handelt es sich ja um ziemlich generische Dinge, die ich in allen Klassen brauche (auch in meiner bisher im UML noch nicht dargestellten Config.class).
 
Und wie muss ich mir das dann in Code vorstellen? Dabei besteht ja dann die Gefahr, dass das Model nicht den gleichen Status wie das ViewModel hat. Ich kenne bisher nur die Bindings, um das wirklich sicherzustellen. Habt ihr/du da ein kleines Beispeilcodeschnipsel oder etwas, wo ich mir das Anlesen kann?
Das Model kann das ViewModel übers ganz normale Observer-Pattern über Änderungen informieren, das ViewModel das Model über ganz normale Methodenaufrufe, wenn sich was ändert.
Ein passendes Beispiel müsste ich erst raussuchen, kann ich später aber machen.


Wie würdest du die Interfaces benennen? Mit "able" am Ende passt ja nicht wirklich zu meinen Interfaces.
Media und MetaData ;)
Das Interface sollte das "normale" sein, und nicht irgendwas besonderes. Wenn man das extra kennzeichnet macht man es nur zu was besonderem was es nicht ist. Siehe zb im JDK in den Collections ;)



Datatypes würde ich btw auch umbenenne, den Plural schon im Typen stehen haben macht in den seltenste Fällen Sinn, bei einem Enum aber vermutlich nie.


Mir fiel da ehrlich gesagt kein besserer Name ein. Die Methoden sollen die Listen mit Inhalt füllen. Sollte ich das direkt über die setXyz machen? Wäre dann halt ein Setter, der keinen Übergabeparameter hat.
Welche Listen denn und wo kommt der Inhalt her?

Spontan würde ich die einfach nur get* nennen, zumindest von der Signatur um UML.

Wo würdet ihr denn dann die bisher in meiner Util-Klasse abgebildeten Funktionalitäten abbilden? Dabei handelt es sich ja um ziemlich generische Dinge, die ich in allen Klassen brauche (auch in meiner bisher im UML noch nicht dargestellten Config.class).
Zum Teil könnte man das zB in Repositories auslagern, kommt immer drauf an, wo und wie man das benutzt.
 
Das Model kann das ViewModel übers ganz normale Observer-Pattern über Änderungen informieren, das ViewModel das Model über ganz normale Methodenaufrufe, wenn sich was ändert.
Ein passendes Beispiel müsste ich erst raussuchen, kann ich später aber machen.
Danke, fürs erste reicht mir das als Ansprungspunkt zum Nachschlagen.

Welche Listen denn und wo kommt der Inhalt her?

Spontan würde ich die einfach nur get* nennen, zumindest von der Signatur um UML.
Die Listen in den einzelnen Klassen selbst (also die MetaData und Season-Listen).
Die Methode soll dann über einen Filefilter eben die entsprechende Liste befüllen.

Zum Teil könnte man das zB in Repositories auslagern, kommt immer drauf an, wo und wie man das benutzt.
In einem eigenen Repo? Habe ich bisher noch nie gemacht.
 
Die Listen in den einzelnen Klassen selbst (also die MetaData und Season-Listen).
Die Methode soll dann über einen Filefilter eben die entsprechende Liste befüllen.
Ah, die beiden Klassen sollen ihre Daten selbst aus den Dateien laden?

Würde ich vermeiden, dass macht das ganze deutlich komplizierter. Besser ist meist, das Laden an etwas anderes zu delegieren (zb ein Repository) und dann ein fertiges Objekt zu bekommen.

In einem eigenen Repo? Habe ich bisher noch nie gemacht.
Repository-Pattern, das meint nicht ein eigenes git-Repo oder so ;)
 
Ok, ein Großteil ist bereits umstrukturiert und ich habe das Kapitel über Observer-Pattern aus "Entwurfsmuster von Kopf bis Fuß" durchgearbeitet und sollte das dann (hoffentlich) auch so in meinem Projekt umsetzen können.

Was ich nach lesen einiger Artikel im Internet noch nicht so ganz verstanden habe, ist das Repository Pattern.
Ich habe ja bereits ein Interface "MetaData", das ich dann also als Repository umfunktioniere? Bin ehrlich gesagt noch nicht so ganz dahinter gestiegen, wie das Interface dann aufgebaut und Implementiert werden soll.

Mein Repo besteht dann aus KodiNfo und Picture Objekten und wird dann über das Repo-Interface den anderen Klassen zur Verfügung gestellt, richtig?
 
Was ich nach lesen einiger Artikel im Internet noch nicht so ganz verstanden habe, ist das Repository Pattern.
Ich habe ja bereits ein Interface "MetaData", das ich dann also als Repository umfunktioniere? Bin ehrlich gesagt noch nicht so ganz dahinter gestiegen, wie das Interface dann aufgebaut und Implementiert werden soll.

Mein Repo besteht dann aus KodiNfo und Picture Objekten und wird dann über das Repo-Interface den anderen Klassen zur Verfügung gestellt, richtig?
MetaData (und Kindobjekte) sind Entitäten, die würde man über ein Repository laden, sie wären aber kein Repository ;)

Man könnte sich zB ein TvShowRepository vorstellen, das hätte dann zB eine List<TvShow> findAll()-Methode, welche alle existierenden TvShows zurückgibt. Die TvShows, die zurückgegeben werden, sind dann völlig initialisiert, und haben zB schon die gefüllten Listen mit MetaDaten.

Wie das Laden (und Speichern) passiert, abstrahiert das Repository, das kann dann zB intern mit GSON passieren, womit die FileUtil-Klasse größtenteils überflüssig ist (wobei natürlich ein paar Methoden später in ähnlicher Form in der Repository-Implementierung zu finden sein können).
 
A

Anzeige




Vielleicht hilft dir das hier weiter: (klicke hier)
Passende Stellenanzeigen aus deiner Region:

Neue Themen

Oben