Was ist für euch guter Programmcode?



  • simon.phoenix schrieb:

    btw: Das nachgestellte const erschwert die Lesbarkeit (Gewöhnungsfaktor, dass man das const vorne an stellt). Finde ich 🙂

    Nein, ein nachgestelltes const ist eigentlich logischer. Vorallem wenn du dir mal anguckst, wie man Member-Funktionen konstant macht.



  • Ich meinte mit in die breite eher im Sinne von vielen kleinen Klassen anstelle von einer rießigen Mammutklasse.



  • Der es besser weiss schrieb:

    Es gibt keinen guten oder schlechten Code.
    Es gibt nur Code, der eine Problemstellung zufriedenstellend löst,
    oder eben nicht zufriedenstellend löst. Ob da jetzt viele Schleifen oder
    viele Funktionen oder viel Flammerei mit im Spiel ist, ist dann eigentlich
    belanglos!!!

    Schluss jetzt!!!

    Ja! Endlich ein Statement das den Nagel auf den Kopf trifft! Auch ein perfekt strukturierter Code ist wertlos, wenn er die Problemstellung nicht löst! Es führen bekanntlich viele Wege nach Rom!

    @Shade: Ich hab mir Dein Tic Tac Toe jetzt einmal angeschaut. 20 oder mehr separate Quelldateien für so eine Mini-Aufgabe? Ist das nicht ein bisschen übertrieben? Um Mücken zu jagen, verwendet man doch auch nicht schwere Artillerie.

    @Alle:
    So, jetzt knall ich Euch einfach mal eine etwas anspruchsvollere Aufgabe auf den Tisch.
    Ihr seid eine Firma, habt 2000 Mitarbeiter, auf Eurem Firmengelände gibt es 3000 Türen die sich mit 150 verschiedenen Schlüsseln aufsperren lassen. Ihr könnt es euch leisten und stellt jemanden ein, der die Vergabe der Schlüssel an die Mitarbeiter gegen Kaution sowie die Zurücknahme der Schlüssel von aus der Firma ausscheidenden Mitarbeitern sowie die Wiederauszahlung der Kaution mit Hilfe eines massgeschneiderten Datenbankprogrammes verwalten soll, ausserdem soll immer abfragbar sein, wieviele Exemplare eines der 150 Schlüsseltypen momentan vorrätig sind und wieviele sich momentan im Gewahrsam der Mitarbeiter befinden.
    Ungefähr so erzählt ihr es dann einem Softwareentwickler, der ein entsprechendes, massgeschneidertes Datenbankprogramm entwickeln soll. Das Betriebssystem ist Windows2000.
    Dann will er noch haben, dass die Daten bereits sortiert in die Datei geschrieben werden (asynchron natürlich, nur um es noch komplizierter zu machen), damit man die Datensätze später im Rekordtempo mit Binary Tee aus der Datei lesen kann.

    ---
    So, wo fangt ihr an, wenn zufällig Euch das Schicksal ereilt hätte, und Ihr müsstet jetzt diese Anwendung schreiben?
    Denkt daran, dass ihr mit Eurem Konzept auch noch in einigen Tagen zurechtkommen müsst, wenn ihr mit Eurer Arbeit bereits weit fortgeschritten seid.

    Was das alles mit gutem oder schlechtem Programmcode zu tun hat? Sehr viel, denn wenn ich jetzt anfange, hunderte kleine Minimodule für jede simple kleine Setz- oder Leseoperation zu schreiben, dann verliere ich als Normalsterblicher mit hundertprozentiger Sicherheit innerhalb kürzester Zeit den Überblick über die Bezeichner für seine Funktionen!
    Ihr werdet feststellen, dass Ihr Euch bei der Umsetzung von Aufgabenstellungen diesen Typs ziemlich schnell selber dabei ertappen werdet, hundertzeilige und ziemlich komplizierte Funktionen schreiben zu müssen.

    Es ist nur allzu logisch, dass der Umfang einer Funktion proportional zu der von ihr verlangten Mächtigkeit ist.

    (Nur aus Neugierde, hat jemand von Euch schon einmal ein Projekt diesen Typs auf dem Tisch gehabt?)



  • Mecnels schrieb:

    Was das alles mit gutem oder schlechtem Programmcode zu tun hat? Sehr viel, denn wenn ich jetzt anfange, hunderte kleine Minimodule für jede simple kleine Setz- oder Leseoperation zu schreiben, dann verliere ich als Normalsterblicher mit hundertprozentiger Sicherheit innerhalb kürzester Zeit den Überblick über die Bezeichner für seine Funktionen!

    Du scheinst den Sinn von diesen vielen Aufspaltungen nicht ganz gerafft zu haben. Bessere Wartbarkeit und _Wiederverwendbarkeit_. Teile einer Funktion sind beschissen wiederzuverwenden, ganze Funktionen nicht. Außerdem wird die Übersichtlichkeit stark erhöht.

    Und den Überblick über Bezeichner verliert man nur wenn man in einer 500 Zeilen-Funktion 87 lokale Variablen erzeugt hat 👎



  • nichtsdestotrotz besteht der code einer funktion aus funktionsaufrufen oder trivialen handlungen.

    du würdest wirklich die ganze problemstellung in, sagen wir mal, ein halbes dutzend funktionen packen? dann kannst du auch gleich alle funktionen sparen und alles ins hauptprogramm klatschen (überspitzt formuliert).



  • Was das alles mit gutem oder schlechtem Programmcode zu tun hat? Sehr viel, denn wenn ich jetzt anfange, hunderte kleine Minimodule für jede simple kleine Setz- oder Leseoperation zu schreiben, dann verliere ich als Normalsterblicher mit hundertprozentiger Sicherheit innerhalb kürzester Zeit den Überblick über die Bezeichner für seine Funktionen!

    Ich nicht. Ich kann, wenn ich Code lese, mit einem viel aussagenden Funktionsaufruf wie sortList(list); mehr anfangen, als wenn ich das ausschreibe.
    Dadurch behalte ich mehr Überblick, denn wenn ich nur wissen muss "aha, da wird die liste sortiert", dann bin ich froh, wenn ich den Code, der die Liste sortiert, gar nicht sehen muss, wenn mich die Sortierung selber gar nicht interessiert im Moment.
    Und wenn man alles schön in Klassen kapselt hat man kein Problem damit, viele kleine Einzelteile zu haben.

    Ihr werdet feststellen, dass Ihr Euch bei der Umsetzung von Aufgabenstellungen diesen Typs ziemlich schnell selber dabei ertappen werdet, hundertzeilige und ziemlich komplizierte Funktionen schreiben zu müssen.

    Natürlich ertappe ich mich dabei, wie meine Funktion immer länger wird. Und was mach ich dann? Ich sehe "aha, mit diesem Code lese ich erstmal die Konfiguration von irgendwas aus. Dann markier ich diesen Code, mache einen Rechtsklick, wähle aus dem Kontextmenü "Extract method" denk mir noch einen Namen wie readConfig() aus und dann steht statt den 50 Zeilen Code nur noch readConfig(); da und die 50 Zeilen stehen woanders. Das interessiert mich dann schon gar nicht mehr und ich kann an der ursprünglichen Funktion weitercoden und habe den Überblick. Denn readConfig(); sagt mir immer wieder recht schnell, was hier passiert.



  • Längere Parameterlisten lassen sich aber manchmal nicht vermeiden.

    Doch. Man muss nur kreativ sein.

    class CreateFoo {
       friend class Foo;
    
    public:
       CreateFoo & host (std::string const & newHost)
       { host_ = newHost; }
    
       CreateFoo & user (std::string const & newUser)
       { user_ = newuser; }
    
       CreateFoo & password (std::string const & newPassword)
       { password_ = newPassword; }
    
       CreateFoo & method (std::string const & newMethod)
       { method_ = newMethod; }
    
    private:
       std::string host_;
       std::string user_;
       std::string passwd_;
       std::string method_;
    };
    
    class Foo {
    public:
       Foo (CreateFoo const &);
       ...
    };
    

    Und dann kanste so ein Foo bauen:

    Foo foo = CreateFoo()
       .host ("...")
       .user ("...")
       .password ("...")
       .method ("...");
    

    Du kannst 'CreateFoo' sogar ein default-Konstruktor spendieren, der alles mit vernünftigen Vorlagen belegt. Auf die Weise kann man sogar beliebige Parameter bei der Definition auslassen, statt nur die letzten und man hat freie Wahl über die Reihenfolge. Es ist also nicht schlimm, wenn man sich mal nicht genau erinnert.



  • besserwisser schrieb:

    nichtsdestotrotz besteht der code einer funktion aus funktionsaufrufen oder trivialen handlungen.

    Simmt, also kann ich ihn zerlegen und übersichtlicher machen.

    dann kannst du auch gleich alle funktionen sparen und alles ins hauptprogramm klatschen (überspitzt formuliert).

    Nein, da hat das eine mit dem anderen nichts zu tun 🙄



  • Mecnels schrieb:

    Ihr werdet feststellen, dass Ihr Euch bei der Umsetzung von Aufgabenstellungen diesen Typs ziemlich schnell selber dabei ertappen werdet, hundertzeilige und ziemlich komplizierte Funktionen schreiben zu müssen.

    Es ist nur allzu logisch, dass der Umfang einer Funktion proportional zu der von ihr verlangten Mächtigkeit ist.

    Andersherum wird ein Schuh draus. Dinge wie Modularisierung nach dem Prinzip "high cohesion, low coupling", Information Hiding, Datenkapselung, Layering, Dependency Inversion usw. werden mit zunehmender Komplexität der Aufgabe wichtiger nicht andersherum. Nicht umsonst sind alle diese Techniken Antwortversuche auf die zunehmende Komplexität von Software. Ein kleines Ein-Tag-Ein-Mann-Projekt kannst du völlig ohne Berücksichtigung solcher Mittel in eine große Funktion mit 60% globalen Variablen klatschen. Das geht, weil du das Programm vollständig und umfassend verstehst. Sobald dies aber nicht mehr gegeben ist, wird die Qualität der Abstraktion und Modularisierung entscheidend.
    Nicht nur für die Verständlichkeit und Wartbarkeit des Programms sondern auch für die Möglichkeit arbeitsteilig daran zu entwickeln.

    (Nur aus Neugierde, hat jemand von Euch schon einmal ein Projekt diesen Typs auf dem Tisch gehabt?)

    Nur aus Neugierde, hast du jemals irgendein Buch zum Thema Softwareentwicklung gelesen?



  • Mecnels schrieb:

    Was das alles mit gutem oder schlechtem Programmcode zu tun hat? Sehr viel, denn wenn ich jetzt anfange, hunderte kleine Minimodule für jede simple kleine Setz- oder Leseoperation zu schreiben, dann verliere ich als Normalsterblicher mit hundertprozentiger Sicherheit innerhalb kürzester Zeit den Überblick über die Bezeichner für seine Funktionen!

    Ist dir mal in den Sinn gekommen, dass gute Modularisierung auch Zeit sparen kann? Wenn jetzt jemand kommt und sagt: "Erweitere das Ding um die und die Funktion", wer steht wohl besser da?

    Mecnels schrieb:

    Ihr werdet feststellen, dass Ihr Euch bei der Umsetzung von Aufgabenstellungen diesen Typs ziemlich schnell selber dabei ertappen werdet, hundertzeilige und ziemlich komplizierte Funktionen schreiben zu müssen.

    Danke für deine Ratschläge, aber ich habe auch schon in großen Projekten mitgearbeitet und gemerkt, dass meine Arbeit immer einfacher wurde, je mehr ich auf Wiederverwendbarkeit badacht war.

    Kurzes Beispiel:
    Man will einen String nach einem Trennzeichen aufsplitten. "Keine Frage" denkst du, "eine simple Schleife und die Aufgabe ist erledigt". Ich denke "Wenn ich das in eine Funktion verpacke dann kann ich es woanders wieder verwenden" und beim nächsten mal brauche ich nur einen Funktionsaufruf, weil ich die Funktion ja habe. Der Mehraufwand, eine Funktion zu basteln, zahlt sich später um ein Vielfaches aus, wenn ich wieder so etwas brauche.



  • @Helium:

    Coole Lösung werde ich mir merken, allerdings sieht das für dieses Problem etwas überzogen aus 😃



  • simon.phoenix schrieb:

    besserwisser schrieb:

    nichtsdestotrotz besteht der code einer funktion aus funktionsaufrufen oder trivialen handlungen.

    Simmt, also kann ich ihn zerlegen und übersichtlicher machen.

    hä???

    void funktion1(int a, int  b, int c)
    {
        funktion2(a);
        funktion3(b);
    
        trivialhandlung1;
        trivialhandlung2;
    
        funktion3(c);
    
        return;
    }
    

    da gibts nix mehr zu zerlegen, du schnellmerker- wie ich geschrieben habe. 🙂

    simon.phoenix schrieb:

    dann kannst du auch gleich alle funktionen sparen und alles ins hauptprogramm klatschen (überspitzt formuliert).

    Nein, da hat das eine mit dem anderen nichts zu tun 🙄

    kannst du lesen?!? "überspitzt formuliert"!!!!

    wir haben hier jemanden, der keine peilung von modularisierung hat und von monsterfunktionen schwärmt. die konsequente fortsetzung dieser denkweise sieht dann eben so aus:

    int main()
    {
        loese_die_problemstellung();
    
        return 0;
    }
    


  • @besserwisser:

    habe dich wohl etwas falsch verstanden 🙂

    besserwisser schrieb:

    int main()
    {
        loese_die_problemstellung();
    
        return 0;
    }
    

    Das werde ich mir merken, sehr feine Sache 😃

    Spaß beiseite, wieso programmiert mecnels nicht gleich C 👎?



  • Das hat mit C nichts zu tun.



  • HumeSikkins schrieb:

    Shade Of Mine schrieb:

    An Humes Code trau ich mich nicht ran

    Warun nicht? Zu wild, zu lang oder zuwenig "Fleisch"?

    Angst, pure nackte angst.
    Stell dir mal vor ich versteh ihn nicht, dann bin ich ganz traurig und verzweifle 😞

    :p

    Gregor schrieb:

    simon.phoenix schrieb:

    Längere Parameterlisten lassen sich aber manchmal nicht vermeiden.

    z.b. Logon(server, user, pw, method)

    dann noch hübsche "const std::string& bla" und schon muss man in die nächste Zeile um die 80 zeichen einzuhalten 😞

    Kannst ja irgendwelche Wrapper oder structs oder so nutzen. 🙂

    Ne, das wäre IMHO übertrieben. Wenn eine Funktion 4 params braucht, dann braucht sie diese halt. Und hier ist ja auch klar warum sie gebraucht werden.

    Natürlich muss man überlegen ob man die Parameter nicht irgendwie reduzieren kann, aber oft ist dies nicht möglich. Und sofern es keine WinAPI auswüchse annimmt ist es ja ok. Solange man weiß, was welcher Param macht, ist es OK.

    Mecnels schrieb:

    @Shade: Ich hab mir Dein Tic Tac Toe jetzt einmal angeschaut. 20 oder mehr separate Quelldateien für so eine Mini-Aufgabe? Ist das nicht ein bisschen übertrieben? Um Mücken zu jagen, verwendet man doch auch nicht schwere Artillerie.

    Es gibt durchaus einige Codestellen wo ich das mit Absicht gemacht habe. zB wird Template Method verwendet, obwohl es unnötig ist.

    Aber diese Einteilung in Source Dateien ist sogar noch recht kompakt. Die neue Version wird noch etwas mehr haben.

    Der Grund: eine Datei, eine Aufgabe
    Das erleichtert das finden von bestimmten Codestellen ungemein.
    Wenn ich Code warte, dann brauch ich idR nur kleine Teile, denn der Rest sollte ja laufen 😉
    Da ist es praktisch wenn ich weiß, dass ich wirklich nur Code vor mir habe, der relevant ist und nicht in der ganzen Datei nach den einzelnen stellen suchen muss (das wird mir natürlich nicht erspart bleiben, aber wenn ich 100 Zeilen Code durchsuchen muss, ist es einfacher als 10000 zeilen).

    Auch kann ich viele Sachen wiederverwenden, zB das Menü. Kann man in jeder beliebigen Anwendung ohne nennenswerte Änderung übernehmen.

    Die Einteilung erleichtert es mir, die relevanten Stellen zu finden. Und auch Tests lassen sich so leichter schreiben, weil man mehr überblick hat und nicht so leicht etwas übersehen kann.

    So, jetzt knall ich Euch einfach mal eine etwas anspruchsvollere Aufgabe auf den Tisch.
    Ihr seid eine Firma, habt 2000 Mitarbeiter, auf Eurem Firmengelände gibt es 3000 Türen die sich mit 150 verschiedenen Schlüsseln aufsperren lassen. Ihr könnt es euch leisten und stellt jemanden ein, der die Vergabe der Schlüssel an die Mitarbeiter gegen Kaution sowie die Zurücknahme der Schlüssel von aus der Firma ausscheidenden Mitarbeitern sowie die Wiederauszahlung der Kaution mit Hilfe eines massgeschneiderten Datenbankprogrammes verwalten soll, ausserdem soll immer abfragbar sein, wieviele Exemplare eines der 150 Schlüsseltypen momentan vorrätig sind und wieviele sich momentan im Gewahrsam der Mitarbeiter befinden.
    Ungefähr so erzählt ihr es dann einem Softwareentwickler, der ein entsprechendes, massgeschneidertes Datenbankprogramm entwickeln soll. Das Betriebssystem ist Windows2000.
    Dann will er noch haben, dass die Daten bereits sortiert in die Datei geschrieben werden (asynchron natürlich, nur um es noch komplizierter zu machen), damit man die Datensätze später im Rekordtempo mit Binary Tee aus der Datei lesen kann.

    Und?
    Ist ja nicht so, dass wir täglich in der Arbeit nur Nasenbohren...

    So, wo fangt ihr an, wenn zufällig Euch das Schicksal ereilt hätte, und Ihr müsstet jetzt diese Anwendung schreiben?
    Denkt daran, dass ihr mit Eurem Konzept auch noch in einigen Tagen zurechtkommen müsst, wenn ihr mit Eurer Arbeit bereits weit fortgeschritten seid.

    Dann mach ich es.
    Sehe momentan keine großen Hürden, habe es aber nur überflogen und deshalb vermutlich etwas übersehen.
    Ist aber im Prinzip egal. Ich würde mich hinsetzen und es versuchen zu lösen.

    Was das alles mit gutem oder schlechtem Programmcode zu tun hat? Sehr viel, denn wenn ich jetzt anfange, hunderte kleine Minimodule für jede simple kleine Setz- oder Leseoperation zu schreiben, dann verliere ich als Normalsterblicher mit hundertprozentiger Sicherheit innerhalb kürzester Zeit den Überblick über die Bezeichner für seine Funktionen!

    Kann ich echt nicht bestätigen. Ich komme mit meinem Stil super aus. Und der Code ist so schön wartbar (was für mich das wichtigste ist, denn Code warten ist scheiße, deshalb will ich, dass es so einfach wie möglich ist, damit ich nicht zuviel Zeit mit Debuggen verschwende).

    Gerade viele kleine Funktionen erlauben mir überblick zu behalten. Ich hab mal in einer Firma gearbeitet wo es etwa wie du es machen würdest, gemacht wurde. Es hab wenig Funktion (OK, wenig ist untertrieben, es war einfach soviel Code, dass es eine Menge Funktionen waren) dafür aber riesige. Durchaus auchmal 1000 oder mehr Zeilen.

    Ich musste die Funktion "data_save" welche folgende Aufgaben hatte ändern:
    Daten aus Formularen lesen
    Zugriffsrechte checken
    Daten validieren
    Schaun ob Daten gecacht waren
    Daten verarbeiten
    Daten speichern
    "Erfolg" an den user melden

    War ungemein praktisch das Ding. es hat alles erledigt.
    Doch dann musste ich "Daten speichern" ändern. Ich musste die ganze Funktion klonen und nur diese eine Stelle ändern. Denn für Refaktoring war keine Zeit und der Chef hat den Grund nicht eingesehen warum er dafür meine zeit opfern sollte.

    Nungut: die Funktion war doppelt vorhanden, was ein richtiger Albtraum war.

    Mit kleinen FUnktionen wäre das nicht passiert.
    Ich hätte die Änderungen viel schöner integrieren können, weil ich "daten lesen", "access checks", "validierung",... nicht hätte beachten müssen und ich hätte die stelle "daten speichern" sofort gefunden ohne mich durch 1000 Zeilen code zu wühlen und zu schaun wo jetzt genau was gespeichert wird.

    btw: mittlerweile ist die Firma pleite 😉

    Ihr werdet feststellen, dass Ihr Euch bei der Umsetzung von Aufgabenstellungen diesen Typs ziemlich schnell selber dabei ertappen werdet, hundertzeilige und ziemlich komplizierte Funktionen schreiben zu müssen.

    Nein, müssen nicht. Man wird aber dazu verleitet, weil das beim schreiben des Programms einfacher ist. Es hat aber gravierende Nachteile wenn man es mal warten muss.

    Es ist nur allzu logisch, dass der Umfang einer Funktion proportional zu der von ihr verlangten Mächtigkeit ist.

    Ne, ist nicht so. Klar man tendiert stark dazu große mega Funktionen zu schreiben, man darf sich aber nicht gehen lassen.

    Denn ganz ehrlich: so etwas wie "data_save" will ich nie wieder erleben.

    (Nur aus Neugierde, hat jemand von Euch schon einmal ein Projekt diesen Typs auf dem Tisch gehabt?)

    Diesen Typs? Eine DB Anwendung zur Verwaltung von Schlüssel?
    Nein Leider. Aber Bücher? zählt das?

    Viele Leute hier im Forum arbeiten oder arbeiten an Hobby Projekten. idR haben die Leute hier also durchaus Ahnung von "the real world".

    Wobei ich dir natürlich recht gebe: große Funktionen die alles machen zu schreiben, ist sehr verlockend. Es ist einfacher als sich ein gutes Design zu überlegen. Kostet aber in der Wartung enorm viel.



  • Optimizer schrieb:

    Das hat mit C nichts zu tun.

    Übertrieben gesagt schon. Wieso die Vorteile von C++ nicht nutzen, wenn sie denn gegeben sind. Und die aus dem objektorientierten Prinzip resultierende größere Wiederverwendbarkeit von C++- gegenüber C-Code ist ein entscheidender Vorteil. Also landet man wieder bei C.



  • simon.phoenix schrieb:

    Spaß beiseite, wieso programmiert mecnels nicht gleich C 👎?

    Nur zu deiner Information: intelligente Softwareentwicklung gab es auch schon vor C++, Java, OOP, der Komponentenorientierung und anderen neuen Techniken die so more "unified", "service-oriented" oder "model-driven" sind. Die wichtigsten Ideen (Modularisierung, Coupling-Cohesion, Information Hiding, Layering...) sind allesamt schon älter (werden allerdings immer wieder neu erfunden) und können auch in C angewendet werden.



  • simon.phoenix schrieb:

    Und die aus dem objektorientierten Prinzip resultierende größere Wiederverwendbarkeit

    ...ist ein Mythos der sich in 20 Jahren OOP nicht bewahrheitet hat. Die Annahme OOP = Wiederverwendbar ist in der Praxis schlicht und einfach falsch.



  • simon.phoenix schrieb:

    Optimizer schrieb:

    Das hat mit C nichts zu tun.

    Übertrieben gesagt schon. Wieso die Vorteile von C++ nicht nutzen, wenn sie denn gegeben sind. Und die aus dem objektorientierten Prinzip resultierende größere Wiederverwendbarkeit von C++- gegenüber C-Code ist ein entscheidender Vorteil. Also landet man wieder bei C.

    C-Code ist selbstverständlich modularisiert und bisweilen objektorientiert, einige einschlägigen Unix-Libs helfen dabei (die glib zB.).

    Das ganze Gewäsch von wegen "C unbrauchbar", "C++ toll" wird ja von der Realität nicht getragen. Die Wiederverwendbarkeit von durchschnittlichem Quelltext geht in beiden Sprachen -> 0, wenn man da nicht von Anfang an darauf Wert gelegt hat, und gegen viel, wenn doch (Bibliotheken eben). Die vielbeschworenen Vorteile von C++ belaufen sich insgesamt auf einen Haufen syntactic sugar, den nicht jeder lernen will -- und wenn man sich mal echte OOP ansehen will, dann nimmt man nicht C++ dafür.

    Sesch, immer dieses unqualifizierte C-Bashing. Furchtbar, das.



  • Daniel E. schrieb:

    Die Wiederverwendbarkeit von durchschnittlichem Quelltext geht in beiden Sprachen -> 0

    Warum? Es müssen ja nicht immer gleich umfangreich aufgezogene Libs sein. Paar stinknormale Klassensets finden auch in nem anderen Programm Verwendung. Und dass man darauf von Anfang an bedacht ist, hoffe ich doch.


Anmelden zum Antworten