Name von Antipattern gesucht



  • @volkard
    Ach so ein Schmarrn!
    CreateFile ist definitiv keine problematische Schnittstelle.

    Die vermeintliche Komplexität ergibt sich nur daraus dass CreateFile eine Factory-Funktion für viele verschiedenste "Klassen" ist, die sich halt alle ein wenig unterschiedlich verhalten. Was aber gut und notwendig ist.

    Was man hier in Frage stellen kann ist die Doku. Also ob es schlau ist das alles auf einer Seite zu dokumentieren, statt es in eine Seite pro Objekt-"Klasse" zu splitten. Wobei ich die Variante mit einer Seite nichtmal schlecht finde, hat auch Vorteile.



  • hustbaer schrieb:

    @volkard
    Ach so ein Schmarrn!
    CreateFile ist definitiv keine problematische Schnittstelle.

    Die vermeintliche Komplexität ergibt sich nur daraus dass CreateFile eine Factory-Funktion für viele verschiedenste "Klassen" ist, die sich halt alle ein wenig unterschiedlich verhalten. Was aber gut und notwendig ist.

    Ach, so ein Schmarrn!
    Das hätten ruhig verschiedene Funktionen werden dürfen.



  • volkard schrieb:

    Das hätten ruhig verschiedene Funktionen werden dürfen.

    Was den Aufruf von CreateFile() umständlich macht, ist aber eher die Vielzahl der Parameter und nicht der Umstand, daß die Funktion neben Dateien auch Pipes, Laufwerke und Tape-Backups öffnen kann. Und die meisten Parameter braucht man halt. So schlimm finde ich die Funktion jetzt auch nicht, wenn der durchschnittliche Aufruf so aussieht:

    hFile = CreateFileW (path, GENERIC_READ | (writeFlag ? GENERIC_WRITE : 0),
                FILE_SHARE_READ | (writeFlag ? FILE_SHARE_WRITE : 0), &sa, OPEN_EXISTING,
                FILE_ATTRIBUTE_NORMAL, NULL);
    

    Einzig die Sache mit der Template-Datei hätte man besser in eine andere Funktion verschieben können, weil das einen Parameter ( dwFlagsAndAttributes ) unnötig macht und nur irgendeine Zusatzfunktionalität ist, die man zu 99% nicht braucht.

    Oder meinst du das anders mit dem Aufteilen auf mehrere Funktionen?



  • hustbaer schrieb:

    Die vermeintliche Komplexität ergibt sich nur daraus dass CreateFile eine Factory-Funktion für viele verschiedenste "Klassen" ist, die sich halt alle ein wenig unterschiedlich verhalten. Was aber gut und notwendig ist.

    Man braucht eh schon für die verschiedenen "Klassen" z.B. verschiedene write-Funktionen, da kann man das uniform-Ding gleich lassen und mehrere Funktionen machen.
    Linux API ist da wesentlich besser - und irgendwie schaffen die es ja auch mit 3 (?) Parametern.



  • Nathan schrieb:

    Linux API ist da wesentlich besser - und irgendwie schaffen die es ja auch mit 3 (?) Parametern.

    Welche Funktion meinst du 😕



  • vararg schrieb:

    Nathan schrieb:

    Linux API ist da wesentlich besser - und irgendwie schaffen die es ja auch mit 3 (?) Parametern.

    Welche Funktion meinst du 😕

    Na, open.
    Eine schöne klare Funktion, wenn man "mal eben" eine Datei öffnen muss, muss man nicht lang irgendwelche tausenden Parameter verstehen.
    Die haben die Funktionen um an Filedescriptoren zu kommen schön aufgeteilt, je nachdem, was man öffen will.



  • Nathan schrieb:

    Man braucht eh schon für die verschiedenen "Klassen" z.B. verschiedene write-Funktionen (...)

    😕
    Mit WriteFile schreibst du auf
    * lokale files
    * volumes
    * disks
    * remote files
    * serielle ports
    * usb ports
    * drucker (raw)
    * konsolen
    * tapes
    Und allgemein beliebige treiber die IRP_MJ_WRITE implementieren.

    CreateFile hat einfach nur den falschen Namen. Besser wäre etwas wie z.B. CreateDriverHandle .

    Nathan schrieb:

    da kann man das uniform-Ding gleich lassen und mehrere Funktionen machen.

    Kann man. Kannst du unter Windows auch.
    Ich sehe aber keinen besonders grossen Sinn dahinter solche Commodity-Funktionen in der Kernel API (!) anzusiedeln.
    Ich meine... wir reden hier über einen Einzeiler.

    BTW: Wie macht man unter Linux ein Kommunikationshandle zu nem Treiber bzw. Kernel-Modul auf?



  • hustbaer schrieb:

    Nathan schrieb:

    Man braucht eh schon für die verschiedenen "Klassen" z.B. verschiedene write-Funktionen (...)

    😕
    Mit WriteFile schreibst du auf
    * lokale files
    * volumes
    * disks
    * remote files
    * serielle ports
    * usb ports
    * drucker (raw)
    * konsolen
    * tapes
    Und allgemein beliebige treiber die IRP_MJ_WRITE implementieren.

    CreateFile hat einfach nur den falschen Namen. Besser wäre etwas wie z.B. CreateDriverHandle .

    Irgendwie hab ich lieber WriteConsole http://msdn.microsoft.com/en-us/library/windows/desktop/ms687401(v=vs.85).aspx benutzt, weil's mehr kann.



  • volkard schrieb:

    Ich fürchte, Du hast noch nie gegen eine ver{FehlendesWort}e Schnittstelle programmiert.

    hmm, naja, eigentlich ist das mittlerweile meine hauptaufgabe, ich wrapper ugly schnittstellen auf sauberen, simplen

    Ich nenne mal als Beispiel

    ...

    und dieses Beispiel ist längsr nicht das härteste, was man im kommeziellen Umfeld kekkenlernen darf.

    das problem im design ist kein pattern problem. das problem ist legacy code und ein anderes pattern was besagt, dass man ein interface moeglichst minimal halten sollte statt es zu ueberladen. (ich spreche nicht von funktionsueberladung sondern "interface bloat")

    statt also CreateFile, CreatePipe, CreatePrinter, CreateLowLevelHDDFile,... gibt es halt nur eines. Das befolgte pattern dabei ist also eigentlich positiv. dass dich die docu erschlaegt ist dabei ein anderes problem. wie Nathan sagte, hast du das auch bei simplen funktionen wie 'open' auf linux das dir jedes erdenkliche device oeffnen kann. ich denke die meisten programmierer haben auch diese doku nicht komplett gelesen aufgrund ihres umfangs.

    /*
    wenn ich es wrappen wuerde, waere es vermutlich

    HANDLE Create(GenericDescriptor& );
    

    und von generic descriptor waere alles noetige abgeleitet sodass du nur noch

    ...=Create(FileReadDescriptor(name));
    ...=Create(FileReadAsyncDescriptor(name,timeout));
    ...=Create(TCPSocketDescriptor(name,port));
    ...=Create(DirectorySecureDescriptor(name,ACCESSFLAGS_RW_ADMIN));
    

    deswegen denke ich bestaetigt dein Beispiel nur dass die realisierung ein wenig schlecht ist, die grundidee einer funktion die dinge erstellt statt 20 leicht abgeaenderte finde ich gut.
    Stell dir vor du moechtest logging oder einen andere art von error handling oder performance istrumentation oder... einbauen, 20mal dasselbe verbauen zu muessen sollte dann spaetestens jedem programmierer als 'falsch' aufgehen.
    */



  • hustbaer schrieb:

    Ich sehe aber keinen besonders grossen Sinn dahinter solche Commodity-Funktionen in der Kernel API (!) anzusiedeln.

    Naja. Windows hat sowieso einen Abstraktionslayer über der Kernel-API die teilweise ja nichtmal dokumentiert ist und eigentlich auch nicht vom Nutzer benutzt werden sollte.
    Kann ja gerne die Gott-Funktion geben - die darf aber in der ntdll.dll leben, in der kernel32.dll sollte ein höheres Abstraktionsniveau herrschen.

    Wird ja ansonsten auch konsequent so gemacht. Zb. soll der Nutzer doch bitte VirtualQuery (kernel32.dll) mit 3 Parametern nutzen anstatt mit NtQueryVirtualMemory (ntdll.dll) mit 6 Parametern herumzuwerkeln.



  • Dann mach mal nen konkreten Vorschlag wie du CreateFile ändern würdest, bzw. durch welche weiteren Funktionen ersetzen.
    Da is nämlich kein einziger Parameter auf den man verzeichten könnte.



  • hustbaer schrieb:

    BTW: Wie macht man unter Linux ein Kommunikationshandle zu nem Treiber bzw. Kernel-Modul auf?

    Mit open auf ein Device-File. Also z.B. open("/dev/ttyS0", flags) für die serielle Schnittstelle, aber auch open("/dev/kvm", flags) für ein Handle zum Virtualisierungssubsystem KVM.



  • Eine traurige Ausnahme sind übrigens Network-Sockets. Diese existieren nicht im normalen Namespace, was seit jeher ein Designfehler der Berkeley-Sockets war. Als das erkannt wurde, war es bereits zu spät, die Nutzung der Sockets-API zu verbreitet.

    Später designte Systeme wie Plan 9 (Bell Labs) räumten hiermit zwar auf, aber als zu Unix inkompatible Systeme hatten sie es schwer, sich durchzusetzen. Andere Ideen aus Plan 9 sind zwar zurück in moderne Unixe wie Linux und FreeBSD geflossen, die Netzwerk-API gehört aber leider nicht dazu.

    So lebt die Inkonsistenz weiter, dass Sockets und Netzwerkgeräte quasi in einer Parallelwelt existieren.



  • Dann verstehe ich nicht was an der Linux open() Variante besser sein soll bzw. überhaupt grossartig anders ist.


Anmelden zum Antworten