Ausnahmen vs. Regeln in der Usability

written by Martin Häcker on

Heute hatten wir wieder ein schönes Beispiel für Usability unter Windows. Zuerst das Setting. Eine Bekannte hatte auf einem USB-Stick Fotos mitgebracht und diese sollten auf einem Windows-Vista Notebook dann angeschaut werden.

Das ganze wäre dann fast daran gescheitert, dass auf dem USB-Stick ein Trojaner drauf war und sich Windows nach Entdeckung und Beseitigung des Virus (durch irgend ein Third-Party-Programm) standhaft geweigert hat den Stick im Explorer anzuzeigen. Je nachdem in welchem der USB-Stecker man den Stick einsteckte erkannte Windows nicht mal das überhaupt ein USB-Stick eingesteckt war. Aber ich lenke ab, denn das Problem konnte ich mit etwas Windows-Shell dann doch lösen.

Zum Thema: Nachdem der Ordner mit den Bildern im Explorer aufgemacht wurde, ging es nach einem Doppelklick auf das erste Bild los - der Bildbetrachter ging auf und die Slideshow ging los.

Und das ist das Thema: Unter Windows öffnet der Bildbetrachter Bilder anders als sonst Programme Dateien öffnen - implizit wird dort nämlich der ganze Ordner geöffnet, damit man leicht eine Slideshow mit den Bildern starten kann.

Und das hat doch immerhin so gut funktioniert, dass die Slideshows doch zusammen kamen.

Das bringt aber die Abstrakte Frage mit, was passiert wenn solche "Ausnahmen" immer mehr werden. Verstehen die Anwender dann trotzdem noch das allgemeine Prinzip was dahinter liegt? Sind sie in der Lage zu abstrahieren und das Verhalten an anderen Stellen vorherzusagen?

Ich sehe ein Problem darin, dass Computer fĂĽr die meisten Menschen "magisch" sind - man lernt nur die magische Formel, die zu einem konkreten Ziel fĂĽhrt. Dass hinter den 30 Formeln die man mit der Zeit lernt ein Prinzip steht geht dann nur allzu oft verloren.

Zurück zum Beispiel: Am Mac ist es so, dass man, wenn man eine Datei öffnet, dann auch nur diese Datei aufgeht. Und das ist auch so wenn man ein Bild öffnet. Unter Windows ist das anders, denn man öffnet im Bildbetrachter (wehe wenn die Zuordnung verloren geht, weil man ein Bildbearbeitungsprogramm installiert hat) der von sich aus schon die nächsten Bilder kennt und es einem leicht macht eine Slideshow zu starten.

Was ist Besser? Im Beispiel waren relative Laien in der Lage eine Slideshow zu starten - das ist ein Plus. Hätten sie das auch auf einem Mac geschafft?

Wann ist es Besser so eine Ausnahme vom regulären Benutzungs-Konzept zu machen und wann nicht? Für mich ist die Grenze völlig unklar - nur dass es irgendwann den Punkt gibt, wo zu viele Ausnahmen selbst zum Problem werden.

Creating NSNumbers from arbitrary values...

written by Martin Häcker on

... without needing to care which constructor to use this time to get it right.

Well, I was offended by this just long enough, so I sat down and wrote a macro that allows this:

//  Use like this:
    id dict = [NSDictionary dictionary];
    NSNumber *ten = NMMakeNumber(10.0f);
    [dict setObject:ten forKey:@"numberOfFingers"];
    [dict setObject:NMMakeNumber(23) forKey:@"fnord"];
    [dict setObject:NMMakeNumber(YES) forKey:@"wantFries"];

I'ts almost the way Autoboxing works in Java or C#.

This quite eases the pain of creating NSNumbers correctly for me, because it means I don't have to repeat the type of what I am working with quite as often.

Pretty DRY. :)

[source:/open-source/NSNumberCreator/trunk Here's the code to a NSNumber category and a makro that makes this happen.]

Wundervolle Werbung...

written by Martin Häcker on

... fĂĽrs Anschnallen!

Super. :)

Wie definiert man "GlĂĽck haben"?

written by Martin Häcker on

Mein Vorschlag fĂĽr diese Diskussion

Applicationen in den AppStore zu kriegen...

written by Martin Häcker on

... ist schwer.

GlĂĽcklicherweise wurde der Prozess endlich offen gelegt!

Afro Samurai

written by Martin Häcker on

Wunderschönes Comic - gezeichnet und gesprochen von Meistern.

Eine schräge Mischung aus japanischem Samurai-Gemetzel mit western Einflüssen plus Handies und Roboter...

Muss man sehen.

(Mehr Info)

Ein Tool sie zu finden und alle zu binden...

written by Martin Häcker on

... auf die Shell zu ziehen und ins Highlighting zu ziehen. :) (Sorry, ich muss dringend mal wieder Herr der Ringe anschauen)

Mir geht es um grep, der Urgroßvater aller Projektweiten Suchmöglichkeiten. Das kann viel - aber nicht alles.

Zum Beispiel ist es nicht besonders einfach in einem Projekt zu suchen dass SVN für die Versionskontrolle einsetzt. Grep besteht nämlich darauf dass es unbedingt bei -r auch in .svn Verzeichnissen suchen will. Und das beste: es gibt zwar eine Option --exclude - die funktioniert aber nur auf File-Basis. Das heißt man kann damit keine Verzeichnisse (wie z.B. .svn) ausschließen.

Doh.

Die Entwickler sagen dazu dass man doch lieber find . mumbojumbomagic | grep muster benutzen soll. Tja.

Enter Ack. Die Lösung aller Probleme. :)

  • Das Tool sucht standardmäßig rekursiv - d.h. man muss nicht ständig -r angeben.
  • Es ignoriert standardmäßig .svn .hg .git etc. Verzeichnisse - daher muss man nicht ständig irrelevante Resultate ignorieren.
  • Man kann mit switches wie --python oder --objc nur in Dateien dieses Typs suchen.
  • Man hat vollständige UnterstĂĽtzung fĂĽr Perls Regular Expressions auf der Kommandozeile (das Tool ist in Perl geschrieben)
  • Man kann das Tool supereinfach in ~/.ackrc konfigurieren.

Hier meine .ackrc:

$ cat ~./ackrc
--type-set=nib=.nib
--type-add=objc=.mm
--ignore-dir=build

Schick ne?

Hier zu finden.

2008/09/13/13.40

written by Martin Häcker on

FlĂĽssigkeit != FlĂĽssig

Was ich als SpaĂź alles verpasst habe, weil ich sowas noch nicht gesehen habe.

Damit ist sogar endlich geklärt wie es Jesus geschafft hat über das Wasser zu gehen.

:-)

Ach ja, die Erklärung (Wikipedia) und mehr details (Wikipedia) und eine Anleitung zum Selber herstellen.

Have phun!

Sicherheit und Parkhäuser

written by Martin Häcker on

Heute hatte ich das witzige Erlebnis dass wir in ein Parkhaus gefahren sind - und einfach keinen Parkplatz gefunden haben.

Ok, soweit wars noch nicht spannend - aber was ich hoch-interessant fand, ist dass wir dann beim Rausfahren nicht zur Kasse gebeten wurden. Das macht Sinn, denn wer keinen Parkplatz findet, ist natürlich Dreifach angepisst wenn er dann dafür auch noch Zahlen muss. (Und vielleicht während dessen auch noch den Ausgang versperrt.. :)

Ich halte es fĂĽr sehr wahrscheinlich, dass jedes Parkhaus so funktioniert. Cool. Denn das bedeutet, dass man prinzipiell jedes Parkhaus umsonst benutzen kann - man muss sich nur unmittelbar vor dem herausfahren eine neue Marke besorgen.

:)

(Ok, das mag nicht soo leicht sein, aber im Prinzip...)

2008/09/05/19.08

written by Martin Häcker on

Python != Funktionales Programmieren

--- zumindest scheint es so.

Das hier zum Beispiel:

#!/usr/bin/env python
# encoding: utf-8

def counter_generator():
    current_value = 0
    def counter():
        current_value += 1
        return current_value
    return counter

import unittest
class ClosureTest(unittest.TestCase):
    def test_counter(self):
        self.assertEquals(1, counter_generator()())

if __name__ == '__main__':
    unittest.main()

Wenn man das ausführt dann kriegt man den wenig aussagekräftigen Fehler UnboundLocalError: local variable 'current_value' referenced before assignment. Oder mit anderen Worten: "Wiebidde?"

Kommentiert man current_value += 1 aus, funktioniert das ganze (ok, bis auf den dann fehlschlagenden Testcase).

Immerhin kommt man dem Problem dann auf die Spur (ich und Felix haben das leider erst herausgefunden nachdem wir auf #python nachgefragt haben...) Python kann nämlich seine Scopes nicht vernünftig. Das heißt, man kriegt keinen schreibenden Zugriff auf die Variablen eine Funktion weiter draußen - nur lesend. Nur dass die Fehlermeldung natürlich total mist ist.

Grah.

Interessanterweise geht diese Lösung auch nicht

#!/usr/bin/env python
# encoding: utf-8

def counter_generator():
    workaround = object()
    workaround.current_value = 0
    def counter():
        workaround.current_value += 1
        return workaround.current_value
    return counter

import unittest
class ClosureTest(unittest.TestCase):
    def test_counter(self):
        self.assertEquals(1, counter_generator()())

if __name__ == '__main__':
    unittest.main()

FĂĽr hinweise wieso bin ich dankbar.

Vielleicht kriegt Python dass ja irgendwann mal hin. In der Zwischenzeit hilft dieser Workaround:

#!/usr/bin/env python
# encoding: utf-8

class AttributableDict(dict):
    def __getattr__(self, attribute_name):
        return self[attribute_name]
    def __setattr__(self, attribute_name, value):
        self[attribute_name] = value

def counter_generator():
    workaround = AttributableDict()
    workaround.current_value = 0
    def counter():
        workaround.current_value += 1
        return workaround.current_value
    return counter

import unittest
class ClosureTest(unittest.TestCase):
    def test_counter(self):
        self.assertEquals(1, counter_generator()())

if __name__ == '__main__':
    unittest.main()