RAII und Java

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

RAII und Java

Beitrag von Chromanoid »

Abgetrennt von http://zfx.info/viewtopic.php?f=9&t=307 ... 935#p30577

Ich habe jedenfalls nicht das Gefühl, dass Java Entwickler so viel über Sprachdetails und undefiniertes Verhalten zu meckern haben :D. Außerdem sind doch try-with-resources und multi-catch in Java 7 schon mal ein guter Anfang!

Ich bin dafür, dass Speichermanagement automatisch passiert, sei es nun durch statische Analyse oder einen GC, Hauptsache ich muss mich nicht drum kümmern.
Zuletzt geändert von Chromanoid am 25.05.2012, 13:05, insgesamt 1-mal geändert.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Chromanoid hat geschrieben:Ich bin dafür, dass Speichermanagement automatisch passiert, sei es nun durch statische Analyse oder einen GC, Hauptsache ich muss mich nicht drum kümmern.
Ich bin dafür, dass Ressourcenmanagement automatisch und verlässlich passiert, womit ein GC ausscheidet. Javas neuer Syntax Sugar ist wieder mal ein Witz. Ich sehe zum Beispiel keinen Referenztyp für Ressourcen, was effektiv heißt, dass ...
  • ... Exceptions im Konstruktor nach wie vor ein großes Problem bleiben,
    (Initialisiere Ressourcen mit ARM: Ressourcen gleich wieder geschlossen nach Konstruktor. Initialisiere Ressourcen ohne ARM: Ressourcen leaken oder klassisches redundantes try-catch.)
  • ... am Ende alle Klassen, die auf irgendeiner Ebene Ressourcen enthalten, transitiv zu Fuß mit noch mehr Redundanz close()-Methoden implementieren müssen,
    (Ja, das ist noch schlimmer, als transitiv geprüfte Ausnahmen hochzureichen, weil es eigentlich keine Möglichkeit gibt, diese Kette zu unterbrechen.)
  • ... geteilte Ressourcen nach wie vor frei und unsicher im GC-Raum schweben.
Wieso werde ich das Gefühl nicht los, dass sie keins ihrer neuen Features je überhaupt verstanden haben. Um die an unserer Uni gerne dargebotene Kernaussage zu Java zu negieren: Java enthält eigentlich nichts Neues, es kombiniert nur längst dagewesene Konzepte in besonders ungeschickter Weise.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Naja, das hat ja alles seinen Grund. Mal eben so eine Möglichkeit einführen den GC zu umgehen ist keine gute Lösung. Mit der neuen try-with-resources Syntax bleibt das ganze wenigstens beisammen und man vertut sich weniger. Und es ist ja jetzt nicht so, dass das als RAII-Lösung vorgestellt wurde, sondern man findet es in einem Sammel-JSR "Small Enhancements to the Java Programming Language"...

Gibt es das gleiche Problem nicht auch in C++ nur eben nicht für RAII-konforme Ressourcen, sondern bei ganz normalen Objekten die man auf dem Heap allokiert?
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Chromanoid hat geschrieben:Naja, das hat ja alles seinen Grund. Mal eben so eine Möglichkeit einführen den GC zu umgehen ist keine gute Lösung. Mit der neuen try-with-resources Syntax bleibt das ganze wenigstens beisammen und man vertut sich weniger.
Nein, der GC wird in keinem Fall umgangen (bzw. wenn, dann ist das ein Implementierungsdetail). Es geht um den automatischen Aufruf der close()-Methode, und dieses Konzept haben sie mal wieder gründlich verkackt, siehe Post obendrüber.
Chromanoid hat geschrieben:Gibt es das gleiche Problem nicht auch in C++ nur eben nicht für RAII-konforme Ressourcen, sondern bei ganz normalen Objekten die man auf dem Heap allokiert?
Es gibt keine RAII-konformen Ressourcen. RAII ist ein einfaches Konzept, das sich auf jede Ressource anwenden lässt, und keine der genannten Lücken aufweist.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

CodingCat hat geschrieben:
Chromanoid hat geschrieben:Naja, das hat ja alles seinen Grund. Mal eben so eine Möglichkeit einführen den GC zu umgehen ist keine gute Lösung. Mit der neuen try-with-resources Syntax bleibt das ganze wenigstens beisammen und man vertut sich weniger.
Nein, der GC wird in keinem Fall umgangen (bzw. wenn, dann ist das ein Implementierungsdetail). Es geht um den automatischen Aufruf der close()-Methode, und dieses Konzept haben sie mal wieder gründlich verkackt, siehe Post obendrüber.
Der GC müsste umgangen werden, wenn man RAII vernünftig in Java einbauen will oder etwa nicht? Java hat ja Finalizer, deren Ausführung ist dank des GC allerdings unbestimmt, was für die Implementierung des RAII-Konzeptes unglücklich ist. Mit einem GC lässt sich sowas wie RAII einfach nicht realisieren oder sehe ich das falsch?
CodingCat hat geschrieben:
Chromanoid hat geschrieben:Gibt es das gleiche Problem nicht auch in C++ nur eben nicht für RAII-konforme Ressourcen, sondern bei ganz normalen Objekten die man auf dem Heap allokiert?
Es gibt keine RAII-konformen Ressourcen. RAII ist ein einfaches Konzept, das sich auf jede Ressource anwenden lässt, und keine der genannten Lücken aufweist.
Wenn man die RAII Konzepte nicht befolgt hat man RAII-unkonforme Resourcen oder nicht? Es würde doch reichen, wenn ich Java-Klassen in C++ nachbaue und mit einem close versehe und keinen Desktruktor implementiere und schon habe ich eine Resource, die nicht RAII-konform funktioniert?!
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Chromanoid hat geschrieben:Der GC müsste umgangen werden, wenn man RAII vernünftig in Java einbauen will oder etwa nicht? Java hat ja Finalizer, deren Ausführung ist dank des GC allerdings unbestimmt, was für die Implementierung des RAII-Konzeptes unglücklich ist. Mit einem GC lässt sich sowas wie RAII einfach nicht realisieren oder sehe ich das falsch?
Ja, das siehst du falsch. Das Konzept ist orthogonal zum GC. Es müsste nur close() entsprechender Unterobjekte aufgerufen werden, wenn die close()-Methode des Elternobjekts aufgerufen wird, oder bei dessen Konstruktion eine Ausnahme fliegt.
CodingCat hat geschrieben:Wenn man die RAII Konzepte nicht befolgt hat man RAII-unkonforme Resourcen oder nicht? Es würde doch reichen, wenn ich Java-Klassen in C++ nachbaue und mit einem close versehe und keinen Desktruktor implementiere und schon habe ich eine Resource, die nicht RAII-konform funktioniert?!
Es geht nicht darum, wie es ist, wenn man es falsch macht. Es geht darum, ob es sinnvoll richtig umsetzbar ist. Wenn eine Ressource nicht durch RAII verwaltet wird, dann schreibst du eine Klasse, die diese Ressource durch RAII verwaltet. Wie die Leute mit den Features umgehen, ist nicht Javas Problem, das Problem ist, dass diese Features überhaupt nicht existieren, und so verlässliche automatische Ressourcenverwaltung und Fehlerbehandlung weiterhin unmöglich bleiben.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

CodingCat hat geschrieben:
Chromanoid hat geschrieben:Der GC müsste umgangen werden, wenn man RAII vernünftig in Java einbauen will oder etwa nicht? Java hat ja Finalizer, deren Ausführung ist dank des GC allerdings unbestimmt, was für die Implementierung des RAII-Konzeptes unglücklich ist. Mit einem GC lässt sich sowas wie RAII einfach nicht realisieren oder sehe ich das falsch?
Ja, das siehst du falsch. Das Konzept ist orthogonal zum GC. Es müsste nur close() entsprechender Unterobjekte aufgerufen werden, wenn die close()-Methode des Elternobjekts aufgerufen wird, oder bei dessen Konstruktion eine Ausnahme fliegt.
Jetzt verstehe ich was du meinst. Sollten weitere Ressourcen von einer "Oberressource" benötigt werden, hat man das Problem, dass diese dann wieder ohne ARM abgearbeitet werden müssen. Das ist natürlich ein gewisser Nachteil, aber man könnte auch einfach alle benötigten Ressourcen in der entsprechenden Reihenfolge im Konstruktor übergeben oder mit wenig Arbeit die entsprechenden close-Aufrufe der Subresourcen via ByteCode-Injection oder Annotation-Processor in die close-Methode der Oberresource einbauen. RAII ist das dann aber immer noch nicht, da die Ressourcen-Lebenszeit vom try-with-resources-Block bestimmt wird und nicht vom Scope. Und genau letzteres ist in Java aufgrund des GC nicht möglich. Man bräuchte also ein Flag in Java, das sicherstellt, dass ein Objekt bei Verlassen des Scopes finalisiert wird und dann würde man den GC umgehen. Bei C++ funktioniert RAII doch nur, weil es möglich ist Objekte nach Verlassen des Scopes automatisch abräumen zu lassen.

Wie du schon gesagt hast, handelt es sich bei try-with-resources um Programmierhilfen und nicht um die Ermöglichung eines für Java neuen Konzepts, das wurde auch nie anders kommuniziert. Laut Wikipedia wurde in C genau so ein Flag, wie ich es erwähnt habe, per GCC Erweiterung eingebaut. So eins bräuchte man für Java, um anständig RAII anwenden zu können.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Chromanoid hat geschrieben:[...] wenn ich Java-Klassen in C++ nachbaue und mit einem close versehe und keinen Desktruktor implementiere und schon habe ich eine Resource, die nicht RAII-konform funktioniert?!
Dann hast du den Fehler begangen, in C++ Java zu programmieren ;)
Chromanoid hat geschrieben:Bei C++ funktioniert RAII doch nur, weil es möglich ist Objekte nach Verlassen des Scopes automatisch abräumen zu lassen.
Das "nur" würde ich weglassen. Genau das ist es ja. Scope ist kein lästiger Feind der mit allen Mitteln bekämpft werden muss. Scope ist einer der mächtigsten Verbündeten die ein Programmierer sich überhaupt nur wünschen kann.

Am Planeten Java ist unser guter Freund der Scope jedoch in den vielen sinnlosen Kriegen gegen die Speicherlöcher verkrüppelt und wahnsinnig geworden. Erst als die Speicherlöcher besiegt waren, wurde Java klar, dass diese nur ein Stamm vom Volk der Ressourcen waren. Durch den Krieg hat sich nun das ganze intergalaktische Volk der Ressourcen gegen Java verbündet. Und der Scope ist schwach und des Kampfes müde. Wird Java den bevorstehenden Konflikt überstehen?
Garbage Collection: Retribution. Jetzt im Kino!
Zuletzt geändert von dot am 25.05.2012, 01:44, insgesamt 5-mal geändert.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

dot hat geschrieben:
Chromanoid hat geschrieben:Bei C++ funktioniert RAII doch nur, weil es möglich ist Objekte nach Verlassen des Scopes automatisch abräumen zu lassen.
Das "nur" würde ich weglassen. Genau das ist es ja. Scope ist kein lästiger Feind der mit allen Mitteln bekämpft werden muss. Scope ist einer der mächtigsten Verbündeten die ein Programmierer sich überhaupt nur wünschen kann.
Ja, ich hätte nichts gegen so ein keyword, aber es ist doch klar, dass sowas nicht mal ebenso eingebaut werden kann. Um mal wieder darauf zurück zu kommen, warum bei mir Unverständnis aufgekommen ist:
CodingCat hat geschrieben:
Chromanoid hat geschrieben:Naja, das hat ja alles seinen Grund. Mal eben so eine Möglichkeit einführen den GC zu umgehen ist keine gute Lösung. Mit der neuen try-with-resources Syntax bleibt das ganze wenigstens beisammen und man vertut sich weniger.
Nein, der GC wird in keinem Fall umgangen (bzw. wenn, dann ist das ein Implementierungsdetail). Es geht um den automatischen Aufruf der close()-Methode, und dieses Konzept haben sie mal wieder gründlich verkackt, siehe Post obendrüber.
Ich finde im Rahmen, dessen was man ohne VM/GC-Erweiterung einbauen kann, ist try-with-resources so wie es jetzt funktioniert, das einzige was man hätte machen können.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Genau darum geht's ja: Garbage Collection ist Chemotherapie, RAII ein gesunder Lebensstil ;)

Cat und mich stimmt es traurig wenn die Chemotherapie als dem gesunden Lebensstil überlegene Alternative angepriesen wird...
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Gut das ist verständlich, wenn man ohne das Konzept nur ungern arbeitet.

Verliert man auf der anderen Seite mit RAII nicht ziemlich viel Flexibilität was das Rumreichen von Daten angeht? Sicher kann das wahllose Rumreichen gefährlich sein, aber es sorgt für einfache und schnelle Programmierarbeit.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Chromanoid hat geschrieben:Verliert man auf der anderen Seite mit RAII nicht ziemlich viel Flexibilität was das Rumreichen von Daten angeht?
Nein, wieso sollte man?
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Naja man kann den Scope von gescopten Objekten in C++ doch nicht einfach so ändern? Das ist ja das was man durch den GC gewinnt, das Ändern des Scopes von Objekten ohne sich um das Löschen kümmern zu müssen.

Sowas wie
AbstractMessage x=server.retrieveMessage();
pipeline.processMessage(x);
ist doch in C++ nur möglich wenn man am Ende noch x zerstört oder etwa nicht? Dieses Beispiel kann man also nicht via RAII implementieren, oder?
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Doch, dank move Semantik funktioniert das völlig natürlich und problemlos. Ich muss den Scope der RAII Objekte nicht ändern. Ich kann einfach die Ownership von einem RAII Objekt zu einem anderen übertragen ;)
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Kann man das denn auch machen, wenn man gar nicht weiß um welchen Objekttyp es sich genau handelt, bzw. der Objekttyp aus einer unbekannten dynamisch gelinkten Bibliothek kommt? Wenn ja, dann bin ich begeistert und halte meinen Mund :D
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Bin mir nicht sicher was genau du meinst, aber natürlich funktioniert das mit abstrakten Typen. Und C++ kann den genauen Typ natürlich auch automatisch deduzieren:

Code: Alles auswählen

auto x = server.retrieveMessage();  // returned z.B. einen std::unique_ptr<AbstractMessage>
pipeline.processMessage(x.get());
Oder falls der Besitz der Message in die Funktion wandern soll:

Code: Alles auswählen

auto x = server.retrieveMessage(); 
pipeline.processMessage(move(x));
bzw. einfach

Code: Alles auswählen

pipeline.processMessage(server.retrieveMessage());
In keinem Fall wird hier jemals etwas leaken. Ja der Benutzer des server Objektes muss selbst nichtmal genau wissen mit was er da eigentlich hantiert.
Zuletzt geändert von dot am 25.05.2012, 02:30, insgesamt 1-mal geändert.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Naja sagen wir mal so, es gibt eine Bibliothek die mir die Pipeline bereitstellt. Nun baue ich einen eigenen Server und ein eigenes Message-Objekt, das von AbstractMessage erbt und füge ein paar Felder hinzu. Dann baue ich noch einen kleinen Processor, den ich in die bereits bestehende Pipeline einhänge. Mein eigener Server gibt jetzt ab und zu mal mein individuelles Message-Objekt in die Pipeline und manchmal eben ein anderes. Für diesen Zweck brauche ich natürlich Reflexion oder eine Typ-ID, da die einzelnen Prozessoren ja gar nicht wissen können, was für schöne Nachrichten da so alles eintrudeln und welche überhaupt von ihnen verarbeitet werden können.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Imo hat Reflection (außer vielleicht compile-time Reflection) in einer statisch typisierten Sprache nichts verloren. Wenn du in so einer Sprache meinst Reflection zu brauchen, ist in aller Regel dein Design kaputt. Zumindest meiner Erfahrung nach und imo ganz besonders in diesem Fall: Deine Pipeline ist darauf ausgelegt, AbstractMessages zu verarbeiten. Das Interface der AbstractMessage ist für deine Anwendung allerdings offenbar nicht ausreichend. Das bedeutet entweder, dass die Abstraktion der AbstractMessage oder die Struktur der Pipeline unpassend ist. In anderen Worten: In deinem Beispiel ist diese Library also für deine Anwendung offenbar ungeeignet. Das Beispiel ist allerdings nicht konkret genug als dass ich da jetzt eine alternative Lösung vorschlagen könnte, da diese wohl völlig und grundlegend anders aussehen würde. Man müsste eben das Design der Library fixen, bzw. sich ein andere, passende Library suchen/bauen. Wieso müssen die verschiedenen Messages durch die selbe Pipeline? Wie entscheidet die Pipeline was zu welchem Prozessor geht? Wieso muss die Message überhaupt abstract sein? Was genau soll die Pipeline verarbeiten? Was ist die Aufgabe so eines Processors?
Zuletzt geändert von dot am 25.05.2012, 02:49, insgesamt 1-mal geändert.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Naja sowas gibt es in Java Bibliotheken gar nicht so selten:

Code: Alles auswählen

class NormalProcessor implements Processor{
    void process(Object x) {
        if(x instanceof String)
             write(x);
        else if(x instanceof ConcreteMessageA)
             write((ConcreteMessageA)x).getTextA());
        else if(x instanceof ConcreteMessageB)
             write((ConcreteMessageB)x).getTextB());
        else if(x instanceof AbstractMessage)
             write((AbstractMessage)x).getText());
    }
}

class MyProcessor extends NormalProcessor {
    void process(Object x) {
        if(x instanceof ConcreteMessageC)
             write((ConcreteMessageC)x).getTextC());
        else super.process(x);
    }
}
Diese Art des Programmierens erlaubt sehr lose Koppelung und hohe Erweiterbarkeit. das meine ich mit Flexibilität...
Zuletzt geändert von Chromanoid am 25.05.2012, 02:52, insgesamt 1-mal geändert.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Nein, so programmieren Leute die OOP nicht verstanden haben, sry. Das ist genau das Gegenteil von loser Kopplung. Denn NormalProcessor ist nun plötzlich von ConcreteMessageA, ConcreteMessageB, AbstractMessage und Object zusammen abhängig. Das ganze Codebeispiel schreit einfach nur Designfehler aus jeder Zeile.

Ich weiß, dass in der Java Welt sowas ständig irgendwer macht. Aber nur weil viele etwas machen, bedeutet das noch lange nicht, dass es gut ist. Jemand der das liskovsche Subtitutionsprinzip mit Fackeln und Mistgabeln aus seinem Dorf jagt, anstatt vielleicht mal darüber nachzudenken, ob er nicht doch tatsächlich gerade dabei ist, etwas ganz ganz grundlegend falsch zu machen, ist imo jedenfalls kein guter Programmierer, sondern einfach nur ignorant.

Leider erlaubt dir C++ auch, sowas zu machen. typeid und dynamic_cast sind ganz oben auf der Liste der Dinge, die ich nicht vermissen würde, im Gegenteil...
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

Verstehe das Problem nicht. Jeder kann so seine eigenen Objekte durch eine Pipeline jagen und je nach gusto bestimmte Processoren und Nachrichten implementieren. Die Kopplung zwischen der Schnittstelle Processor und möglichen Nachrichten ist praktisch nicht vorhanden. Lediglich die Implementierungen der Processor-Schnittstelle müssen die Klassen, die sie verarbeiten kennen. Typabhängiges Verhalten ist in diesem Fall ja gerade erwünscht. Es wird ein Datenstrom an Objekten verarbeitet, wobei man gar nicht wissen kann in welcher Form andere diese Objekte verarbeiten wollen. Ich halte das für völlig normale Konstrukte in einer Sprache mit vernünftiger Reflexion usw.

Man könnte sich dann folgende Bibliotheken vorstellen, die unabhängig von einander von verschiedenen Parteien implementiert werden können:

Code: Alles auswählen

//Schnittstellen-Bibliothek:
interface Processor  {
    Object process(Object message);
}

//Referenz-Implementierung-Bibliothek:
class StringProcessor implements Processor {
    Object process(Object message){
        if(message instanceof String)
            print(message);
        return message;
    }
}
class Pipeline  implements Processor {
    Processor[] processors;
    Pipeline(Processor... processors){
        this.processors=processors;
    }
    Object process(Object message){
        Object result=message;
        for(p:processors){
            result=p.process(result);
        }
        return result;
    }
}

//Thirdparty-Bibliothek:
class MailServer...
class MailToStringProcessor implements Processor {
    void process(Object message){
        if(message instanceof Mail)
            return ((Mail)message).getText();
    }
}

//Meine-Bibliothek:
class MyMessageQueue...
class MyMessageToStringProcessor implements Processor {
    void process(Object message){
        if(message instanceof MyMessage)
            return ((MyMessage)message).getText();
    }
}
//...
Pipeline p=new Pipeline(new MyMessageToStringProcessor(),new MailToStringProcessor(),new StringProcessor());
p.process(mailServer.getMessage(...));
p.process(myMessageQueue.getMessage(...));
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Einfache Lösung für das erste Beispiel: write() zu einer Methode der AbstractMessage machen.
Einfache Lösung für das zweite Beispiel: Die Library soll einfach direkt Strings verarbeiten.
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

dot hat geschrieben:Einfache Lösung für das erste Beispiel: write() zu einer Methode der AbstractMessage machen. Dann brauchst du plötzlich überhaupt keinen Benutzerdefinierten Processor mehr.
write ist doch hier nur ein Aspekt des Prozessors. Da könnte noch stehen, dass bei einem bestimmten Nachrichten-Typ ein Wert aus der Nachricht in eine Datenbank gespeichert werden soll etc.
dot hat geschrieben:Einfache Lösung für das zweite Beispiel: Die Library soll einfach direkt Strings verarbeiten.
Wie gesagt, das Verhalten von Processor ist absichtlich undefiniert. Es sollen beliebige Eingabeströme in beliebiger Weise transformiert werden.

Genau diese sehr flexiblen Aspekte von Sprachen mit Reflexion und meist GC sind es, warum ich gerne mit diesen Sprachen entwickle und sicher auch warum sie so häufig im Einsatz sind. Alles was mit echter Erweiterbarkeit zu tun hat macht in C++ und Co. wenig Spaß. Ich erinnere mich noch an einige Abenteuer irgendwelche DLL-Plugins für verschiedene Anwendungen zu basteln, der "Spaß" wäre mit einer Anwendung auf Java Basis wirklich ein Spaß gewesen.
Benutzeravatar
dot
Establishment
Beiträge: 1734
Registriert: 06.03.2004, 18:10
Echter Name: Michael Kenzel
Kontaktdaten:

Re: Jammer-Thread

Beitrag von dot »

Chromanoid hat geschrieben:Wie gesagt, das Verhalten von Processor ist absichtlich undefiniert. Es sollen beliebige Eingabeströme in beliebiger Weise transformiert werden.
In dem Fall wäre die elegante Lösung wohl ein richtiger Filtergraph statt dieser starren Pipelinestruktur. Dann kommt man wieder ohne Reflection aus und hat z.B. den massiven Vorteil, dass man die einzelnen Prozessoren nicht versehentlich in der falschen Reihenfolge zusammenhängen kann. Da die Abhängigkeiten dabei explizit über das Typsystem ausgedrückt sind, ist es rein systematisch schon unmöglich, inkompatible Komponenten zu verbinden, denn das würde schon nichtmal kompilieren. Zusätzlich ist das resultierende Design wesentlich flexibler. So kann ich nun z.B. wahlweise verschiedene Eingabeströme für verschiedene Objekttypen haben oder einen Eingabestrom aus dem alles kommt. Verschiedene Ströme können auch überhaupt an verschiedene Punkte im Graph gehen usw.
Es ist wie auch mit dem Scope: Das Typsystem ist kein lästiger Feind den man mit aller Kraft bekämpfen und bei jeder Gelegenheit aushebeln muss. Das Typsystem ist ein sehr mächtiger, wenn nicht überhaupt der mächtigste Verbündete den ein Programmierer sich nur wünschen kann. Und Reflection ist, wenn man dem Typsystem sagt: Go to hell, I don't love you anymore...
Chromanoid hat geschrieben:Genau diese sehr flexiblen Aspekte von Sprachen mit Reflexion und meist GC sind es, warum ich gerne mit diesen Sprachen entwickle und sicher auch warum sie so häufig im Einsatz sind.
Meiner Erfahrung nach handelt es sich da normalerweise um vermeintliche Fexibilität die man sich mit tatsächlicher Flexibilität erkauft hat ;)
Chromanoid hat geschrieben:Ich erinnere mich noch an einige Abenteuer irgendwelche DLL-Plugins für verschiedene Anwendungen zu basteln, der "Spaß" wäre mit einer Anwendung auf Java Basis wirklich ein Spaß gewesen.
Ich behaupte nicht, dass es nicht auch viel schlechten C++ Code gibt ;)
Alexander Kornrumpf
Moderator
Beiträge: 2119
Registriert: 25.02.2009, 13:37

Re: Jammer-Thread

Beitrag von Alexander Kornrumpf »

Vorab: ich programmiere nowadays leider sehr selten und davon nie C++. Ich bin mir nach all den Jahren leider immer noch nicht sicher ob ich verstanden habe, was RAII eigentlich ist. Was jedoch ganz offenichtlich ist (und zwar gerade nach Meinung von euch Experten), ist das RAII die einzige Art ist C++ richtig zu benutzen. Damit unterscheidet sich die Flexiblität RAII nicht zu benutzen sehr deutlich von der Flexbilität etwa nach Belieben prozedural oder objektorientiert zu arbeiten. Oder anderen Freiheiten, die C++ einem einräumt. Es ist klar dass es eine narrensichere Programmiersprache nicht geben kann. Aber von dem einen Idiom abweichen zu können ohne das die Sprache mehr oder weniger in sich zusammenbricht halte ich für naja mindestens unglücklich.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Chromanoid hat geschrieben:Jetzt verstehe ich was du meinst. Sollten weitere Ressourcen von einer "Oberressource" benötigt werden, hat man das Problem, dass diese dann wieder ohne ARM abgearbeitet werden müssen. Das ist natürlich ein gewisser Nachteil, aber man könnte auch einfach alle benötigten Ressourcen in der entsprechenden Reihenfolge im Konstruktor übergeben oder mit wenig Arbeit die entsprechenden close-Aufrufe der Subresourcen via ByteCode-Injection oder Annotation-Processor in die close-Methode der Oberresource einbauen.
Genau sowas wäre Aufgabe des Compilers. Und nein, die Übergabe an den Konstruktor wäre sehr gefährlich, wenn das Objekt selbst im Anschluss an den umschließenden try-with-resources-Block des Erzeugers denselben überleben würde.
Chromanoid hat geschrieben:RAII ist das dann aber immer noch nicht, da die Ressourcen-Lebenszeit vom try-with-resources-Block bestimmt wird und nicht vom Scope. Und genau letzteres ist in Java aufgrund des GC nicht möglich. Man bräuchte also ein Flag in Java, das sicherstellt, dass ein Objekt bei Verlassen des Scopes finalisiert wird und dann würde man den GC umgehen. Bei C++ funktioniert RAII doch nur, weil es möglich ist Objekte nach Verlassen des Scopes automatisch abräumen zu lassen.
Nein, nochmal: Es geht nicht um Finalisierung, es geht um den Aufruf der close()-Methode. Dass sich Java für einen extra Scope-Block entschieden hat, ist auch erstmal gar nicht so wichtig, das ist ohne Move Semantics momentan leider der einzige Weg. Selbst mit diesem Konstrukt ist es schon problemlos möglich, selektiv Abläufe und/oder Ressourcen sicher an einen bestimmten Geltungsbereich zu binden. Was fehlt, sind gerade Move Semantics und ein sicherer transitiver Mechanismus.

Was jedoch ganz offenichtlich ist (und zwar gerade nach Meinung von euch Experten), ist das RAII die einzige Art ist C++ richtig zu benutzen. Damit unterscheidet sich die Flexiblität RAII nicht zu benutzen sehr deutlich von der Flexbilität etwa nach Belieben prozedural oder objektorientiert zu arbeiten. Oder anderen Freiheiten, die C++ einem einräumt. Es ist klar dass es eine narrensichere Programmiersprache nicht geben kann. Aber von dem einen Idiom abweichen zu können ohne das die Sprache mehr oder weniger in sich zusammenbricht halte ich für naja mindestens unglücklich.
Verstehe ich nicht, nach dieser Logik gibt es überhaupt keine Art, Java richtig zu benutzen, bzw. es gibt keine Möglichkeit, den Zustand "zusammengebrochen" in Java jemals zu verlassen.
Zuletzt geändert von CodingCat am 25.05.2012, 10:28, insgesamt 1-mal geändert.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Alexander Kornrumpf
Moderator
Beiträge: 2119
Registriert: 25.02.2009, 13:37

Re: Jammer-Thread

Beitrag von Alexander Kornrumpf »

Verstehe ich nicht, nach dieser Logik gibt es überhaupt keine Art, Java richtig zu benutzen, bzw. es gibt keine Möglichkeit, den Zustand "zusammengebrochen" in Java jemals zu verlassen.
Das widerspricht doch nicht dem was ich geschrieben habe, oder?

EDIT: Ich sehe gerade dass hier "das" und "dass" einen riesigen semantischen Unterschied machen würden, habe es aber zum Glück direkt richtig gemacht. Ich denke auch dass du mich eigentlich verstanden hast, und nur die Intention als "pro-Java" misdeutetest. Das war es nicht. Ich hätte eigentlich gerne ein C++ das RAII erzwingt.

EDIT^2: Ich hatte am Ende doch noch einen das/dass Fehler der in Cats Zitierung ja konserviert bleibt und daher hier bereinigt wurde.
Zuletzt geändert von Alexander Kornrumpf am 25.05.2012, 10:34, insgesamt 1-mal geändert.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Alexander Kornrumpf hat geschrieben:Ich denke auch dass du mich eigentlich verstanden hast, und nur die Intention als "pro-Java" misdeutetest. Das war es nicht. Ich hätte eigentlich gerne ein C++ das RAII erzwingt.
Sorry, ich hatte deine Aussage tatsächlich missverstanden. Diesen Sprung zu konstruktiver Kritik an C++ habe ich an dieser Stelle nicht sofort gepackt. ;)

Und ja, ich stimme dir voll und ganz zu, auch C++ hat da noch Verbesserungspotential.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Benutzeravatar
Chromanoid
Moderator
Beiträge: 4263
Registriert: 16.10.2002, 19:39
Echter Name: Christian Kulenkampff
Wohnort: Lüneburg

Re: Jammer-Thread

Beitrag von Chromanoid »

dot hat geschrieben:In dem Fall wäre die elegante Lösung wohl ein richtiger Filtergraph statt dieser starren Pipelinestruktur. Dann kommt man wieder ohne Reflection aus und hat z.B. den massiven Vorteil, dass man die einzelnen Prozessoren nicht versehentlich in der falschen Reihenfolge zusammenhängen kann. Da die Abhängigkeiten dabei explizit über das Typsystem ausgedrückt sind, ist es rein systematisch schon unmöglich, inkompatible Komponenten zu verbinden, denn das würde schon nichtmal kompilieren. Zusätzlich ist das resultierende Design wesentlich flexibler. So kann ich nun z.B. wahlweise verschiedene Eingabeströme für verschiedene Objekttypen haben oder einen Eingabestrom aus dem alles kommt. Verschiedene Ströme können auch überhaupt an verschiedene Punkte im Graph gehen usw.
Es ist wie auch mit dem Scope: Das Typsystem ist kein lästiger Feind den man mit aller Kraft bekämpfen und bei jeder Gelegenheit aushebeln muss. Das Typsystem ist ein sehr mächtiger, wenn nicht überhaupt der mächtigste Verbündete den ein Programmierer sich nur wünschen kann. Und Reflection ist, wenn man dem Typsystem sagt: Go to hell, I don't love you anymore...
Sicher sind Typsystem und Scope sehr mächtige Verbündete. Der Scope hat bei Java aber per Design weniger Auswirkung bzw. ist praktisch nicht nutzbar (nur mithilfe verschiedener Konstrukte, die im Grunde einen künstlichen Scope einführen). Reflexion ist eine andere Art mit Objekten und Typisierung umzugehen, es hat nichts damit zu tun diese nicht zu beachten oder zur Hölle zu schicken. Es ist klar, dass ihr nicht gerne Java programmiert, wenn ihr die Art des Programmierens für unzumutbar haltet. Man kann und sollte Java nicht wie C++ programmieren. Wer das versucht, der kann sich nicht auf andere Sprachen einlassen. Es gibt nicht wenige Sprachen bei denen es wie in Java zugeht und die kommen auch alle ohne RAII aus. Es gibt auch Sprachen, die gänzlich auf statische Typisierung verzichten, und auch diese Sprachen haben ihre Daseinsberechtigung.

Mit meinen Beispielen wollte ich versuchen Probleme zu konstruieren, bei denen RAII im Weg steht. Jedes Konzept hat seine Nachteile und die habe ich versucht herauszukitzeln. Wenn meine Beispiele nicht schmecken, dann würde ich darum bitten selbst welche zu konstruieren. Nicht damit ich sagen kann warum ich Java toller finde, sondern einfach um den Sachverhalt besser zu verstehen. Meine Vermutung ist immer noch, dass RAII in Punkto Erweiterbarkeit Probleme machen kann.
Benutzeravatar
CodingCat
Establishment
Beiträge: 1857
Registriert: 02.03.2009, 21:25
Wohnort: Student @ KIT
Kontaktdaten:

Re: Jammer-Thread

Beitrag von CodingCat »

Chromanoid hat geschrieben:Sicher sind Typsystem und Scope sehr mächtige Verbündete. Der Scope hat bei Java aber per Design weniger Auswirkung bzw. ist praktisch nicht nutzbar (nur mithilfe verschiedener Konstrukte, die im Grunde einen künstlichen Scope einführen).
Und genau das ist ärgerlich. Wieder hätten sie es schaffen können, dieses Problem ein für alle Mal zu beseitigen, und wieder sind sie kläglich gescheitert. Die Tatsache, dass sie es immer wieder versuchen, zeigt doch, dass der Geltungsbereich eigentlich wichtig genommen wird. Aber die Sprache wird eben genau wie dein Beispiel von oben entworfen. Kaskaden von Sonderfällen, statt ein funktionierender Ansatz.
Chromanoid hat geschrieben:Es ist klar, dass ihr nicht gerne Java programmiert, wenn ihr die Art des Programmierens für unzumutbar haltet. Man kann und sollte Java nicht wie C++ programmieren. Wer das versucht, der kann sich nicht auf andere Sprachen einlassen. Es gibt nicht wenige Sprachen bei denen es wie in Java zugeht und die kommen auch alle ohne RAII aus.
Das ist doch vollkommener Blödsinn. Auch in Java versucht man, ausnahmesicher zu programmieren. Der Unterschied ist, dass es in Java hierfür noch immer keine lückenlose Automatisierung gibt. Dadurch wird der Code unübersichtlich, lückenhaft und fehleranfällig. Ich habe mich eine Zeit lang sehr intensiv auf Java eingelassen, nicht unter dem Gesichtspunkt, wie in C++ zu programmieren, sondern unter dem Gesichtspunkt, sicher zu programmieren. Und ich habe dabei keinen vernünftigen Weg gefunden, weder wie in C++, noch einen alternativen Weg. Und ja, es ist richtig, Java ist mit dieser Problematik nicht allein. Die meisten "modernen" Sprachen bewegen sich in Bezug auf Fehlerbehandlung und Ressourcenverwaltung bis heute auf dem Niveau von C.
Chromanoid hat geschrieben:Mit meinen Beispielen wollte ich versuchen Probleme zu konstruieren, bei denen RAII im Weg steht. Jedes Konzept hat seine Nachteile und die habe ich versucht herauszukitzeln. Wenn meine Beispiele nicht schmecken, dann würde ich darum bitten selbst welche zu konstruieren. Nicht damit ich sagen kann warum ich Java toller finde, sondern einfach um den Sachverhalt besser zu verstehen. Meine Vermutung ist immer noch, dass RAII in Punkto Erweiterbarkeit Probleme machen kann.
RAII ist ein Konzept, das sich auf alles anwenden lässt. Egal ob Invarianten, Ressourcen oder Speicher. RAII steht nur dann im Weg, wenn du Ressourcen nicht entbinden und transferieren kannst, wie das im Moment mit try-with-resources in Java der Fall ist. Das ist aber ein Problem des Unverständnisses auf Seiten der Sprachentwickler, nicht ein Problem des Konzepts.
alphanew.net (last updated 2011-07-02) | auf Twitter | Source Code: breeze 2 | lean C++ library | D3D Effects Lite
Antworten