1. Diese Seite verwendet Cookies. Wenn du dich weiterhin auf dieser Seite aufhältst, akzeptierst du unseren Einsatz von Cookies. Weitere Informationen

Dynamische Methodik und nicht-spezifische Rückgabe werte von Methoden

Dieses Thema im Forum "Enterprise Java (JEE, J2EE, Spring & Co.)" wurde erstellt von StormCraftable, 17. Mai 2016.

?

Haltet ihr so etwas für Sinnvoll?

  1. Ja

    100,0%
  2. Nein

    0 Stimme(n)
    0,0%
  1. StormCraftable

    StormCraftable Grünschnabel

    Halli Hallo!
    Meine Frage ist sehr speziell und ich weiß nicht, ob ich hier richtig bin, aber ich beschreibe mal, was ich versuche zu erreichen.
    Ich möchte etwas ähnliches wie Spring umsetzen, das schon mal vor weg. Nur soll dieses wesentlich mehr an Dependencie Injection für "Modulare Programmierung" orientiert sein. Dazu folgende Idee:
    Ich möchte eine "DataOutputPipe" haben, die eine statische, innere "HashMap" beinhaltet, in welcher sich (Key : Value) [String (Der Name) : Das_Object] befindet. Des weiteren soll man Objekte hinzufügen, sowie bekommen können, also 2 Methoden die im Prinzip "addData(Das_Object object)" und "getData(String key)" heißen.
    Mein Problem liegt dabei in der Methode getData(). Welche Objekte man der "DataOutputPipe" gibt soll nicht vor-definiert sein. Das heißt, füge ich bei einem mal die Klasse A der DataOutputPipe hinzu, soll man diese bekommen und nutzen können, fügt man beim nächsten mal die Klasse B der DataOutputPipe hinzu, soll man auch dieses bekommen und nutzen können. Diesen dynamischen Rückgabe wert konnte ich bis heute nicht erfolgreich umsetzen. Das mag daran liegen, dass ich Jahrelang in php entwickelt habe und jetzt ein bisschen über die Typsicherheit von Java stolpere.. auch wenn das eigentlich besser ist.
    Vorteile davon wären:
    - Objekte müssen nicht instantiiert werden. Man bekommt von der DataOutputPipe (ähnlich wie bei einem Factory-Patern) fertige Objekte.
    - Man bekommt (wenn man will) immer die gleichen Objekte, wie bei einem Singlton-Patern.
    - Unabhängig von der Instantiierung der DataOutputPipe kann jedes beliebige Objekt die gleiche Instanz eines beliebigen Objektes bekommen.

    Das Problem, auf welches ich bisher gestoßen bin, ist, dass ich sehr eingeschränkt bin, wenn ich abstract classes, bzw interfaces für die einzelnen Daten nutze. Das würde heißen, dass jede Klasse in der DataOutputPipe nur eine bestimmte Menge an Methoden haben könnte. Das soll so nicht sein.

    Meine Motivation ist, dass ich diese DataOutputPipe in ein, vor längerer Zeit von mir geschriebenem RegisterHandler-Framework einpflegen möchte. Dieses RegisterHandler-Framework sorgt für Kommunikation zwischen Objekten, ohne Objekte und / oder primitive Datentypen übergeben muss.

    Ferner wäre es unglaublich cool, wenn man der DataOutputPipe sagen werden soll, "füge automatisch alle Klassen mit der custom Annotation @bla hinzu", nur dazu kenne ich mich noch nicht gut genug mit custom Annotations aus (sowas bekommt man im Studium nicht erklärt :D )

    Meine konkrete Frage: Ist so etwas möglich? Und wen ja, wie würde man so etwas am besten umsetzen.

    Hier etwas Pseudocode, wie ich es mir vorstelle:

    Überall wo DataComponent steht, habe ich keine Ahnung, was da hin muss, bzw. wie es anders aufgebaut besser wäre.

    Die DataOutputPipe
    Code (Java):
    1. package pipe;
    2.  
    3. public class DataOutputPipe {
    4.  
    5.     private static DataContainerList<DataComponent> dataComponentContainer; public DataOutputPipe() {
    6.         DataOutputPipe.dataComponentContainer = new DataContainerList<>();
    7.     }
    8.  
    9.     public DataOutputPipe(DataContainerList<DataComponent> dataComponentContainer) {
    10.  
    11.     }
    12.  
    13.     public static DataComponent getData(String name) {
    14.         return DataOutputPipe.dataComponentContainer.get(name);
    15.     }
    16.  
    17.     public static void addData(DataComponent component) {
    18.         DataOutputPipe.dataComponentContainer.add(component.getDataName(), component);
    19.     }
    20. }
    Und hier die DataContainerList, welche später die Daten beinhalten soll.

    Code (Java):
    1. package pipe;
    2.  
    3. import java.util.HashMap;
    4. import java.util.Iterator;
    5. /**
    6. * Eine DataContainerList wird genutzt um DataComponent's zu speichern
    7. * @param <DataComponent>
    8. */
    9.  
    10. class DataContainerList<DataComponent> {
    11.  
    12.     private static HashMap<String, DataComponent> dataComponentList;DataContainerList() {
    13.         createDataComponentList();
    14.     }
    15.  
    16.     public void add(String assertetName, DataComponent component) {
    17.         dataComponentList.put(assertetName,component);}
    18.  
    19.         public void add(DataComponent component)
    20.         dataComponentList.put(component.getDataName(),component);
    21.     }
    22.  
    23.     public DataComponent get(String objectName) {
    24.         return dataComponentList.get(objectName);
    25.     }
    26.  
    27.  
    28.     private void createDataComponentList() {
    29.         if(dataComponentList == null) {
    30.             dataComponentList = new HashMap<>();
    31.         }
    32.     }
    33.  
    34.  
    35.     private boolean testForDuplicate(Object component) {
    36.         Iterator dataComponentListIterator = dataComponentList.entrySet().iterator();
    37.         while (dataComponentListIterator.hasNext()) {
    38.             if(dataComponentListIterator.next().equals(component)) {
    39.                 return true;
    40.             }
    41.         }
    42.         return false;
    43.     }
    44. }
    Ich entschuldige mich für den langen Post und hoffe ich habe etwas Struktur beibehalten, Ich bin noch nicht so perdu mit dem Forum! Sollten euch Rechtschreibfehler, oder Formatierungs-Fehler auffallen, tut es mir leid.
    Vielen Dank schon mal im Voraus für eure Zeit!
     
    Zuletzt bearbeitet: 17. Mai 2016
  2. Improof

    Improof Erfahrenes Mitglied

    Hi,

    also ich gebe zu wahrscheinlich auf Anhieb nicht alles korrekt verstanden zu haben, aber zu deiner Problematik mit dynamischen Rückgabewerten:

    Einfachste Art und Weise wäre, deine Methode getData gibt einfach Object zurück...dann kannst du immer alles zurückgeben, aber du weißt halt entweder nicht, was es wirklich für ein Objekt ist oder musst dann erst noch casten...

    Besser: generischer Rückgabewert!

    Code (Java):
    1. public <T> T getData(String name, Class<T> type) {
    2.     return type.cast(map.get(name));
    3. }
    Natürlich fehlt da noch Error-Handling, was ist, wenns das nicht gibt usw.
    Die Elemente werden dann richtig gecastet:

    Code (Java):
    1. MyClass obj = dataOutputPipe.getData(name, MyClass.class);
    Jetzt ist aber nur das Problem gelöst, dass du nicht mehr casten musst. Wenn du nicht weißt, was für ein Klasse sich hinter dem Namen verbirgt bist du aufgeschmissen. Aber sowas kann man auch lösen:

    Code (Java):
    1. public Class<?> getType(String name) {
    2.     return map.get(name).getClass();
    3. }
    Verbunden mit der anderen Methode, kriegst du dann deine Objekte immer richtig gecastet und dynamisch raus. Einziger Nachteil: Du musst den Namen 2 mal übergeben, die Methode getType irgendwie in den Hintergrund innerhalb von getData zu packen bringt nichts, da die Methode getData ja ihren dynamischen Returntype kennen muss. Und den musst du immer von außerhalb angeben. Der ganze Aufruf, um ein Objekt zu holen, wäre dann quasi:

    Code (Java):
    1. MyClass obj = dataOutputPipe.getData(name, dataOutputPipe.getType(name));

    Ich hoffe das ist so ungefähr was du suchst.


    Viele Grüße

    Daniel
     
    Zuletzt bearbeitet: 31. Mai 2016
    StormCraftable und sheel gefällt das.
  3. StormCraftable

    StormCraftable Grünschnabel

    Super! Vielen Dank! Das klappt schon ganz gut!
    Das einzige Problem, das ich momentan noch habe, ist das die Klasseninstanzen in diesem Fall abhängig sind von der DataOutputPipe.
    Hier mal das Beispiel:
    Die DataOutputPipe:
    Code (Java):
    1. package pipe;
    2.  
    3. import java.util.ArrayList;
    4. public class DataOutputPipe {
    5.  
    6.     private static ArrayList<String> dataKeyList = new ArrayList<>();
    7.     private static DataContainerList<String, Object> dataComponentList = new DataContainerList<>();
    8.     public void add(String name, Object component) {
    9.         dataComponentList.add(name, component);
    10.         dataKeyList.add(name);
    11.     }
    12.  
    13.     public <T> T getData(String name, Class<T> type) {
    14.         return type.cast(dataComponentList.get(name));
    15.     }
    16.  
    17.     public <T> T getData(String name) {
    18.         Object o = dataComponentList.get(name);
    19.         return (T) o.getClass().cast(dataComponentList.get(name));
    20.     }
    21.  
    22.     public Class<?> getType(String name) {
    23.         return dataComponentList.get(name).getClass();
    24.     }
    25.  
    26.     public ArrayList<String> getAllKeys() {
    27.         return dataKeyList;
    28.     }
    29.  
    30.     public boolean keyInDataOutputPipe(String key) {
    31.         return dataKeyList.contains(key);
    32.     }
    33.  
    34. }
    Hier ist die DataContainerList
    Code (Java):
    1. package pipe;
    2.  
    3. import java.util.Collections;
    4. import java.util.HashMap;
    5. import java.util.Map;
    6.  
    7. class DataContainerList<String, Object> {
    8.  
    9.     private Map<String, Object> dataComponentHashMap;
    10.  
    11.     DataContainerList() {
    12.         dataComponentHashMap = Collections.synchronizedMap(new HashMap<String,Object>());
    13.     }
    14.  
    15.     public void add(String key, Object data) {
    16.         dataComponentHashMap.put(key, data);
    17.     }
    18.  
    19.     public Object get(String key) {
    20.         return dataComponentHashMap.get(key);
    21.     }
    22.  
    23.     public void updateData(String key, Object data) {
    24.         dataComponentHashMap.put(key, data);
    25.     }
    26.  
    27.     public void remove(String key) {
    28.        dataComponentHashMap.remove(key);
    29.    }
    30. }
    Ich habe hier mal ein Beispiel gemacht.
    Dafür habe ich 3 Klassen, A, B und C. Alle haben als Atribut einen Namen und als Methoden eine setName()-Methode, die den Namen setzt (duh) und eine whoAmI()-Methode die den Namen ausgibt.

    Code (Java):
    1. package main;
    2.  
    3. import main.test.A;
    4. import pipe.DataOutputPipe;
    5.  
    6. public class Main {
    7.     public static void main(String[] args) {
    8.         DataOutputPipe dataOutputPipe = new DataOutputPipe();
    9.         dataOutputPipe.add(A.class.getName(),new A());
    10.        
    11.         A a = dataOutputPipe.getData(A.class.getName(), A.class);
    12.         a.whoAmI();
    13.         a.setName("nichtA");
    14.         a.whoAmI();
    15.         A a2 = dataOutputPipe.getData(A.class.getName(), A.class);
    16.         a2.whoAmI();
    17.     }
    18. }
    Diese Main-Methode gibt das folgende aus:

    Mein Name ist "A"
    Mein Name ist "nichtA"
    Mein Name ist "nichtA"

    Die DataOutputPipe sollte aber immer das gleiche Objekt zurück geben. In php würde ich für eine Klasse eine Datei erstellen und diese einbinden, wenn ich sie bräuchte. Dann würde ich bei den Atributen, die ich ändern wollte mit Session-Variablen arbeiten und den Rest von der Instanz des Objektes abhängig machen, so dass die Ausgabe so aussähe:

    Mein Name ist "A"
    Mein Name ist "nichtA"
    Mein Name ist "A"


    Nur wenn ich sage:
    Code (Java):
    1. dataContainerList.update(key, a)
    soll das nächste Objekt, das ich anfrage eben jenes sein, welches ich hier speichere.
    Dafür müsste ich aber Klassen und nicht Objekte speichern, oder bin ich da auf dem falschen Dampfer? Und dann bei
    Code (Java):
    1. dataOutputPipe.getData(A.class.getName(), A.class)
    müsste es dann heißen
    Code (Java):
    1. return new dataContainerList.get(key);
    ?
    Nur dann wäre das Problem, dass man nicht updaten kann, weil man Objekte nicht zu Klassen casten kann, oder?
    Uah, ich bin verwirrt :D
     
    Zuletzt bearbeitet: 18. Mai 2016
  4. StormCraftable

    StormCraftable Grünschnabel

    Okay, also, nach längerem Studium der Java Reflections API habe ich eine Lösung gefunden.
    Vorab der Quellcode, dann die Erklärung.
    Code (Java):
    1. package pipe;
    2. import java.lang.reflect.Constructor;
    3. import java.lang.reflect.InvocationTargetException;
    4. import java.util.ArrayList;
    5.  
    6. public class DataOutputPipe {
    7.  
    8.     private static ArrayList<String> dataKeyList = new ArrayList<>();
    9.     private static ObjectedModuleContainerList<String, Object> moduleContainerList = new ObjectedModuleContainerList<>();
    10.     private static ObjectedModuleContainerList<String, Object> objectedModuleContainerList = new ObjectedModuleContainerList<>();
    11.     public void add(String name, Object component) {
    12.         if(moduleContainerList.contains(name)) {
    13.             // TODO Exceptionhandling
    14.             return;
    15.         }
    16.         DataOutputPipe.moduleContainerList.addObjectedModule(name, component);
    17.         DataOutputPipe.dataKeyList.add(name);
    18.     }
    19.  
    20.     public <T> T getModule(String name, Class<T> type) {
    21.         T toreturn = null;
    22.         Class<?> classToInstance = moduleContainerList.getObjectedModule(name).getClass();
    23.         String classNameToInstance = classToInstance.getName();
    24.    
    25.         // TODO Exceptionhandling
    26.         try {
    27.             Class<?> clazz = Class.forName(classNameToInstance);
    28.             Constructor<?> ctor = clazz.getConstructor();
    29.             Object object = ctor.newInstance();
    30.             toreturn = type.cast(object);
    31.         } catch (InstantiationException e) {
    32.             e.printStackTrace();
    33.         } catch (IllegalAccessException e) {
    34.             e.printStackTrace();
    35.         } catch (InvocationTargetException e) {
    36.             e.printStackTrace();
    37.         } catch (NoSuchMethodException e) {
    38.             e.printStackTrace();
    39.         } catch (ClassNotFoundException e) {
    40.             e.printStackTrace();
    41.         }
    42.         return toreturn;
    43.     }
    44.  
    45.     public <T> T getModule(String name) {
    46.         Object o = DataOutputPipe.moduleContainerList.getObjectedModule(name);
    47.         Class<T> type = (Class<T>) o.getClass();
    48.         return getModule(name, type);
    49.     }
    50.  
    51.     public <T> T getObjectedModule(String key) {
    52.         Object o = objectedModuleContainerList.getObjectedModule(key);
    53.         return (T) o.getClass().cast(objectedModuleContainerList.getObjectedModule(key));
    54.     }
    55.  
    56.     public void updateObjectedModule(String key, Object instanceToSafe) {
    57.         objectedModuleContainerList.addObjectedModule(key, instanceToSafe);
    58.     }
    59.  
    60.     public Class<?> getType(String name) {
    61.         return DataOutputPipe.moduleContainerList.getObjectedModule(name).getClass();
    62.     }
    63.  
    64.     public ArrayList<String> getAllKeys() {
    65.         return DataOutputPipe.dataKeyList;
    66.     }
    67.  
    68.     public boolean keyInDataOutputPipe(String key) {
    69.         return DataOutputPipe.dataKeyList.contains(key);
    70.     }
    71.  
    72. }
    Hier fehlt noch ne Menge, aka custom-exceptions usw. aber so etwas ist ein funktionierender Prototyp.
    Also, was ist das hier.

    Die anderen Dateien wurden nicht groß verändert.
    Zum bessern Verständnis hier mal mit Beispiel.

    Code (Java):
    1. package main;
    2.  
    3. import main.test.A;
    4. import main.test.Tester;
    5. import pipe.DataOutputPipe;
    6.  
    7. public class Main {
    8.     public static void main(String[] args) {
    9.         DataOutputPipe dataOutputPipe = new DataOutputPipe();
    10.         dataOutputPipe.add(A.class.getName(), new A());
    11.  
    12.         A a = dataOutputPipe.getModule(A.class.getName());
    13.         a.whoAmI();
    14.         a.setName("nichtA");
    15.         a.whoAmI();
    16.  
    17.         dataOutputPipe.updateObjectedModule(A.class.getName(), a);
    18.  
    19.         A a2 = dataOutputPipe.getModule(A.class.getName());
    20.         a2.whoAmI();
    21.  
    22.         A a3 = dataOutputPipe.getObjectedModule(A.class.getName());
    23.         a3.whoAmI();
    24.  
    25.     }
    26. }
    Ausgabe:
    Mein Name ist "A"
    Mein Name ist "nichtA"
    Mein Name ist "A"
    Mein Name ist "nichtA"

    Also, der Ablauf.
    Als erstes instantiieren wir eine DataOutputPipe. Dies könnte man auch static machen, oder als Singleton, ist hier aber unwesentlich.
    Wir fügen dann der DataOutputPipe ein Modul hinzu, die Klasse A. Die Klasse A hat wie vorher auch, ein Atribut name und eine Methode whoAmI und setName.
    Hier wäre es cool, wenn das mit annotations klappen könnte
    In der DataOutputPipe wird nun in einer static instanz der DataContainerList, die in meinem vorangegangenen Post steht, von der nur der Name geändert wurde.
    Dort wird das Instantiierte Objekt gespeichert.
    Wenn wir in der nächsten Zeile dann sagen
    Code (Java):
    1.         A a = dataOutputPipe.getModule(A.class.getName());
    returnt die DataOutputPipe eine neue Instanz, des gespeicherten Objektes. So ist a unabhängig von der DataOutputPipe.
    mit whoAmI wird dann ausgegeben ' Mein Name ist "A" '. Wir ändern daraufhin den Namen mit setName und rufen erneut whoAmI auf. Wie erwartet ist die Ausgabe nun ' Mein Name ist "nichtA" '.
    Als nächstes speichern wir uns mit:
    Code (Java):
    1.        dataOutputPipe.updateObjectedModule(A.class.getName(), a);
    die aktuelle Instanz von a mit dem neuen Namen "nichtA". Und speichern uns dann in a2 eine weitere "Basis-Instanz" von A, nur um sicher zu gehen, dass wir diese noch genau so bekommen können, nachdem wir eine neue gespeichert haben, was wir mit a2.whoAmI() bestätigen.
    Interessanter wird es mit a3. In a3 speichern wir uns mit:
    Code (Java):
    1.         A a3 = dataOutputPipe.getObjectedModule(A.class.getName());
    eine Instanz von der geänderten a-Klasse. Und auch das bestätigen wir durch whoAmI, welches "nichtA" ausgibt.
    Ist die DataOutputPipe verständlich, oder soll ich die auch noch einmal erklären?
    Und kennt sich jemand mit Custom-Annotations aus, so dass man sagen kann
    Code (Java):
    1. @Module
    2. public class A {
    3. ....
    Wird die Klasse A automatisch geladen, so dass der code-Teil
    Code (Java):
    1.         dataOutputPipe.add(A.class.getName(), new A());
    nicht mehr notwendig wird? Oder ist so etwas utopisch, allein und "quick and dirty" zu entwickeln?

    Mit den besten Grüßen!
     
    sheel gefällt das.
  5. StormCraftable

    StormCraftable Grünschnabel

    Ich hoffe ich gehe hier keinen auf die Nerven... Nur muss ich das mitteilen...
    Es klappt auch. Der Prototyp, nur ohne tatsächliche Register ist fertig.
    Ich teile hier mal meinen Quellcode. Nur, da dies nicht die tatsächliche Lösung ist, soll ich das Thema schon als gelöst markieren? Oder erst, wenn ich es auch mit Registern zum Laufen gebracht habe?
    Nun aber mal zu dem coolen Teil: Ich haue erst einmal etwas Quellcode raus und versuche das dann zu erklären. Natürlich ist es nicht möglich, das komplett zu erklären, das würde zu lange dauern zu schreiben/lesen und ein tl;dr will keiner.
    Zu erst eine custom Annotation. Wir wollen später alle Klassen, mit dieser Annotation automatisch einbinden in die DataOutputPipe.
    Code (Java):
    1. package annotations;
    2.  
    3. import java.lang.annotation.ElementType;
    4. import java.lang.annotation.Retention;
    5. import java.lang.annotation.RetentionPolicy;
    6. import java.lang.annotation.Target;
    7.  
    8. @Target(ElementType.TYPE)
    9. @Retention(RetentionPolicy.RUNTIME)
    10. public @interface RegisterModul {
    11.     boolean integratedInRegisterHandler() default true;
    12. }
    In diesem Beispiel nutze ich abermals die Klasse A als positives Beispiel. Ich habe noch kein negatives Beispiel (aka eine Klasse, die nicht in der DataOutputPipe ist, aber trotzdem angefordert wird)
    Code (Java):
    1. package main.test;
    2.  
    3. import annotations.RegisterModul;
    4.  
    5.  
    6. @RegisterModul
    7. public class A {
    8.  
    9.     private String name;
    10.  
    11.     public A () {
    12.         name = "A";
    13.     }
    14.  
    15.     public String getDataName() { return this.name; }
    16.  
    17.     public void setName(String name) { this.name = name; }
    18.  
    19.     public void whoAmI() {
    20.         System.out.println("Mein Name ist \""+getDataName()+"\"");
    21.     }
    22. }
    Nun "die Magie", der RegisterHandler.
    Code (Java):
    1. package handler;
    2.  
    3. import handler.scanner.AnnotationScanner;
    4. import pipe.DataOutputPipe;
    5.  
    6. import java.util.List;
    7.  
    8. public class RegisterHandler {
    9.  
    10.     private static List<Class<?>> allModules;
    11.     private static DataOutputPipe dataOutputPipe;
    12.     private static RegisterHandler instance;
    13.  
    14.     public synchronized static RegisterHandler getInstance() {
    15.         if(instance == null) {
    16.             instance = new RegisterHandler();
    17.         }
    18.         return instance;
    19.     }
    20.  
    21.     private RegisterHandler() {
    22.         if(RegisterHandler.allModules == null) {
    23.             RegisterHandler.allModules = AnnotationScanner.getInstance().getAllAnnotadedClasses();
    24.             RegisterHandler.dataOutputPipe = DataOutputPipe.getInstance();
    25.             for(Class<?> c : RegisterHandler.allModules) {
    26.                 try {
    27.                     RegisterHandler.dataOutputPipe.add(c.getName(), c.newInstance());
    28.                 } catch (InstantiationException | IllegalAccessException e) {
    29.                     e.printStackTrace();
    30.                 }
    31.             }
    32.         }
    33.  
    34.     }
    35.  
    36.     public <T> T getClass(String key) {
    37.         return dataOutputPipe.getModule(key);
    38.     }
    39. }
    Der RegisterHandler soll später noch die Möglichkeit bieten, neue Register zu erstellen, Register auf Namen zu binden, damit auch andere / mehrere Prozesse ohne eine Instanz-Übergabe und ohne Observable-Pattern immer die selbe Instanz eines Objektes haben.
    Der RegisterHandler nutzt eine weiter Klasse, neben der DataOutputPipe, den AnnotationScanner.
    Diese Klasse sieht so aus:
    Code (Java):
    1. package handler.scanner;
    2.  
    3. import annotations.RegisterModul;
    4.  
    5. import java.util.List;
    6.  
    7. public class AnnotationScanner {
    8.  
    9.     private static List<Class<?>> annotadedClasses;
    10.  
    11.  
    12.     private static PathScanner pathScanner;
    13.  
    14.     private static String rootPath;
    15.  
    16.     private static AnnotationScanner ourInstance;
    17.     private static List<Class<?>> classesWithAnnotatedType;
    18.  
    19.  
    20.     public static AnnotationScanner getInstance() {
    21.         if(ourInstance == null) {
    22.             ourInstance = new AnnotationScanner();
    23.         }
    24.         return ourInstance;
    25.     }
    26.  
    27.     private AnnotationScanner() {
    28.         System.out.println("$ Starting up PathScanner ... ");
    29.         pathScanner = PathScanner.getInstance();
    30.         System.out.println("$ PathScanner started successfully!");
    31.     }
    32.  
    33.     public List<Class<?>> getAllAnnotadedClasses() {
    34.         if(rootPath == null) {
    35.             rootPath = "main";
    36.         }
    37.         return getClassesWithAnnotatedType();
    38.     }
    39.  
    40.     private List<Class<?>> getClassesWithAnnotatedType() {
    41.         if(AnnotationScanner.classesWithAnnotatedType != null) {
    42.             return AnnotationScanner.classesWithAnnotatedType;
    43.         }
    44.         System.out.println("$ Loading annotaded classes for the first Time ... ");
    45.         List<Class<?>> allClasses = pathScanner.find(rootPath);
    46.         System.out.println("found: " + allClasses.size() + " classes");
    47.         int numberClasses = allClasses.size();
    48.         for(int i = 0 ; i < numberClasses ; i++) {
    49.             System.out.print("prozessing class " + allClasses.get(i).getName() + " ... ");
    50.             if(allClasses.get(i).isAnnotationPresent(RegisterModul.class)) {
    51.                 System.out.print(" this class is annotated to be auto-implemented ... ");
    52.                 allClasses.add(allClasses.get(i));
    53.             }
    54.             System.out.println("done");
    55.         }
    56.         AnnotationScanner.classesWithAnnotatedType = allClasses;
    57.         return allClasses;
    58.     }
    59.  
    60. }
    Kurz gesagt, lädt diese Klasse alle Klassen mit der custom Annotation mit Hilfe der folgenden Klasse:
    Code (Java):
    1. package handler.scanner;
    2.  
    3. import java.io.File;
    4. import java.net.URL;
    5. import java.util.ArrayList;
    6. import java.util.List;
    7.  
    8. public class PathScanner {
    9.  
    10.     private static PathScanner ourInstance;
    11.  
    12.     public static PathScanner getInstance() {
    13.         if(ourInstance == null) {
    14.             System.out.print("$ Initialing PathScanner for the first time... ");
    15.             ourInstance = new PathScanner();
    16.             System.out.println("done");
    17.         }
    18.         return ourInstance;
    19.     }
    20.  
    21.     private PathScanner() {
    22.  
    23.     }
    24.  
    25.     private static final char PKG_SEPARATOR = '.';
    26.  
    27.     private static final char DIR_SEPARATOR = '/';
    28.  
    29.     private static final String CLASS_FILE_SUFFIX = ".class";
    30.  
    31.     private static final String BAD_PACKAGE_ERROR = "Unable to get resources from path '%s'. Are you sure the package '%s' exists?";
    32.  
    33.     public List<Class<?>> find(String scannedPackage) {
    34.         String scannedPath = scannedPackage.replace(PKG_SEPARATOR, DIR_SEPARATOR);
    35.         URL scannedUrl = Thread.currentThread().getContextClassLoader().getResource(scannedPath);
    36.         if (scannedUrl == null) {
    37.             throw new IllegalArgumentException(String.format(BAD_PACKAGE_ERROR, scannedPath, scannedPackage));
    38.         }
    39.         File scannedDir = new File(scannedUrl.getFile());
    40.         List<Class<?>> classes = new ArrayList<Class<?>>();
    41.         for (File file : scannedDir.listFiles()) {
    42.             classes.addAll(find(file, scannedPackage));
    43.         }
    44.         return classes;
    45.     }
    46.  
    47.     private List<Class<?>> find(File file, String scannedPackage) {
    48.         List<Class<?>> classes = new ArrayList<Class<?>>();
    49.         String resource = scannedPackage + PKG_SEPARATOR + file.getName();
    50.         if (file.isDirectory()) {
    51.             for (File child : file.listFiles()) {
    52.                 classes.addAll(find(child, resource));
    53.             }
    54.         } else if (resource.endsWith(CLASS_FILE_SUFFIX)) {
    55.             int endIndex = resource.length() - CLASS_FILE_SUFFIX.length();
    56.             String className = resource.substring(0, endIndex);
    57.             try {
    58.                 classes.add(Class.forName(className));
    59.             } catch (ClassNotFoundException ignore) {
    60.             }
    61.         }
    62.         return classes;
    63.     }
    64. }
    Diese Klasse habe ich aus Artefakten aus dem Internet kreiert. Sie durchsucht quasi ein "root"-verzeichniss, welches aktuell noch durch ein Atribut bestimmt wird (hier empfiehlt sich eine Konfigurationsdatei o.ä). Ich habe überlegt hier eine fertige API zu nutzen, nur wollte ich dies nach Möglichkeit ohne Abhängigkeiten von irgendwas erstellen, was auch geklappt hat!
    Zu guter letzt noch die Main-Klasse, zum testen:
    Code (Java):
    1. package main;
    2.  
    3. import handler.RegisterHandler;
    4. import main.test.A;
    5.  
    6.  
    7. public class Main {
    8.     public static void main(String[] args) {
    9.  
    10.         RegisterHandler d = RegisterHandler.getInstance();
    11.         A a = d.getClass(A.class.getName());
    12.         a.whoAmI();
    13.  
    14.     }
    15. }
    Die Ausgabe ist (mit allen Prototyp-Ausgaben in den anderen Klassen kombiniert):

    $ Starting up PathScanner ...
    $ Initialing PathScanner for the first time... done
    $ PathScanner started successfully!
    $ Loading annotaded classes for the first Time ...
    found: 5 classes
    prozessing class main.Main ... done
    prozessing class main.test.Tester ... done
    prozessing class main.test.C ... done
    prozessing class main.test.A ... this class is annotated to be auto-implemented ... done
    prozessing class main.test.B ... done

    Mein Name ist "A"


    Es funktioniert. Wenn auch noch nicht perfekt, wie es am ende sein soll, aber es funktioniert.
    der Methodenaufruf
    Code (Java):
    1. A a = d.getClass(A.class.getName());
    soll später ersetzt werden durch das öffnen eines neuen Registers, bzw. dem joinen eines bereits bestehenden Registers , in welchen man auf die Objekte zugreifen kann.
    Ich hoffe ich habe nichts vergessen.. Ist das alles so weit verständlich? Soll ich etwas mehr erklären? Oder erst einmal den Prototyp ein einen Entwurfstypen überführen?
    Oder soll ich ganz aufhören, hier zu schreiben?

    Mit den besten Grüßen!
     
    Zuletzt bearbeitet: 19. Mai 2016
  6. sheel

    sheel I love Asm Administrator

    Hi

    Bitte nicht aufhören :)

    Wollte eigentlich auch schon eine Antwort schreiben (zum Thema, und länger als die hier) und bin nur noch nicht dazu gekommen. Tut mir leid, wenn das zurzeit etwas nach Alleinunterhaltung ausschaut. Es liegt sicher nicht daran, dass deine Posts irgendwie schlecht oder fehl am Platz wären, ganz im Gegenteil!

    Ich hoffe, ich komme morgen dazu, auch was themenbezogenes beizutragen...
     
  7. StormCraftable

    StormCraftable Grünschnabel

    Okay, dann kommt jetzt gleich mal der 2. Prototyp :D
    Ich habe die binären Register fertig. Die können 3 elementare Operationen. Pull, fetch und push. Alle die schon mal mit Version-Controll gearbeitet haben, wird das etwas sagen. Auch hier mal wieder ein paar Code-Schnipsel. Sehr viel bestehendes hat sich nicht geändert. Lediglich der RegisterHandler-, sowie die Main-Klasse. In anderen Klassen wurden nur Ausgaben entfernt.

    Der RegisterHandler sieht nun wie folgt aus:
    Code (Java):
    1. package handler;
    2.  
    3. import handler.register.Register;
    4. import handler.register.RegisterID;
    5. import handler.scanner.AnnotationScanner;
    6. import pipe.DataOutputPipe;
    7.  
    8. import java.util.HashMap;
    9. import java.util.List;
    10.  
    11. public class RegisterHandler {
    12.  
    13.     private static List<Class<?>> allModules;
    14.     private static DataOutputPipe dataOutputPipe;
    15.     private static RegisterHandler instance;
    16.  
    17.     // Eine statische Liste alle Register, damit kein Name 2 mal vor kommt
    18.     private static HashMap<String, Register> registerList;
    19.  
    20.     public synchronized static RegisterHandler getInstance() {
    21.         if(instance == null) {
    22.             instance = new RegisterHandler();
    23.         }
    24.         return instance;
    25.     }
    26.  
    27.     private RegisterHandler() {
    28.         System.out.println("$ Initialing RegisterHandler for the first time ...");
    29.         if(RegisterHandler.allModules == null) {
    30.             RegisterHandler.allModules = AnnotationScanner.getInstance().getAllAnnotadedClasses();
    31.             RegisterHandler.dataOutputPipe = DataOutputPipe.getInstance();
    32.             int numberAllModules = allModules.size();
    33.  
    34.             /* Warum nicht foreach?
    35.              *
    36.              * Es gibt mehrere Stellen, an denen die Listen iterriert werden. Manchmal 2 oder mehr gleichzeitig.
    37.              * Wenn an jeder Stelle eine foreach schleife verwendet werden würde, würder der Listeninterne Interator ziemlich durcheinander kommen.
    38.              * Deswegen nutzen wir zum Iterieren der Listen besser normale for-schleifen.
    39.              */
    40.             for(int i = 0 ; i < numberAllModules ; i++) {
    41.                 try {
    42.                     RegisterHandler.dataOutputPipe.add(allModules.get(i).getName(), allModules.get(i).newInstance());
    43.                 } catch (InstantiationException | IllegalAccessException e) {
    44.                     e.printStackTrace();
    45.                 }
    46.             }
    47.         }
    48.         if(registerList == null) {
    49.             registerList = new HashMap<>();
    50.         }
    51.  
    52.     }
    53.  
    54.     public <T> T getModule(String key) {
    55.         return dataOutputPipe.getModule(key);
    56.     }
    57.  
    58.     public RegisterID pullNewRegister() {
    59.         Register newRegister = new Register();
    60.         registerList.put(newRegister.getRegisterId().toString(), newRegister);
    61.         return newRegister.getRegisterId();
    62.     }
    63.  
    64.     public <T> T getModuleFromRegister(RegisterID registerID, String className) {
    65.         return registerList.get(registerID.toString()).pullModule(className);
    66.     }
    67.  
    68.     public Register getRegisterForId(RegisterID registerID) {
    69.         return registerList.get(registerID.toString());
    70.     }
    71.  
    72. }
    Der RegisterHandler besitzt zwar noch die Methode
    Code (Java):
    1.   public <T> T getModule(String key)
    , welche in dem aktuellen Kontext redundant ist, aber sie kann zum testen genutzt werden.
    In Prinzip fehlt hier Abermals das Exception-Handling... :( Ich bitte das zu entschuldigen...
    Die Idee:
    - Pull ein register (ein neues Register erstellen) -> pullModuleFromPipe (fügt ein Modul von der DataOutputPipe dem Register hinzu) -> Arbeite mit diesem.
    Dafür gibt es 2 neue Datentypen:
    1. Das Register:
    Code (Java):
    1. package handler.register;
    2.  
    3. import pipe.DataOutputPipe;
    4.  
    5. import java.util.HashMap;
    6.  
    7. public class Register {
    8.  
    9.     // Die ID des Registers, eindeutig identifizierend
    10.     private RegisterID registerId;
    11.     // Die Instanz der DataOutputPipe
    12.     private DataOutputPipe dataOutputPipe;
    13.  
    14.     // Klassen, zugeschnitten auf die Instanz eines Registers
    15.     private HashMap<String, Object> moduleContainerList;
    16.  
    17.     public Register() {
    18.         moduleContainerList = new HashMap<>();
    19.         registerId = new RegisterID();
    20.         dataOutputPipe = DataOutputPipe.getInstance();
    21.     }
    22.  
    23.     public RegisterID getRegisterId() {
    24.         return registerId;
    25.     }
    26.  
    27.     public void fetchModuleFromPipe(String className) {
    28.         if(!moduleContainerList.containsKey(className)) {
    29.             moduleContainerList.put(className, dataOutputPipe.getModule(className));
    30.         }
    31.     }
    32.  
    33.     public void pullModuleFromPipe(String className) {
    34.         moduleContainerList.put(className, dataOutputPipe.getModule(className));
    35.     }
    36.  
    37.     public <T> T fetchAndGetModuleFromPipe(String className) {
    38.         fetchModuleFromPipe(className);
    39.         return (T) moduleContainerList.get(className);
    40.     }
    41.  
    42.     public <T> T pullAndGetModuleFromPipe(String className) {
    43.         pullModuleFromPipe(className);
    44.         return (T) moduleContainerList.get(className);
    45.     }
    46.  
    47.     public <T> T pullModule(String className) {
    48.         return (T) moduleContainerList.get(className);
    49.     }
    50.  
    51.     //TODO !
    52.     public void pushClassToRegister(Class<?> clazz) {
    53.  
    54.     }
    55. }
    Das Register hat folgende 3 elementare Aktionen:
    - Pull. Hole auf jeden Fall ein Modul aus der DataOutputPipe und füge es dem Register hinzu. Wenn es schon existiert, überschreibe es.
    - Fetch. Hole ein Modul aus der DataOutputPipe und füge es dem Register hinzu, außer es existiert schon.
    - Push. Schreibe ein Klasse in ein Register.

    und 2. Die RegisterID. Diese ist ein ziemlich einfacher Datentyp, aber ich mag den Stil.
    Code (Java):
    1. package handler.register;
    2.  
    3. import java.util.ArrayList;
    4. import java.util.List;
    5. import java.util.UUID;
    6.  
    7. public class RegisterID {
    8.     private static List<String> allRegisterIDs;
    9.     private String currentRegisterID;
    10.  
    11.     public RegisterID() {
    12.         allRegisterIDs = new ArrayList<>();
    13.         randomID();
    14.     }
    15.  
    16.     public String toString() {
    17.         return currentRegisterID;
    18.     }
    19.  
    20.     public void randomID() {
    21.         String toTest = UUID.randomUUID().toString();
    22.         while(allRegisterIDs.contains(toTest)) {
    23.             toTest = UUID.randomUUID().toString();
    24.         }
    25.         RegisterID.allRegisterIDs.add(toTest);
    26.         currentRegisterID = toTest;
    27.     }
    28. }
    Das ist ziemlich einfach. Auch wenn ich hier UUID benutze, gehe ich in der Methode randomID() sicher, dass die ID noch nicht existiert. Und das habe ich mir einfach gemacht, mit einer static List für alle vergebenen RegisterID's.

    Nun hier mal die übliche Main-Methode, mit der ich teste. Ich mache es hier mal einfach mit der getRegisterForId-Methode. Das verletzt etwas das Gesetz von Demeter, aber naja.. wird schon passen.
    Code (Java):
    1. package main;
    2.  
    3. import handler.RegisterHandler;
    4. import main.test.Tester;
    5.  
    6.  
    7. public class Main {
    8.     public static void main(String[] args) {
    9.  
    10.      
    11.         RegisterHandler registerHandler = RegisterHandler.getInstance();
    12.         RegisterID registerID = registerHandler.pullNewRegister();
    13.         A a = registerHandler.getRegisterForId(registerID).pullAndGetModuleFromPipe(A.class.getName());
    14.         A a2 = registerHandler.getRegisterForId(registerID).pullModule(A.class.getName());
    15.         a.whoAmI();
    16.         a2.whoAmI();
    17.  
    18.     }
    19. }
    Ausgabe:

    $ Initialing RegisterHandler for the first time ...
    $ Initialing AnnotationScanner for the first time ...
    $ Initialing PathScanner for the first time...
    $ Loading annotaded-classes for the first Time ...
    Mein Name ist "A"
    Mein Name ist "A"


    Was machen wir hier: Die oberen Ausgaben, die mit einem $ anfangen, werden bei den Singleton-Aufrufen, welche das erste mal eine Instanz erstellen, ausgegeben. Diese kann man ignorieren.
    Im Code erstellen wir zu erst einmal eine Instanz des RegisterHandler's. Dabei werden alle Klassen, des Root-Verzeichnisses rekursiv gescant usw. wie bereits in einen der Vorangegangen Post gezeigt.
    Dann pullen wir ein neues Register mit
    Code (Java):
    1. pullNewRegister()
    Dabei wird eine neue Instanz eines Register in der Liste in dem RegisterHandler erstellt. Im Anschluss könne wir mit der unschönen
    Code (Java):
    1. registerHandler.getRegisterForId(registerID).pullAndGetModuleFromPipe(A.class.getName());
    methode das Register ansprechen. Mit der Methode
    Code (Java):
    1. pullAndGetModuleFromPipe(SomeClassName);
    machen wir das folgende: Wir holen uns die Klasse die SomeClassName heißt aus der DataOutputPipe, speichern diese im Register und zugleich gibt diese Methode die Klasse zurück. Schluss endlich mit
    Code (Java):
    1. a.whoAmI();
    verifizieren wir, dass das auch wirklich a ist.

    Ich bin grade etwas zu sehr im Uni-Stress, als das ich einen vollständigen Feature-Test schreiben könnte.. Das hole ich nach, sobald ich etwas mehr Zeit habe.
    Außerdem würde ich gerne für euch ein Github repository bereitstellen. Das habe ich aber noch nie gemacht und jedes mal wenn ich das pushe und pulle, kann man es nicht mehr kompilieren.. Ich arbeite mit IntelliJ ultimate, das wäre kein Problem, es gibt auch eine Integration für eben jenes, nur habe ich Github noch nie genutzt.

    Ich möchte hier noch einmal betonen, dass ich keiner fertigen Frameworks nutze, da ich den RegisterHander so unabhängig wie möglich haben möchte!

    Das Fehlt noch:
    - Register auf einen bestimmten Namen binden, dass auch Prozesse, die sich auf einen Namen geeinigt haben, dieses Register nutzen können.
    - Doku (wie immer ... ).
    - Register-Templates. Das man sagen kann, erstelle ein neues register und pull gleich dieses und dieses Modul aus der Pipe.
    - Überdenken, ob der Klassenname das beste ist, ob Klassen in der OutputPipe zu identifizieren.
    - Konfiguration einlesen und nutzen.
    - Die Instantiierung der Klassen nicht von dem Constructor abhängig machen.

    Fällt euch noch was auf, was fehlt? Was ihr euch wünschen würdet? Oder wird das :offtopic:?

    Mit den besten Grüßen!
     
    Zuletzt bearbeitet: 20. Mai 2016
  8. StormCraftable

    StormCraftable Grünschnabel

    Ich wollte grade 2 mal schreiben, dass ich einen Fehler gefunden habe, aber mir ist beides mal aufgefallen, dass der Fehler nicht im Quellcode liegt, sondern in den Aufrufen :D
    Das ganze scheint doch noch komplexer zu sein, als es mir scheint. Da kam mir die Frage auf, ist das so verständlich? Soll ich das ganze etwas ausführlicher erklären? Oder gar von Grund auf neu erklären?
     
  9. StormCraftable

    StormCraftable Grünschnabel

    Ich nerve euch mal wieder. Dieses mal mit einem praktischen Beispiel!
    Doch vorab ein bisschen mehr code.
    Der RegisterHandler besitzt nun die Möglichkeit, Register aus eigene, bzw. "legere" ID's zu speichern. Intern werden damit RegisterID's durch legere ID's eindeutig identifiziert.
    Code (Java):
    1. package handler;
    2.  
    3. import handler.register.Register;
    4. import handler.register.RegisterID;
    5. import handler.scanner.AnnotationScanner;
    6. import pipe.DataOutputPipe;
    7.  
    8. import java.util.HashMap;
    9. import java.util.List;
    10.  
    11. public class RegisterHandler {
    12.  
    13.     private static List<Class<?>> allModules;
    14.     private static DataOutputPipe dataOutputPipe;
    15.     private static RegisterHandler instance;
    16.  
    17.     // Eine statische Liste alle Register, damit kein Name 2 mal vor kommt
    18.     private static HashMap<RegisterID, Register> registerList;
    19.     private static HashMap<String, RegisterID> boundRegisters;
    20.  
    21.     public synchronized static RegisterHandler getInstance() {
    22.         if(instance == null) {
    23.             instance = new RegisterHandler();
    24.         }
    25.         return instance;
    26.     }
    27.  
    28.     private RegisterHandler() {
    29.         System.out.println("$ Initialing RegisterHandler for the first time ...");
    30.         if(RegisterHandler.allModules == null) {
    31.             RegisterHandler.allModules = AnnotationScanner.getInstance().getAllAnnotadedClasses();
    32.             RegisterHandler.dataOutputPipe = DataOutputPipe.getInstance();
    33.             int numberAllModules = allModules.size();
    34.  
    35.             /* Warum nicht foreach?
    36.              *
    37.              * Es gibt mehrere Stellen, an denen die Listen iterriert werden. Manchmal 2 oder mehr gleichzeitig.
    38.              * Wenn an jeder Stelle eine foreach schleife verwendet werden würde, würder der Listeninterne Interator ziemlich durcheinander kommen.
    39.              * Deswegen nutzen wir zum Iterieren der Listen besser normale for-schleifen.
    40.              */
    41.             for(int i = 0 ; i < numberAllModules ; i++) {
    42.                 try {
    43.                     RegisterHandler.dataOutputPipe.add(allModules.get(i).getName(), allModules.get(i).newInstance());
    44.                 } catch (InstantiationException | IllegalAccessException e) {
    45.                     e.printStackTrace();
    46.                 }
    47.             }
    48.         }
    49.         if(registerList == null) {
    50.             registerList = new HashMap<>();
    51.         }
    52.         if(boundRegisters == null) {
    53.             boundRegisters = new HashMap<>();
    54.         }
    55.  
    56.     }
    57.  
    58.     public boolean registerIDTaken(RegisterID id) {
    59.         return registerList.containsKey(id);
    60.     }
    61.  
    62.     public boolean registerBound(String legereID) {
    63.         return boundRegisters.containsKey(legereID);
    64.     }
    65.  
    66.     public RegisterID pullNewRegister() {
    67.         Register newRegister = new Register();
    68.         registerList.put(newRegister.getRegisterId(), newRegister);
    69.         return newRegister.getRegisterId();
    70.     }
    71.  
    72.     public <T> T getModuleFromRegister(RegisterID registerID, String className) {
    73.         return registerList.get(registerID.toString()).pullModule(className);
    74.     }
    75.  
    76.     public Register getRegisterForId(RegisterID registerID) {
    77.         return registerList.get(registerID);
    78.     }
    79.  
    80.     public Register getRegisterForId(String legereId) {
    81.         return getRegisterForId(boundRegisters.get(legereId));
    82.     }
    83.  
    84.     public void bindRegister(String legereID, RegisterID id) {
    85.         boundRegisters.put(legereID, id);
    86.     }
    87.  
    88.     public RegisterID getRegisterID(String legereID) {
    89.         return boundRegisters.get(legereID);
    90.     }
    91.  
    92. }
    Die Register haben auch ein kleines Update bekommen. Nicht groß, nur ein paar Kleinigkeiten. Das will ich euch aber nicht vorenthalten.
    Code (Java):
    1. package handler.register;
    2.  
    3. import pipe.DataOutputPipe;
    4.  
    5. import java.util.HashMap;
    6.  
    7. public class Register {
    8.  
    9.     /**
    10.      * The identifier of this register
    11.      */
    12.     private RegisterID registerId;
    13.     /**
    14.      *
    15.      */
    16.     // Die Instanz der DataOutputPipe
    17.     private DataOutputPipe dataOutputPipe;
    18.  
    19.     // Klassen, zugeschnitten auf die Instanz eines Registers
    20.     private HashMap<String, Object> moduleContainerList;
    21.  
    22.     public Register() {
    23.         moduleContainerList = new HashMap<>();
    24.         registerId = new RegisterID();
    25.         dataOutputPipe = DataOutputPipe.getInstance();
    26.     }
    27.  
    28.     public void fetchModuleFromPipe(String className) {
    29.         if(!moduleContainerList.containsKey(className)) {
    30.             moduleContainerList.put(className, dataOutputPipe.getModule(className));
    31.         }
    32.     }
    33.  
    34.     public void pullModuleFromPipe(String className) {
    35.         moduleContainerList.put(className, dataOutputPipe.getModule(className));
    36.     }
    37.  
    38.     public <T> T fetchAndGetModuleFromPipe(String className) {
    39.         fetchModuleFromPipe(className);
    40.         return (T) moduleContainerList.get(className);
    41.     }
    42.  
    43.     public <T> T pullAndGetModuleFromPipe(String className) {
    44.         pullModuleFromPipe(className);
    45.         return (T) moduleContainerList.get(className);
    46.     }
    47.  
    48.     public <T> T pullModule(String className) {
    49.         return (T) moduleContainerList.get(className);
    50.     }
    51.  
    52.     public void pushClassToRegister(Class<?> clazz) {
    53.         pushClassToRegister(clazz.getName(), clazz);
    54.     }
    55.  
    56.     public void pushClassToRegister(String className, Object object) {
    57.         moduleContainerList.put(className, object);
    58.     }
    59.  
    60.     public RegisterID getRegisterId() {
    61.         return registerId;
    62.     }
    63. }
    Soooo und jetzt zum proof of concept! Ich mache es hier mit 2 Top-Level Instanzen. Stellt euch vor, es wären hier mehrere Top- bis Medium-Level Instanzen und anstatt nur 2, relativ synchronen Methoden-Aufrufe, mehrere asynchrone Aufrufe, vielleicht sogar Threads.

    Code (Java):
    1. package main;
    2.  
    3. import handler.RegisterHandler;
    4. import handler.register.RegisterID;
    5. import main.test.Tester;
    6. import main.test.Tester2;
    7.  
    8.  
    9. public class Main {
    10.     public static void main(String[] args) {
    11.  
    12.         RegisterHandler registerHandler = RegisterHandler.getInstance();
    13.  
    14.         RegisterID id = registerHandler.pullNewRegister();
    15.  
    16.         RegisterHandler.getInstance().bindRegister("1" , id);
    17.  
    18.         Tester tester = new Tester();
    19.         Tester2 tester2 = new Tester2();
    20.  
    21.         int longer = 0;
    22.  
    23.         while(longer < 4) {
    24.  
    25.             tester.runner();
    26.             tester2.runner();
    27.  
    28.             longer++;
    29.         }
    30.  
    31.  
    32.     }
    33. }
    Die Main Klasse erstellt zu erst eine Instanz des RegisterHandler's. "Zieht" dann ein neues Register und weist ihm den legeren Namen "1" (ja, ich weiß, ich habe auch schon legerere Namen gesehen..).
    Dann erstellen wir uns Tester und Tester2 (die beiden kommen gleich). Dann führen wir 4 mal hintereinander die runner Methode der beiden aus.
    Jetzt zu den Testern! Ich zeige hier nur eine Klasse, aber keine Sorge, die Klasse Tester2 ist 1:1 die selbe Klasse, nur heißt sie Tester2.
    Code (Java):
    1. package main.test;
    2.  
    3. import handler.RegisterHandler;
    4. import handler.register.Register;
    5.  
    6. public class Tester {
    7.  
    8.     private Register register;
    9.  
    10.     public Tester() {
    11.         this.register = RegisterHandler.getInstance().getRegisterForId("1");
    12.     }
    13.  
    14.     public void run() {
    15.         C c = register.fetchAndGetModuleFromPipe(C.class.getName());
    16.         c.howMuch();
    17.         c.higher();
    18.         register.pushClassToRegister(C.class.getName() , c);
    19.     }
    20.  
    21. }
    Und hier noch eine Klasse C, die wir brauchen:
    Code (Java):
    1. package main.test;
    2.  
    3. import annotations.RegisterModul;
    4.  
    5. @RegisterModul
    6. public class C {
    7.  
    8.     private int counter;
    9.  
    10.     public C() {
    11.         counter = 1;
    12.     }
    13.  
    14.     public void higher() {
    15.         counter++;
    16.     }
    17.  
    18.     public void howMuch() {
    19.         System.out.println("Ich zähle " + counter);
    20.     }
    21.  
    22. }
    Also, was machen wir hier? Im Konstruktor Holen wir uns ein Register aus dem RegisterHandler. Nach der legeren ID "1", in der Wir vorhin das andere Register gespeichert haben (C kommt gleich noch).
    In der Methode Run haben wir einen sehr einfachen Algorithmus:
    Wir holen (fetchen, also überschreiben das gespeicherte Objekt nicht) uns die Klasse C aus dem Register -> Wir fragen C wie hoch sein Counter ist -> Wir erhöhen C -> Wir speichern (pushen) C in das Register unter dem Namen von C
    Normal müsste die Ausgabe jetzt so aussehen:

    Ich zähle 1
    Ich zähle 1
    Ich zähle 2
    Ich zähle 2
    Ich zähle 3
    Ich zähle 3
    Ich zähle 4
    Ich zähle 4

    Aber! Weil wir das Objekt immer aus dem Register Pulen und wieder rein Pushen, weiß Tester2 was Tester gemacht hat und anders rum.
    Ohne dass die Klassen Instanzen von C übergeben, haben sie die aktuelle Instanz von C, obwohl C keine einzige / s static Methode / Atribut besitzt.
    Deswegen sieht die Ausgabe so aus:

    Ich zähle 1
    Ich zähle 2
    Ich zähle 3
    Ich zähle 4
    Ich zähle 5
    Ich zähle 6
    Ich zähle 7
    Ich zähle 8


    etwas feedback wäre cool :)

    Als nächste Erweiterung kommt ExceptionHandling, sowie abfragen, ob RegisterID's bereits existieren usw. Ohne diese Abfragen bekommt man nullpointer exceptions. Nicht immer schön...

    Das Fehlt noch:
    - (done) Register auf einen bestimmten Namen binden, dass auch Prozesse, die sich auf einen Namen geeinigt haben, dieses Register nutzen können. (done)
    - Doku (wie immer ... ).
    - Register-Templates. Das man sagen kann, erstelle ein neues register und pull gleich dieses und dieses Modul aus der Pipe.
    - Überdenken, ob der Klassenname das beste ist, ob Klassen in der OutputPipe zu identifizieren.
    - Konfiguration einlesen und nutzen.
    - Die Instantiierung der Klassen nicht von dem Constructor abhängig machen.

    Anmerkungen? Erweiterungswünsche? Fragen?

    Wie immer: Mit besten Wünschen!
     
    Zuletzt bearbeitet: 20. Mai 2016
  10. StormCraftable

    StormCraftable Grünschnabel

    Jetzt wirkt es wirklich wie allein-Unterhaltung :D
     
  11. StormCraftable

    StormCraftable Grünschnabel

  12. StormCraftable

    StormCraftable Grünschnabel

    Ich habe ein tatsächlich hartnäckiges Problem. Und zwar muss ich Objekte "clonen" und das zur Laufzeit, ohne Copy-Konstruktor.
    Das Problem manifestiert sich wie folgt:
    Ich nutze hier das selbe Beispiel, wie vorher auch.
    Code (Java):
    1.         RegisterHandler registerHandler = RegisterHandler.getInstance();
    2.         RegisterHandler.setScanRootPackage("test");
    3.  
    4.         RegisterID id = registerHandler.pullNewRegister();
    5.  
    6.         RegisterHandler.getInstance().bindRegister("1" , id);
    7.  
    8.         Tester tester = registerHandler.getRegisterForId(id).fetchAndGetModuleFromPipe(Tester.class.getName());
    9.         Tester2 tester2 = registerHandler.getRegisterForId(id).fetchAndGetModuleFromPipe(Tester2.class.getName());
    10.  
    11.         C c = RegisterHandler.getInstance().getRegisterForId("1").pullAndGetModuleFromPipe(C.class.getName());
    12.  
    13.         RegisterHandler.getInstance().getRegisterForId("1").pushModuleToRegister("c", c);
    14.  
    15.         c.higher();
    16.  
    17.         int longer = 0;
    18.  
    19.         while(longer < 4) {
    20.  
    21.             tester.run();
    22.             tester2.run();
    23.  
    24.             longer++;
    25.         }
    Meiner Idee nach, dürfte die Zeile:
    Code (Java):
    1. c.higher();
    keine Auswirkung auf die beiden Tester haben, da ich nachdem ich c.higher() aufrufe, das neue Objekt nicht mehr pushe.
    Ich habe das auf die Methodik zurück verfolgt, nach der wir die Module (Objekte) aus den Registern bekommen. Da wir in jedem Fall das Gleiche Objekt zurück geben, denkt der Compiler bei jedem Aufruf eines Objektes, das wir so bekommen haben, es sei das Selbe und spreche die gleichen Physikalischen Register an. Das soll nicht sein. Deswegen meine Frage:
    Ist es möglich, ein Objekt zur Laufzeit zu Clonen, ohne einen Copy-Konstruktor zu besitzen?
    Das Problem ist, dass die .clone() Methode von Objekten protected ist, ich aber auf keinen Fall die Klasse Klonen kann, da ich ansonsten den ganzen Sinn des RegisterHandlers negiere.
    Ist das möglich?
    Bitte um Rückmeldung!
     
  13. StormCraftable

    StormCraftable Grünschnabel

    Abermals habe ich auch das Problem gelöst. Ich habe einfach folgende Methode zu dem Register hinzugefügt:
    Code (Java):
    1. public synchronized  Object cloneObject(Object object) {
    2.   try{
    3.   Object clone = object.getClass().newInstance();
    4.   for (Field field : object.getClass().getDeclaredFields()) {
    5.   field.setAccessible(true);
    6.   if(field.get(object) == null || Modifier.isFinal(field.getModifiers())){
    7.   continue;
    8.   }
    9.   if(field.getType().isPrimitive() || field.getType().equals(String.class)
    10.   || field.getType().getSuperclass().equals(Number.class)
    11.   || field.getType().equals(Boolean.class)){
    12.   field.set(clone, field.get(object));
    13.   }else{
    14.   Object childObj = field.get(object);
    15.   if(childObj == object){
    16.   field.set(clone, clone);
    17.   }else{
    18.   field.set(clone, cloneObject(field.get(object)));
    19.   }
    20.   }
    21.   }
    22.   return clone;
    23.   }catch(Exception e){
    24.   return null;
    25.   }
    26.   }
    Jetzt returned die Methode "pullModule" lediglich cloneObject(moduleContainerList.get(className));
    Leute, jetzt mal wirklich, interresiert das hier keinen? Ist das alles kacke? Ist das eigentlich schon gelöst und deswegen antwortet keiner?
    Ich meine, selbst wenn das alles kacke ist, könntet ihr mir wenigstens das als Feedback geben!
     
  14. StormCraftable

    StormCraftable Grünschnabel

    Ich setzte jetzt ne Dead-line. Wenn hier innerhalb der nächsten, keine Ahnung, Woche kein Feedback bekomme, gehe ich davon aus, dass das Thema "gelöst" und damit geschlossen ist.
     
  15. Improof

    Improof Erfahrenes Mitglied

    Hi StormCraftable,

    bitte nicht aufhören, ich finds super spannend, vor allem wie sichs so entwickelt :)
    Ich bin selber leider bisher nicht mit Spring o.ä. in Verbindung geraten, daher kann ich dir leider auch nicht sagen, inwieweit du etwas realisierst, das es evtl. schon gibt.

    Und dass sonst keiner was dazu sagt liegt wahrscheinlich daran, dass du ein sehr spezielles Thema behandelst und nichts ganz allgemeines. Das macht es aber nicht weniger interessant ;)

    Viele Grüße
    Daniel
     
  16. StormCraftable

    StormCraftable Grünschnabel

    Okay. Also, für jeden den Spring nichts sagt, Spring ist ein Framework für "Dependency Injection". Das ist kein böses oder gar ausgedachtes Wort, o.ä. sondern ein ziemlich nützliches Mittel um Zugehörigkeiten vom "Master-Slave-Prinzip" unter Kontrolle zu haben. Google hilft da sehr weiter.
    Das Framework hier, das "Register-Handler Framework" (RHF) vereint mehrere Prinzipien. Zum einen Dependency Injection und "Singleton-Pattern" ohne die direkte Nutzung vom Singleton-Pattern und etwas, dass ich das Register-Pattern nenne. Die Idee, dass Instanzen zu Registern gehören und nicht zu Prozeduren. Das sorgt für kontrollierte Instanz-Zugehörigkeiten ohne dass Prozeduren Instanzen teilen müssen.

    Vorteile, die mir so schnell einfallen wären:
    - leichteres Debugging
    - Erhöhte Wiederverwendbarkeit in Modular aufgebauten Programmen.
    Nachteile:
    - Mehr Code und kompliziertere Instantiierungen

    Außerdem ist es super für das MVP(Model-View-Presenter)-Pattern. Die Idee von MVP ist es, die konkrete "Sicht" und die Daten für die Sicht zu trennen und durch "Presenter" zu vereinen. Der View kennt den Presenter und der Presenter kennt die Model. Auch hier hilft Google.
    Das RHF ist dabei ein vollwertiger Presenter. Mehrere View-Instanzen können nämlich hier gleiche Instanzen von Objekten nutzen, ohne dass sie diese Übergeben müssen. Wir machen also konkreten Gebrauch von dem Presenter. Ein konkretes Beispiel wäre hier sowas wie:
    Fenster (Gui, also der View) fragt RegisterHandler (den Presenter) nach Nutzernamen (den Model). Der Holt sich aus dem Register das Model aller Nutzernamen und kann diese an mehrere Fenster geben. Auch wenn sich hier etwas verändert, kann das ganz einfach jeder View erneut übernehmen, bzw muss es nicht einmal, nutzt er das entsprechende (also ein bestimmtes) Register nutzt.

    Ist das so verständlicher? Oder habe ich jetzt noch mehr komische Begriffe in den Raum geworfen, was mehr Verwirrung stiftet als es aufklärt?
    Etwas schnelleres Feedback wäre wirklich cool..
     
    Improof und Bratkartoffel gefällt das.
  17. Bratkartoffel

    Bratkartoffel gebratene Kartoffel Premium-User

    Hi,

    von mir aus kannst du hier gerne weiterbauen, bzw. uns auf dem Laufenden halten wo du stehst, was das Ding inzwischen kann oder wo du Probleme hast. Ich verfolge den Thread (bisher passiv) weil ich das Thema interessant und deinen Ansatz gut finde. Deine Einführung gerade eben in das Thema fand ich sehr gut, danke hierfür. Mein erster Gedanke bei dem Thema war eh "Der baut Spring nach", erst langsam habe ich erkannt dass dem nicht so ist. Dein letzter Post hat hier auch sehr gut geholfen, gut dass hier jemand spring erwähnt hat.

    Zu einer deiner Fragen weiter oben:
    Ist es möglich, ein Objekt zur Laufzeit zu Clonen, ohne einen Copy-Konstruktor zu besitzen?

    Ja, ich würde hier aber nicht per Reflection die Daten kopieren. In deinem Fall wird der Konstruktur der neuen Klasse durchlaufen, was u. U. zu unerwarteten Verhalten, Exceptions oder sonst was führen kann. Da du ein Framework zur Verfügung stellst weisst du ja nicht, was die anderen damit machen wollen. Ich würde hier ganz dreckig auf sun.misc.Unsafe setzen: http://howtodoinjava.com/core-java/related-concepts/usage-of-class-sun-misc-unsafe/

    Grüsse,
    BK
     
  18. Improof

    Improof Erfahrenes Mitglied

    Hi,

    erstmal danke für die super Erklärung!

    Weil @Bratkartoffel es jetzt nochmal erwähnt hat, ich hab mir für deep copies mal eine Methode geschrieben, die mit Streams arbeitet...ist nur ewig her. Zur Performance im Vergleich zu der anderen Methode und ob das jetzt eher quick and dirty ist kann ich nichts sagen, evtl. ist sun.misc.Unsafe um einiges besser, kannte ich aber bisher nicht.

    Ich schreib dir mal in etwa, wie der Code damals aussah:

    Code (Java):
    1. public <T> T cloneObject(T object, Class<T> type) throws Exception {
    2.     ObjectOutputStream oos = new ObjectOutputStream(baos);
    3.     oos.writeObject(object);
    4.     ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
    5.     ObjectInputStream ois = new ObjectInputStream(bais);
    6.     return type.cast(ois.readObject());
    7. }
    Die Objekte müssen dann aber Serializable implementieren!

    Viele Grüße
    Daniel
     
  19. StormCraftable

    StormCraftable Grünschnabel

    Sorry das sich mich länger nicht gemeldet habe. Ich habe grade etwas Stress in der Uni..

    @Improof
    Also ich kann nicht erwarten, dass jede Klasse Serializable implementiert. Außer es gibt ne Reflection Möglichkeit das zur Laufzeit zu implementieren, was Probleme verursachen könnte.

    @Bratkartoffel
    Ich habe mir das Unsafe mal genauer angeschaut. Ich weiß nur nicht in wie weit das helfen kann.
    Das Problem ist, dass ich sowieso davon ausgehe, dass die Konstruktoren vernünftig aufrufbar sind. Ansonsten könnten die Klassen nicht in der DataOutputPipe instantiiert werden.
     
  20. StormCraftable

    StormCraftable Grünschnabel

    Nach laaaaanger Zeit melde ich mich mal wieder.
    Leider bin ich, auf Grund eines Universitäts-Projekt, nicht dazu gekommen, weiter zu machen.. Aber hey! Ich habe ein github-repository erstellt! Ihr findet es hier https://github.com/ThorbenKuck/RegisterHandlerFW.
     
Die Seite wird geladen...