Integration numerischer Typen in Programmiersprachen

Rechnen ist ja das, was wir mit den Computern so machen, deshalb heißen sie ja auch Rechner.
Und zum Rechnen brauchen wir die numerischen Typen andauernd, also kann das wohl kein Problem sein, oder?

Es hängt ein bisschen davon ab, was man sich unter numerischen Typen vorstellt. Fließkommazahlen oder irgendeine Art von Ganzzahlen können fast alle, manche sogar beides. Gute Ganzzahlentypen sind aber leider nur selten verfügbar, da die Frage des Überlaufs oft schlecht gelöst ist. Weitere interessante numerische Datentypen sind rationale Zahlen, komplexe Zahlen und Festkomma-Dezimalzahlen.

Doch wie sieht es mit der Integration in die Sprache aus? Man möchte gerne so etwas wie
a = b*c + d*e
schreiben und meint damit, dass eine Zuweisung an a erfolgen soll, die den Wert (b*c) + (d*e) beinhaltet. Wegen „Punktrechnung vor Strichrechnung“ sollte man die Klammern aber weglassen können. Im Fall von Sprachen wie Lisp oder Forth, die eine völlig andere Syntax verwenden, passt diese Infix-Schreibweise natürlich nicht ins Bild und man kann diese Anforderung nicht sinnvoll stellen. In Lisp mit der Präfix-Schreibweise wäre es so etwas wie:
(setq a (+ (* b c) (* d e)))
und in Forth mit seiner Postfix-Schreibweise wäre es etwa so etwas:
b @ c @ * d @ e @ * + a ! .
C, Perl, Ruby, C++, Java, C#, JavaScript, Lua und vielen anderen funktioniert das mit den eingebauten Datentypen recht gut, aber sobald man eigene numerische Datentypen einführt, braucht man so etwas wie „Operator überladen“, was z.B. Lua, Perl, Ruby, C++ und C# können, aber Java und JavaScript nicht. Deshalb fängt man in Java an, für BigDecimal, BigInteger oder eigene Datentypen so etwas wie
a = b.multiply(c).add(d.multiply(e))
zu schreiben, was praktisch unlesbar und damit fehleranfällig ist. Vielleicht kann man sich mit einem Präprozessor behelfen, aber es bleibt ein Gebastel.

Ein anderer Aspekt ist am Beispiel von Java ganz gut zu sehen. Dort soll ja „alles“ ein Objekt sein. Man kann schön Interfaces, Klassen und Methoden schreiben und Verwenden, die sich auf Objekte verlassen, wie z.B. Map. Nun sind diese „primitiven“ Typen leider keine Objekte und man muss diese Wrapper-Klassen wie Integer, Double, Long, Boolean u.s.w. verwenden, was leider umständlich ist, zumal man mit den Wrappern die numerischen Fähigkeiten nicht mehr zur Verfügung hat. Scheinbar wurde das durch Autoboxing und Autounboxing gelöst, aber ich glaube, dass diese Erweiterung mehr Probleme geschaffen als gelöst hat. Nur als Beispiel, was bedeutet
x==y
wenn x und y long oder Long sind? Mal wird die Objekt-Identität und mal der Wert verglichen und ich vergesse immer, ob unboxing oder boxing zum Zuge kommt, wenn man dabei long und Long zusammenkommen lässt. Man kann aber einige spezielle Collection-Klassen finden, die auf Primitive zugeschnitten sind und dadurch ohne Boxing und Autoboxing auskommen, schneller laufen und weniger Speicher verbrauchen. In erster Linie bleibt es aber umständlich, weil man immer wieder diese Sonderfälle für Primitive und zum Teil auch Arrays berücksichtigen muss.

Share Button

Schatten-IT

Jeder hat es schon gesehen, dass Leute, die oft gar nicht in direkt Informatik arbeiten, sich mit Tabellenkalkulationen anfreunden und dort sogar in recht kurzer Zeit sehr nützliche Dinge zustande bringen. Solange dies Dinge sind, die nur für eine Person nützlich sind, sind alle glücklich und jeder hat seine eigenen Tabellen, man tauscht sie vielleicht sogar aus (natürlich nur innerhalb der eigenen Organisation).

Wenn die Tabellen aber richtig gut sind, dann baut man sie irgendwann in Geschäftsabläufe ein. Irgendwelche Daten werden an Mitarbeiter XY gemailt, der baut sie in seine Tabellenkalkulation auf Laufwerk C: ein und mailt das Ergebnis weiter. Schön, dass wir EMail haben und auch große Attachments möglich sind. Blöd ist nur, wenn XY mal krank ist oder Ferien hat oder gar die Tätigkeit wechselt. Solange es noch in derselben Firma ist, funktioniert noch alles weiter wie gehabt, aber auch das kann mal vorbei sein. Oder das Laufwerk C: kann man unter Gedächtnisverlust leiden, warum auch immer. Deshalb ist man so professionell geworden, die Tabellenkalkulationsdatei auf dem Netzwerklaufwerk M: statt auf Laufwerk C: zu haben. So wird sie beim Backup berücksichtigt und XYs Stellvertreter kann sie dort auch finden, wenn XY mal nicht erreichbar ist. Vielleicht wird auch ein CMS oder ECM statt des Netzwerklaufwerks eingesetzt, was schon etliche Nachteile der Lösung eliminiert.

Weil das alles immer noch ein chaotisches Gebastel ist, muss aber eine richtige Lösung her. Eine „Enterprise“-Lösung. Man kauft eine professionelle Software oder entwickelt sie selber oder lässt sie entwickeln. Allein die Entwicklung oder die Integration der eingekauften Lösung brauchen schon mehr als zehnmal soviel Zeit wie XY insgesamt benötigt hat, um seine Lösung zu entwickeln, obwohl diesmal Informatikprofis am arbeiten sind. Vielleicht sollten wir all unser Fachwissen vergessen und dadurch die Produktivität um Faktor 10 steigern?

Zunächst einmal muss man die beiden Lösungen vergleichen. Was man gewonnen hat, ist durchaus relevant, z.B.:

  • Mehrbenutzerfähigkeit
  • Benutzerverwaltung oder besser noch Integration in das Benutzer und Rollenkonzept
  • Migrationspfade für die Daten
  • Lösung ist an die Organisation und nicht an einzelne Personen gebunden
  • Datensicherheit besser beherrschbar als wenn Daten auf Laptop liegen
  • Besseres Backup
  • Erweiterbarkeit

Lohnt sich dafür der zehnfache Aufwand? Wenn die Applikation wichtig ist, normalerweise schon. Und die Tabellenkalkulationslösung ist wahrscheinlich sogar hilfreich gewesen, weil sie dazu beigetragen hat, wirklich gut zu verstehen, was man mit der Applikation tun will.

Aber die Frage, ob die Entwicklung der Applikation nicht zu lange dauert, ist schon berechtigt. Gewonnen hat man in diesem Fall schon, dass gut bekannt ist, was entwickelt werden soll. Aber das „wie“ lässt noch viele Möglichkeiten offen. Ist der Entwicklungsprozess gut genug? Werden die Leute optimal eingesetzt? Können sie an der Aufgabe arbeiten oder hauptsächlich an administrativen Aufgaben? Ja, das ist eine sehr berechtigte Frage, vielleicht schreibe ich dazu mal etwas. Wird die richtige Architektur eingesetzt? Ist die Lösung zu kompliziert? Oder zu einfach (und funktioniert nachher doch nicht so)? Wird die richtige Technologie eingesetzt? Ich finde es sinnvoller, ab und zu etwas neues zu lernen als den „goldenen Hammer“ zu pflegen, mit dem jede Schraube wie ein Nagel aussieht.

Konkreter: Für eine Applikation, deren Funktionalität in eine Tabellenkalkulation gepasst hat, ist so ein klassischer „Enterprirse-Stack“ mit Java, EJB, Oracle oder DB2, Weblogic u.s.w. sicher fast immer zu aufwendig.

Share Button

Ruby wird 20

Ruby wird 20 Jahre alt:
http://ruby20th.herokuapp.com/

Share Button

Fließkommazahlen als Ersatz für Ganzzahlen

Wie ärgerlich ist eigentlich die Tatsache, dass Lua und JavaScript keine „int“s kennen und man stattdessen Fließkommazahlen („double“) verwenden muss?

Natürlich fühlt es sich völlig falsch an und ich möchte die Entscheidung, nur diese 8-byte-Fließkomma-Zahlen als numerischen Typ zu unterstützen, nicht gutheißen.

Aber was bedeutet das nun genau? Bekommen wir nun plötzlich irgendwo 7.999999999 statt 8?

Die Fließkommazahlen sind heute zum Glück standardisiert und fast jede Implementierung hält sich einigermaßen an diesen Standard. Es gibt eine 4-Byte-Variante und eine 8-Byte-Variante. Die 4-Byte-Variante ist nur dann sinnvoll, wenn man dem Entwickler die Wahl lässt, und fast jeder Entwickler nimmt „zur Sicherheit“ lieber die 8-Byte-Variante. Diese Fließkommazahl sieht nun ungefähr so aus:
1 Bit ist das Vorzeichen
11 Bits drücken den 2er-Exponenten aus
52 Bits drücken die Mantisse aus, dazu kommt als 53stes eine führende 1, die man nicht speichern muss, weil sie ja immer da ist.
Damit kann man die ganzen Zahlen von -2^{53} bis 2^{53} exakt ausdrücken und hat damit etwas, was etwa 54-Bit-Ganzzahlen entsprechen würde. Rein informationsmäßig ist das eine zusätzliche Bit im Vorzeichen und das andere im 2-er-Exponenten codiert. Wenn man also nur addiert, subtrahiert und multipliziert und dabei immer im Bereich von -2^{53} bis 2^{53} bleibt, kann man exakte Ergebnisse erwarten. Natürlich ist es langsamer, aber auch das ist bei heutigen CPUs, die schnelle Fließkommaoperationen seit etwa 20 Jahren standardmäßig (und vorher gegen Aufpreis) in der Hardware eingebaut haben, nicht so tragisch. Wie ist es mit den ungenutzten Bits? Normalerweise stören die nicht, aber wenn man Applikationen entwickelt, die sehr speicherintensiv sind, kann auch so etwa ausnahmsweise mal relevant werden.

Ärgerlicher ist da schon die Division. Hat man bei ints so etwas wie „/“ für die Division mit Abrundung und „%“ für den Rest, wird bei Fließkommazahlen mit „/“ wirklich dividiert und eine neue Fließkommazahl berechnet. Wie rundet man hier ab, um das „int-/“ und das „int-%“ zu bekommen? Es sollte dabei ja trotzdem noch das 6.9999999999999 zu 7 gerundet werden.

Ungefähr so etwas könnte funktionieren:

def divmodf(x, y)
xf = x.to_f
yf = y.to_f
df = if (yf < 0) then
-0.2
else
0.2
end
qf = (xf + df) / yf;
q = qf.floor
r = (yf*(qf-q)).round(0)
return q,r
end

Das ist jetzt in Ruby geschrieben, aber es würde natürlich ähnlich in Lua oder JavaScript funktionieren.

Share Button

Wie könnte ein C-Nachfolger aussehen?

Man verwendet heute C kaum noch für Applikationsentwicklung, aber wenn wir uns anschauen, welche Software wir täglich dauernd verwenden, ist ein großer Teil davon in C oder eventuell in C++ geschrieben, z.B. gängige Betriebssysteme (Linux, MS-Windows,…), Web-Browser, die meisten Datenbanken, die meisten Editoren, die meisten Office-Programme, die meisten Fenstersysteme, viele Bildverarbeitungsprogramme, u.s.w.

Was könnten die Gründe sein, daß man für diese Zwecke C verwendet? Vielleicht die relative Nähe an der Assemblersprache kombiniert mit der richtigen Abstraktion und einigen nützlichen Library-Funktionen? Vielleicht der direkte Zugriff auf Betriebssystem-APIs? Das einigermaßen gut vorhersehbare Zeitverhalten? Oder einfach die vorhandene Codebasis? Und C-Programme sind schnell. Aber nur wenn man genügend Zeit zum Entwickeln hat. Die C-Syntax hat sich zum de-facto-Standard für viele Programmiersprachen entwickelt.

Diese Vorteile will man natürlich beibehalten. Daher scheiden als direkte Nachfolger Sprachen mit einer hohen Abstrahierung, einem kommplizierten Laufzeitsystem mit Garbage Collection u.s.w. aus. Natürlich wurde C in der Applikationsentwicklung durch Java, Scala, C#, Ruby, Perl, PHP, Python, JavaScript u.s.w. teilweise verdrängt. Gerade deswegen braucht man die Features dieser Sprachen nicht einfach in C zu übernehmen, denn wenn man das will, kann man diese Sprachen statt C verwenden.

Vielleicht will also niemand einen C-Nachfolger? Kann sein, aber es gibt doch ein paar Dinge, die man besser machen könnten und die vielleicht einen Nachfolger, aber sicher nicht einen ganzen Zoo von Nachfolgern rechtfertigen würden:

  • Bessere arithmetische Operationen auf den primitiven Typen: Durchreichung von nützlichen Assembler-Features wie Addtion mit Carry, Multiplikation mit einem Ergebnis von der doppelten Länge der Faktoren.
  • Echte Zeichenketten mit einfach handhabbarer nativer Unterstützung für verschiedene Zeichensatzcodierungen (UTF-8, ISO-8859-*, ISO-646-irv, UTF-16,…)
  • Bessere Unterstützung für Thread-Safety.
  • Vielleicht eine bessere Fehlerbehandlung
  • So etwas wie ein Schlüsselwort „unsafe“, bei dessen Benutzung C-übliche Dinge wie Casts von Zahlen auf Pointer, indizieren von Arrays außerhalb von deren Bereich u.s.w. möglich sind. Ohne das Schlüsselwort werden diese Operationen nicht unterstützt oder führen zu Laufzeitfehlern.
  • Mehrfache Rückgabewerte bei Funktionsaufrufen

Einen C-Nachfolger, der nicht Link-Compatibilität zu C bietet, könnte man natürlich vergessen. Man möchte ja auf jeden Fall die bestehende Code-Basis und die Libraries weiterhin benutzen können.

Wird es so etwas geben? Ich weiß es nicht, es sieht natürlich im Moment nicht danach aus und man behilft sich damit, Dinge, die in C nicht gut gehen, stattdessen in anderen Sprachen zu entwickeln. Wir sehen es ja an dem ähnlichen Beispiel Ceylon, das einen Java-ähnlichen Java-Ersatz bieten soll und dabei einige unglaublich ärgerliche Design-Fehler von Java vermeidet. Wenn Ceylon einmal ausgereift ist, ist es wahrscheinlich absurd, noch Dinge in Java zu entwickeln, aber wenn man schon wechselt, kann man auch zu moderneren Sprachen wie
Ruby, Scala oder Clojure wechseln. So halten sich ärgerliche Designfehler in Programmiersprachen länger als eigentlich nötig.

Es hilft viel, für die Aufgabe das passende Werkzeug zu verwenden, aber es ist sicher gut, auch einmal darüber nachzudenken, wie man einige dieser Werkzeuge verbessern könnte.

Share Button

Verknüpfung Social Media anderen mit Webseiten

Wir finden oft auf beliebigen Webseiten Facebook-„Like“-Buttons oder „+1“-Buttons oder „Twitter“-Buttons. Ich habe selber welche hier drauf.

Es gibt aber auch Seiten, die anbieten „login mit Facebook“ oder „login mit Google+“ oder „login mit Twitter“.

Was bedeuten diese Verknüpfungen?

Es sind, wie man unschwer erkennen kann, zwei verschiedene Mechanismen. Grundsätzlich basieren sie darauf, daß viele von uns an einem oder mehreren dieser Social-Media-Systeme teilnehmen. Dazu muß man dort einen Account erwerben, eine Mailadresse und eventuell eine Mobiltelefonnummer angeben, ein Password und ein Captcha abtippen und dann ist man nach der Bestätigung einer Testmail dabei. Nun gibt es Leute, die sich nach Gebrauch bei diesen Social-Media-Seiten abmelden und sogar die Cookies aufräumen oder im „private-Modus“, den Firefox anbietet, auf diese Seiten gehen. Das ärgerliche daran ist, daß man mehr und mehr dazu übergehen muß, wirklich gute Passwörter zu verwenden und auch noch verschiedene für jede dieser seiten. Und man darf sie nicht aufschreiben. Das ganze soll dann noch auf diversen Computern, Mobiltelefonen und anderen Geräten funktionieren. Und google bietet schon einen sichereren Login-Mechanismus wie bei E-Banking an, wo man zusätzlich noch einen SMS-Code eingeben muß. Schlimm, wenn man sein Leben in diese Social-Media-Systeme verlegt hat und sein Mobiltelefon verlegt hat. Aber sowas kann uns in der heutigen Zeit ja nicht passieren… Eher vergißt man seine Füße oder seinen Kopf oder so etwas zuhause, wenn man aus dem Haus geht.

Die Erfahrung zeigt, daß die meisten Computerbenutzer immer bei allen Social-Media-Netzen eingeloggt sind. Man kann also Facebook, Google+ und Twitter starten und einfach losschreiben. Wie sicher ist das? Nun, gemäß N. Kaspersky: „A security guy who says that something is absolutely secure is not a real security guy“. Ob ich nun ein Security-Guy bin oder nicht, es ist also nicht absolut sicher, denn wenn der Rechner, an dem wir sitzen, Teil eines Bot-Netzes ist, hat der Bot-Netz-Betreiber Zugriff auf alles, was auf dem Rechner läuft, also auch auf Cookies, Passwörter, Tastatureingaben u.s.w. Je nach Features der Bot-Netz-Software. Und es sind so viele Rechner in der Welt mit Schadsoftware „infiziert“, daß diese Botnetze groß sind. Man kann sie ja von entsprechenden Geheimfirmen im zehntausender-Pack mieten, habe ich gehört.

Aber gut, wenn unser Rechner sauber ist und wenn wir nicht glauben, daß der Hersteller eines propritären Betriebssytems Features eingebaut hat und nutzt, um sich auf unserem Rechner umzusehen, dann kommen wir zur Serverseite. Jede von diesen Seiten, auf der wir uns einloggen können, muß Passwörter speichern, außer sie setzen ganz auf dieses „login in via…“-Zeug. Wenn wir überall verschiedene Passwörter verwenden, ist das nicht so tragisch, kann aber schnell *sehr* ärgerlich werden, wenn jemand Kommentare oder EMails oder Tweets unter unserem Namen schickt. Vor allem, wenn man das nicht so schnell merkt. Aber man kann auch Server, die im Internet stehen, vernünftig absichern und das Risiko von Einbrüchen über das Netz verringern. Wie sieht es mit der Netzwerkverbindung aus?

Auf Seiten, die ein Passwort erfordern, sollten wir immer https verwenden. Das hat den Nachteil, daß die Proxys nichts mehr cachen können, weil für dieselbe Seite durch die verwendete Verschlüsselung für jeden Empfänger andere Daten übertragen werden. Die grobe Idee ist etwa die folgende: Der https-Server hat ein Zertifikat, das im Wesentlichen aus einem privaten Schlüssel besteht, der nur auf dem Server bekannt sein darf, und aus einem öffentlichen Schlüssel, der in mit dem Domainnamen und weiteren Metainformationen verknüpft ist und wiederum mit dem privaten Schlüssel einer Zertifizierungsstelle signiert ist. Die Browser kennen nun die Public-Keys aller gängigen Zertifizierungsstellen und können so diese Kombination abprüfen. Wenn also die Zertifizierungsstelle sauber arbeitet, vergibt sie das Zertifikat nur an denjenigen, der die entsprechende Domain hat. Nun kann unser Browser erkennen, ob wir wirklich mit der Domain kommunizieren, zu der das Zertifikat gehört. Wenn nicht, erscheinen komische Fehlerboxen, die man nicht einfach wegklicken sollte. So wird schon verhindert (oder sehr erschwert), daß wir über gefälschtes DNS, gefälschtes Routing oder gefälschte Proxys auf die falsche Seite gelangen. Denn die falsche Seite kommt nicht an das richtige Zeritifikat heran, bzw. kann es nicht einfach aus dem öffentlichen Teil rekonstruieren, weil der private Schlüssel fehlt.

Die gesamte Kommunikation findet verschlüsselt statt und man kann dabei eine Verschlüsselung haben, die wahrscheinlich nach heutigen Erkenntnissen sichtbar ist. Vielleicht kann jemand den https-Traffic aufzeichnen und in 20 Jahren entschlüsseln, aber dafür muß es schon sehr interessant sein. Wichtig ist, daß das Passwort nicht im Klartext über das Netz geht. Nun bekommt der Browser einen Cookie, also ein paar Bytes. Jedesmal wenn wir auf dieselbe Seite gehen, wird dieser Cookie wieder an den Server geschickt und daran kann dieser erkennen, daß wir noch eingeloggt sind. Wenn nur das login via https erfolgt, der Rest der Kommunikation aber über http, kann unterwegs jemand den Cookie abgreifen und unsere Session übernehmen. Daher ist es sinnvoll, die Seiten mit login komplett via https anzusprechen.

Wenn nun jemand, der in Facebook noch eingeloggt ist, auf eine Seite mit like-Buttons kommt, dann wird mit dem Laden dieser Seite auch ein bißchen Code von Facebook geladen. Dabei wird der Cookie an Facebook geschickt und die wissen dadurch etwas darüber, welche Webseiten uns interessieren. Mit dem Like-Button wird nun etwas bequemer dasselbe nachgebildet wie wenn wir die URL in ein Facebook-Fenster Kopieren und man kann je nach Social-Media-System noch etwas dazu schreiben.

Dieser Login-via-Google-Knopf dient zwei Zwecken. Er soll uns Anwender sparen, noch ein Passwort mehr zu lernen und er soll dem Serverbetreiber sparen, die Passwörter sicher zu handhaben. Aber auch hier wird natürlich google (oder twitter, Facebook o.ä.) über den Besuch der Seite jeweils informiert. Damit das funktioniert, müssen wir als Benutzer in unserem google-Account die Seite, auf der wir uns einloggen, autorisieren, unsere Identitätsinformation zu beziehen, wenn wir dort eingeloggt sind. Das ist, wenn man es sich überlegt, nichts anderes als ein Login. Aufpassen muß man aber, daß wir diesen Seiten nicht zu viele Rechte auf unserem Account einräumen, sonst schicken die Spam-Nachrichten oder so etwas in unserem Namen.

Share Button

Drittanwendungen in Twitter, Facebook, Google+ & Co.

Diese Social-Media-Systeme erlauben es, daß man Drittanwendungen Zugriff auf seinen Account gibt. Es gibt durchaus legitime Gründe, aber man sollte schauen, ob man der Drittanwendung und deren Security-Team traut und eventuell die Zugriffe abstellen oder reduzieren. So geht es:

Twitter mit Web-Client (geht auch auf dem Mobiltelefon mit twitter.com):

  • Oben rechts das Zahnrad anklicken
  • In dem Zahnrad-Menü auf „Einstellungen“
  • Links auf „Apps“
  • Die Liste der Apps durchschauen und die unerwünschten Zugriffsrechte sperren

Facebook mit Web-Client (geht auch auf dem Mobiltelefon mit facebook.com):

  • Auch oben rechts das Zahnrad anklicken, ist nur kleiner als bei Twitter
  • In dem Zahnrad-Menü auf „Konto-Einstellungen“
  • Links auf Anwendungen
  • Unerwünschte Anwendungen weg-x-en
  • Legitime Anwendungen mit „Bearbeiten“ überprüfen

Google+ mit Web-Clienten (Gugel+):

  • Auch oben rechts das Zahnrad anklicken, ist wieder größer als bei Facebook. Haben die drei sich abgesprochen??
  • In dem Zahnrad-Menü auf „Einstellungen“
  • Sicherheit
  • Verbundene Anwendungen und Websites: „Zugriff verwalten“
  • Verbundene Websites, Apps und Dienste
  • Bei unerwünschten Anwendungen die Zugriffsrechte widerrufen

Nun gibt es Seiten, die ein „Login mit Facebook“, „Login mit Google(+)“, „Login mit Twitter“ o.ä. erlauben. Dieser Mechanismus ist besser als überall dasselbe Passwort zu verwenden, ob man ihn will oder nicht ist eine längere Diskussion. Aber alle Seiten, auf denen man sich mit Twitter, Google oder Facebook anmeldet, tauchen hier auf und wenn man die weglöscht, löscht man sein Login auf diesen Seiten. Aber warum eine Seite, auf die ich mich mit Twitter anmelden kann, deshalb das Recht bekommen soll, in meinem Namen Tweets oder Facebook-Kommentare oder Google+-Beiträge zu schreiben, ist eine andere Frage.

Und noch was: Seiten, auf denen man sich einloggt, bitte immer mit https und nicht http ansprechen! Wenn die Seite kein http kann, dann ist das Passwort dieser Seite quasi öffentlich, sollte also auf keinem Fall mit einem Passwort auf einer https-Seite übereinstimmen. E-Banking ist immer mit https. Google(+), Facebook, Xing, Wikipedia und viele andere Seiten können mit https angesprochen werden oder schalten automatisch darauf um.

Ich versuche bis zum kommenden Freitag einen Blog-Artikel zu schreiben, in dem ich genauer auf die Funktionsweise dieser Mechanismen eingehe.

Share Button

Cyber-Attacke gegen Twitter

tagesanzeiger.ch

Share Button

Micro-Komponenten-Antipattern

Eine größere Software muss man sicher strukturieren, sonst baut man sich ein Monster.

Nun kann man versuchen, Komponenten zu definieren, die so klein wie nur möglich sind. Die Komplexität innerhalb der Komponenten wird dadurch reduziert und überschaubar. Aber man bekommt ein Problem, weil die Menge der Komponenten dabei zu groß wird und man eine sehr hohe Komplexität bekommt, diese vielen Komponenten miteinander zu verknüpfen.

Man sieht es bei Büchern, bei denen die Gliederung gut gelungen ist, dass es eine Hierarchie von Gliederungen gibt und in jeder Ebene findet man etwa 2-10 Untereinträge zu den Einträgen aus der nächst höheren Ebene, nicht aber 50 Kapitel, die nicht in irgendeiner Form gruppiert sind.

Solche Ebenen wie „Teil“, „Kapitel“, „Abschnitt“… hat man in der Software-Architektur auch zur Verfügung, wobei es von der Technologie abhängt, welche Hierarchieebenen für dieses Gliederung und Strukturierung zur Verfügung stehen, z.B. Methode – Klasse – Package – Library – Applikation und man sollte sie mit Bedacht einsetzen. Was gehört zusammen und was nicht?

Es gibt aber noch einen anderen Aspekt. Oft verbaut man sich durch zu feingranulare Aufteilung Wege.

Ein Beispiel: Es soll eine Multiplikation von Matrizen mit kmoplexen Zahlen als Elementen implementiert werden. Nun ist es sehr elegant, die Matrizenmulitplikation einmal zu implementieren und dabei einen abstrakten numerischen Typ zu verwenden. Für jeden dieser numerischen Typen implementiert man die Grundoperationen.

Dies kann aber in Bezug auf die Perforamnce und auch in Bezug auf die Rechengenauigkeit beim Arbeiten mit Fließkommazahlen zu Problemen führen. Es lassen sich viel performantere Algorithmen für diese Matrizenmultiplikation finden, wenn man sie speziell für komplexe Zahlen schreibt und auch auf die Realteile und Imagniärteile der Matrizenelemente zugrifen kann. Besonders tückisch sind aber auch die Rundungsfehler. Um Rundungsfehler zu verringern muss man auf die Kalkulationen Zugriff haben und deren Reihenfolge und Assoziierung steuern können.

Hier ein Beispiel:

a=3.0
b=4.0
c=5e30
d=-5e30

Berechnet man nun (a+b)+(c+d), erhält man 7.0, aber mit (a+c)+(b+d) erhält man 0.0.
In irb (ruby) sieht es etwa so aus:


$ irb
irb(main):001:0> a=3.0
=> 3.0
irb(main):002:0> b=4.0
=> 4.0
irb(main):003:0> c=5e30
=> 5.0e+30
irb(main):004:0> d=-5e30
=> -5.0e+30
irb(main):005:0> (a+b)+(c+d)
=> 7.0
irb(main):006:0> (a+c)+(b+d)
=> 0.0
irb(main):007:0>

Der Fehler kann sich nun natürlich noch beliebig fortpflanzen.

Das ändert aber nichts daran, dass ein auf Polymorphie basierender Ansatz in den allermeisten Fällen der richtige Weg ist, solange man nicht auf die entsprechende Optimierung angewiesn ist.

Es bleibt aber dabei, dass bei einer Aufteilung in Komponenten die richtige Granularität gewählt werden sollte, also keine Microkomponenten, aber auch nicht wahlloses Zusammenfügen von Dingen, die nicht zusammengehören, nur um die richtige Größe der Komponenten zu erreichen.

Share Button

Unicode, UTF-8, UTF-16, ISO-8859-1: Warum ist das so schwierig?

English

Seit etwa 20 Jahren schlagen wir uns mit der Umstellung auf Unicode herum.

Warum ist das so schwierig?

Das größte Problem ist, dass man Dateien nur sehr begrenzt ansieht, wie ihr Inhalt zu interpretieren ist. Wir haben letztlich ein paar Tricks, mit denen man es oft erkennen kann:
Die Endungen funktionieren für häufige und gut definierte Dateitypen, z.B. .jpg oder .png recht gut. In anderen Fällen wird der Inhalt der Datei untersucht und zum Beispiel am Anfang der Datei so etwas wie

#!/usr/bin/ruby

gefunden, woraus geschlossen werden kann, dass das mit ruby ausgeführt werden soll und zwar mit dem Ruby, das unter /usr/bin/ruby steht. Wenn man sich lieber nicht festlegen will und das Ruby haben will, das zuerst im Pfad ( $PATH ) kommt, dann kann man stattdessen

#!/usr/bin/env ruby

verwenden. Das geht leider unter MS-Windows nur unter cygwin, wenn man das cygwin-Ruby verwendet, nicht aber mit dem nativen Win32-Ruby (oder Win64-Ruby).

Nun kommt aber der nächste Schritt und der ist einfach ärgerlich. Welches „encoding“ wird für diese Datei verwendet? Man kann sich auf UTF-8 oder ISO-8859-1 einigen, aber sobald einer im Team vergisst, seinen Editor entsprechend zu konfigurieren, ist Durcheinander abzusehen, weil dann Dateien entstehen, die UTF-8 und ISO-8859-1 (oder noch andere Encodings) miteinander mischen, was dann irgendwann zu obskuren Fehlern führt.

Es war ein großer Fehler, dass bei der Entwicklung von C, Unix und vor allem der libc ein Verständnis von Dateien definiert wurde, das keine Typ-Information für den Dateiinhalt erlaubt. Im Internet haben wir Mime-Header für EMail und WWW-Seiten und alles mögliche andere. Dadurch weiß der Empfänger der Kommunikation stets, wie die empfangen Daten zu interpretieren sind. Ich denke, dass Dateien solche Metainformationen haben sollte, die etwa dem Mime-Header entsprechen. Dann könnte man Dateien beliebig umbenennen, sogar die Endung, ohne dass die Datei dadurch unlesbar würde. Aber als Unix und C entwickelt wurde, wurde auch die libc und die Filesystemkonzepte definiert. Daran haben sich alle Unixe seither gehalten und auch Linux folgt diesen Vorgaben. Aber auch in der MS-Windows-Welt hat man die Betriebssysteme wahrscheinlich in C entwickelt und dabei diese Eigenschaften oder deren Fehlen geerbt. Ich weiß nicht, bis wann man versucht hat, MS-Windows-NT/2000/XP/Vista/7/8… noch auf FAT-Dateisystemen lauffähig zu halten, dabei hätte NTFS mit den multiplen Streams pro Datei eine Werkzeug geschaffen, mit dem man so einen Mime-Typ im zweiten Stream und den eigentlichen Inhalt im ersten Stream speichern könnte. Was aber fehlt ist ein allgemein anerkanntes Regelwerk, das die Nutzung des zweiten Streams für Typinformationen festlegt Aber man verwendet weiterhin Endungen, hofft auf gutes Glück bei Textdateien und analysiert magic-bytes innerhalb der Dateien, um den Typ der Datei und das Encoding zu raten. Linux hat Attribute, in denen man solche Information ablegen kann, aber das bringt nur etwas, wenn es ein Standard ist, den „alle“ kennen und der von jeder Software, die davon betroffen ist, eingehalten wird.

Natürlich haben XML und HTML die Möglichkeit, das Encoding innerhalb der Datei zu definieren. Dummerweise muss man aber das Encoding der Datei schon kennen, um die Zeilen zu lesen, wo drinsteht, welches Encoding die Datei hat. Das ist nicht so schlimm, denn letztlich steht diese Information jeweils am Anfang der Datei und man kann ein paar Encodings durchprobieren, und jeweils unter dieser Annahme anfangen, die Datei zu lesen, bis man es richtig weiß. UTF-16 erkennt man an den beiden Markerbytes am Anfang und dann kann man mit der Annahme UTF-16 lesen, bis man das Encoding, das nun UTF-16 sein muss findet. Andernfalls kann man mit der Annahme, dass es UTF-8 ist, anfangen und muss dann auf das, was man gefunden hat, umstellen. Wenn kein Encoding angegeben ist, ist UTF-8 sowieso der Defaultwert.

Am gefährlichsten ist es, UTF-8 und ISO-8859-1 (oder ähnliche Encodings) zu verwechseln. Da die unteren 128 Zeichen bei beiden gleich sind und zumindest im deutschen Sprachraum doch die überwältigende Mehrheit von Text-Inhalten darstellen, sticht das Problem nicht gleich ins Auge, sondern schleicht sich eher ein, wenn man nicht sauber arbeitet und Dateien mit verschiedenen Encodings zusammenkopiert oder die Dateien den falschen Konversionen aussetzt. Nun werden aber die Umlaute in UTF-8 durch zwei Zeichen codiert, in ISO-8859-1 durch eines. Beim Lesen der Datei unter der falschen Annahme bekommt man also irgendwann mal Zeichenfolgen, die in dem Encoding eigentlich gar nicht vorkommen dürften. Bei UTF-16 ist das einfacher, weil die Dateien dort jeweils mit FFFE oder FEFF anfangen, so dass man einigermaßen sicher UTF-16 an sich und die Bytefolge (niedriges zuerst oder hohes zuerst) erkennen kann. Es gäbe auch eine drei Byte lange Markierung für UTF-8. Obwohl das jede Software verstehen sollte, ist man in der Praxis meistens gezwungen, diese Byte-Sequenz zu entfernen, weil sie die meiste Software verwirrt, zu fehlerhaften Verhalten bringt oder gar zum Absturz bringt.

In der MS-Windows-Welt kommt noch als weiteres Ärgernis hinzu, dass zwar das ganze System mit modernen Encoding arbeiten kann, aber diese schwarzen CMD-Fenster kommen immer noch mit CP-850 oder CP-437 hoch, enthalten also in etwa dieselben Zeichen wie ISO-8859-1, aber an anderen Positionen. Da bekommt man dann schon einmal ein Sigma-Zeichen statt einem „ä“ zu sehen. Diese Incompatibilität innerhalb desselben Systems bringt natürlich Nachteile mit sich.

Links

Share Button