Archiv für den Monat: Januar 2013

In Zukunft noch mehr „Suchbegriff nicht definiert“ in Piwik?

Google Chrome LogoWie heise in ihrem Newsticker gemeldet hat, wird Chrome 25 standardmäßig mit verschlüsselter Google-Suche arbeiten. Das wird vermutlich zur Folge haben, dass in der Auswertung in Piwik mehrheitlich nur noch „Suchbegriff nicht definiert“ angezeigt wird. Siehe dazu auch meinen Blog-Artikel Zeitgeist 2012 oder Piwik: Suchbegriff nicht definiert bei Redirect 301.

Von der Benutzerseite her gesehen ist dies sicher zu begrüssen, dass nicht jeder zuhört, nach was er gerade sucht. Als Webseiten- / Blog-Betreiber fehlen mir aber Informationen, was der Besucher gesucht hat. Anhand dieser Angaben könnte ich z.B. neuen Inhalt generieren, der die vom Besucher gesuchte Information enthält.

Was aber einen üblen Beigeschmack hinterlässt ist die Vermutung, dass über Google Analytics die Suchbegriffe auch von verschlüsselten Suchabfragen verfügbar sein dürften. Da nützt Google ihre Marktmacht im Suchmaschinenbereich aus, um die Marktanteile im Analytics-Bereich zu sichern.

Wie seht ihr die Situation mit „Suchbegriff nicht definiert“? Begrüsst ihr, dass der Suchbegriff nicht weitergegeben wird oder kämpft ihr mit dem Informationsverlust? Kann jemand bestätigen oder dementieren, dass Google Analytics die Suchbegriffe noch anzeigt?

LinkedList Dojo mit Visual T# – Teil 5

Karate

Image courtesy of „luigi diamanti“ / FreeDigitalPhotos.net


Von den am Anfang des LinkedList Dojo Teil 4 aufgeführten noch fehlenden Implementationen haben wir erst die GetEnumerator()-Funktionen abgehakt. Packen wir nun die noch fehlenden Funktionen und Properties an. Den in Teil 4 implementierten Enumerator wollen wir nun auch nutzen, um einige von diesen Funktionen zu schreiben.

Die Kopie ist besser als das Original

Nehmen wir uns der CopyTo-Methode an. Als erstes die Tests (ich erlaube mir, gleich zwei Tests zu erstellen).


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Dank dem Enumerator ist die Implemetation der Funktion fast schon geschenkt:

1
2
3
4
5
6
7
public void CopyTo(T[] array, int arrayIndex)
{
    foreach (T item in this)
    {
        array[arrayIndex++] = item;
    }
}

Und noch ein paar Test für die Fehlerfälle:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Und die dazugehörige Implementation mit den Überprüfungen:

1
2
3
4
5
6
7
8
9
10
public void CopyTo(T[] array, int arrayIndex)
{
    if (array == null) throw new ArgumentNullException("array");
    if (arrayIndex  array.Length) throw new ArgumentException("The number of elements in the source collection is greater than the available space from arrayIndex to the end of the destination array.");

    foreach (T item in this)
    {
        array[arrayIndex++] = item;
    }
}

Ich möchte da noch etwas hinzufügen

Unter anderem fehlt noch die Funktion Insert. Auch hierzu zuerst wieder die Tests.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Auch hier wieder die Implementation der Funktion, welche die Tests erfüllt.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void Insert(int index, T item)
{
    if (index == Count)
    {
        Add(item);
        return;
    }

    LinkedListElement element = new LinkedListElement();
    element.Element = item;

    if(index == 0)
    {
        element.Next = first;
        first = element;
        return;
    }

    LinkedListElement priorIndex = GetAt(index - 1);
    element.Next = priorIndex.Next;
    priorIndex.Next = element;
}

Wiederum benötigen wir noch Tests für die Fehlerfälle:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Diese führen dann zu der folgenden Implementation der Insert-Funktion.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void Insert(int index, T item)
{
    if (index  Count) throw new ArgumentOutOfRangeException();

    if (index == Count)
    {
        Add(item);
        return;
    }

    LinkedListElement element = new LinkedListElement();
    element.Element = item;

    if(index == 0)
    {
        element.Next = first;
        first = element;
        return;
    }

    LinkedListElement priorIndex = GetAt(index - 1);
    element.Next = priorIndex.Next;
    priorIndex.Next = element;
}

Weg damit

Für die RemoveAt-Funktion implementiere gleich alle Tests auf einmal.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Dann schaue ich, dass alle Tests grün werden.

1
2
3
4
5
6
7
8
9
10
11
12
public void RemoveAt(int index)
{
    if (index = Count) throw new ArgumentOutOfRangeException();
    if (index == 0)
    {
        first = first.Next;
        return;
    }

    LinkedListElement priorIndex = GetAt(index - 1);
    priorIndex.Next = priorIndex.Next.Next;
}

Und wenn wir schon am entfernen sind packen wir die Remove-Funktion auch noch an. Wie gewohnt zuerst die Tests:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Der letzte Test überprüft dabei, dass das erste Auftreten eines Elementes entfernt wird. In der dazu passenden Implementation können wir von bereits bestehenden Funktionen profitieren:

1
2
3
4
5
6
7
8
public bool Remove(T item)
{
    int pos = IndexOf(item);
    if (pos < 0) return false;

    RemoveAt(pos);
    return true;
}

Bitte nicht berühren!

Für das letzte Property kann man sicherlich darüber streiten, ob ein Test nötig ist. Aber der Vollständigkeit halber auch hier zuerst der Test.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Und das implementierte Property IsReadOnly:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Somit ist die Klasse LinkedList fertig implementiert. Doch bevor wir uns wieder dem Tagesgeschäft zuwenden wollen wir noch einen Blick zurück werfen und schauen, was wir gelernt haben und wo noch Potential für Verbesserungen besteht.

Retrospektive

Als erstes stellt sich die Frage: Haben wir das Ziel erreicht?
Das dürfen wir mit gutem Gewissen mit ja beantworten, wir haben eine funktionstüchtige Klasse implementiert, die durch Tests abgesichert ist.

Was aber noch wichtiger ist: Was haben wir auf dem Weg zum Ziel gelernt?
Der Test-First-Ansatz hat mir gut gefallen, auch wenn ich mich manchmal zurückhalten musste, gleich mit der Implementation vorzupreschen, ohne den Test geschrieben zu haben. Einige wenige Male habe ich schon eine zusätzliche Überprüfung eingebaut, bevor ich diese durch einen Test nötig gemacht hatte. Aber wenn es sich um eine einzelne Überprüfung handelt und man den Test gleich nachliefert ist das aus meiner Sicht legitim. Wir wollen ja nicht päpstlicher als der Papst sein…
Was den Einsatz von Visual T# für die Tests anbelangt bin ich nicht so begeistert. Einige Sachen gefallen mir (z.B. die klare Trennung zwischen Vorbereitung, Ausführung und Überprüfung), aber ich frage mich, ob sich der Aufwand lohnt, eine neue Sprache dafür zu lernen. Mit Unit Tests in C# kann man die Tests ebenfalls schreiben und man muss dann nicht andauernd zwischen den beiden Sprachen wechseln. Vielleicht habe ich mich aber auch noch zu wenig mit Visual T# auseinander gesetzt und es verbergen sich noch interessante Konzepte darin, welche die Arbeit mit Unit-Tests vereinfachen oder verbessern. Was aber an Visual T# noch verbessert werden muss ist die Stabilität. Es ist mir einige Male abgestürzt und manchmal hat es unerklärliche Kompilierfehler gemeldet, die mit einem Neustart des Visual Studios behoben werden konnten.
Den Aufwand, die Klasse komplett zu implementieren, hatte ich etwas unterschätzt. Anhand der Properties und Methoden, die IList verlangt, habe ich die Dauer in etwa geschätzt, dabei aber nicht beachtet, dass ich noch einen Enumerator implementieren muss. Dass ich die Blog-Artikel parallel dazu geschrieben habe hat einen zusätzlichen Sprachwechsel erfordert, so dass ich immer zwischen C#, T# und Deutsch wechseln musste. Hier hat mir der Test-First-Ansatz geholfen, bei den einzelnen Schritten fokussiert zu bleiben und nicht in einer Sprache zu weit vorzupreschen und die anderen Sprachen dann mühsam (und fehleranfällig) „nachzuziehen“.
Das einfache Beispiel erlaubt es auch, einmal ein grobes Verhältnis zwischen Test-Code und Produktiv-Code zu schätzen. Sowohl bei der LinkedList als auch beim LinkedListEnumerator ist der Test-Code etwas doppelt so lang wie der Produktiv-Code. Das Verhältnis der Anzahl Test-Methoden gegenüber der Anzahl Properties und Methoden in den beiden Klassen ist dabei noch grösser (circa 2,5 mal so viele Test-Methoden wie produktive Methoden und Properties). Kurzfristig denkende Manager werden sich bei solchen Zahlen vermutlich die Haare raufen, wenn zwei drittel des Codes nicht im produktiven System verwendet wird. Wenn aber dadurch die Fehler, die den Kunden bei seiner Arbeit mit unserer Software behindern, verhindert oder zumindest stark reduziert werden können, ist dieser „unproduktive“ Code schlussendlich ebenso wertvoll wie der Produktiv-Code.

Den entwickelten Code der LinkedList will ich niemandem Vorenthalten. Was für einen produktiven Code noch fehlt sind die XML-Kommentare.

Die Beispiel-Lösung von Stephan Lieser ist in der Juni-Ausgabe erschienen.

Fazit
Test-First-Prinzip erfolgreich angewandt und als gut befunden, die Unit Tests werde ich aber wohl weiterhin mit C# schreiben.

Hat euch dieses Dojo gefallen? Habt ihr ähnliche Erfahrungen mit Visual T# gemacht? Gerne erwarte ich eure Kommentare.

Piwik 1.10 mit Page Overlay Report

Piwik LogoGestern hat das Piwik-Team den Release 1.10.1 veröffentlicht. Die grösste Neuerung dürfte dabei der Page Overlay Report sein.

Mit dem Page Overlay Report kann verfolgt werden, auf welche Elemente auf der Webseite der Besucher geklickt hat. Damit kann man untersuchen, welche Links von den Besuchern öfters angeklickt werden und welche eher weniger beachtet werden. So sieht man vielleicht noch Verbesserungspotential beim Design bzw. bei der Verteilung der Links.
Das Overlay Plugin ist nach der Installation des Updates automatisch aktiviert.

Die zweitgrösste Neuerung dürften die Text Annotations sein. Damit kann man zu einem Datum eine Bemerkung hinzufügen, z.B. wenn ein neuer Artikel veröffentlicht wurde, wenn Änderungen am Design vorgenommen wurden oder auch wenn es zu Ausfällen kam. Dadurch kann auch später noch einfach nachvollzogen werden, wie bestimmte Werte zu Stande kamen oder es lässt sich beobachten, ob eine Designänderung mehr Besucher brachte.
Wie das Overlay Plugin ist auch das Annotation Plugin automatisch aktiv.

Weitere Änderungen im Kurzüberblick:

  • Automatisches Update der GeoIP Daten.
    Zudem wurde die Installation von GeoIP vereinfacht (falls man noch die Standard-Einstellung verwendet).
  • Neuer Social Networks Report.
    Filtert die Verweis-Reports nach den bekannten Social Networks.
  • Neue Seite Systemprüfung in den Einstellungen.
    Erlaubt es dem Super User zu überprüfen, ob die Server-Konfiguration mit Piwik kompatibel ist.
  • Aufruf zur Unterstützung (Donations).
    Piwik ist immer noch ein Open Source Projekt. Um weiterhin so häufig Neuerungen anbieten zu können bittet das Team aber um finanzielle Unterstützung (besonders von Personen/Firmen, die mit dem Einsatz von Piwik Geld verdienen).
  • Besucher können nun anhand des User Agents von der Zählung ausgenommen werden.
  • Das SEO Widget wurde verbessert (zusätzliche Werte und Bugfixes).
  • Verfolgen von wiederkehrenden Besuchern.
  • Anzeige von Längen- und Breitengrad des Besuchers.
  • Neue Parameter im Tracking API

Die Änderungen der Version 1.10 sind im Detail beschrieben. Insgesamt wurden 68 Tickets bearbeitet. Dabei wurden auch Sicherheitslücken geschlossen, deshalb sollte der neue Release auch installiert werden, wenn man die neuen Features nicht verwenden will.

LinkedList Dojo mit Visual T# – Teil 4

Karate

Image courtesy of „luigi diamanti“ / FreeDigitalPhotos.net


Im vierten Teil von “LinkedList Dojo mit Visual T#” nehmen wir uns den im Teil 3 noch fehlenden Implementierungen an. Folgende Methoden werfen immer noch die NotImplementedException:

  • Insert(int index, T item)
  • RemoveAt(int index)
  • CopyTo(T[] array, int arrayIndex)
  • IsReadOnly
  • Remove(T item)
  • GetEnumerator() in den Varianten „Generics“ und „Non-Generics“

Wagen wir und gleich an den wahrscheinlich grössten Brocken, die zwei GetEnumerator()-Methoden. Die Implementierung der Methoden wird dabei nicht das schwierige und aufwändige sein, sonder der Enumerator, der zurückgegeben wird.

Erstellen wir also eine neue Klasse für den Enumerator und im Testprojekt die zugehörigen Tests.

Als erstes die zwei Tests, die den Enumerator verlangen:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Für den nicht generischen Enumerator muss unsere Liste auf das nicht generische Interface gecastet werden, bevor die Funktion aufgerufen werden kann.

Um die Test grün werden zu lassen benötigen wir nun die Implementation des Enumerators. Ich habe mich dabei entschieden, eine eigene Klasse auf der selben Ebene wie die Liste zu erstellen und keine verschachtelte Klasse.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
namespace RolandBaer.LinkedList
{
    public class LinkedListEnumerator : IEnumerator
    {
        public T Current
        {
            get { throw new NotImplementedException(); }
        }

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        object System.Collections.IEnumerator.Current
        {
            get { throw new NotImplementedException(); }
        }

        public bool MoveNext()
        {
            throw new NotImplementedException();
        }

        public void Reset()
        {
            throw new NotImplementedException();
        }
    }
}

Jetzt noch die beiden GetEnumerator-Methoden implementieren:

1
2
3
4
5
6
7
8
9
public IEnumerator GetEnumerator()
{
    return new LinkedListEnumerator();
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
    return GetEnumerator();
}

Da das generische Interface das nicht-generische beinhaltet kann in der nicht-generischen Methode auf die generische Methode zurückgegriffen werden. So habe ich schon eine Redundanz verhindert.

Durchnummerieren!

Viel sinnvolles können wir aber mit der Implemetation des Enumerators noch nicht anfangen. Schreiben wir deshalb ein paar Tests, die den Enumerator testen.

Als erstes testen wir, ob MoveNext false zurückliefert, wenn wir eine leere Liste haben. Wie wir die Liste übergeben, haben wir noch gar nicht überlegt, aber wenn keine Liste bekannt ist muss sicher false zurückgegeben werden.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Um den Test grün werden zu lassen müssen wir MoveNext anpassen.

1
2
3
4
public bool MoveNext()
{
    return false;
}

Wie gesagt, die Liste muss dem Enumerator auf irgend eine Art bekannt gemacht werden. Ich bevorzuge in solchen Fällen den Konstruktor, da ich so nicht ein zusätzliches Attribut einführen muss. Der Compiler verhindert auch, dass ich vergesse, die Liste anzugeben wenn der Default-Konstruktor fehlt.

Zuerst aber wieder der Test, der das ganze nötig macht.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Der erste Test ist der schon vorher eingeführte Test, erweitert um den Parameter im Konstruktor. Der zweite Test ist der neue Test, welcher einen Zugriff auf die Liste verlangt. Im LinkedListEnumerator muss der Konstruktor und eine Variable für die Liste hinzugefügt werden und MoveNext wird angepasst:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class LinkedListEnumerator : IEnumerator
{
    LinkedList list;

    public LinkedListEnumerator(LinkedList list)
    {
       this.list = list;
    }

    public bool MoveNext()
    {
        return list.Count > 0;
    }

    // (...)
}

GetEnumerator in der LinkedList muss auch noch angepasst werden, wir übergeben dem Konstruktor einfach this und schon kompiliert wieder alles. Und die Test sind auch wieder alle grün.

Den zweiten Test passe ich nun noch an, so dass eine vernünftige Implementation von MoveNext nötig wird.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Für die Implementierung sind noch ein paar Umbauarbeiten nötig, doch zuerst die neue Version von MoveNext:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
private LinkedListElement current;

public bool MoveNext()
{
    if(list.Count == 0) return false;

    if (current == null)
    {
        current = list.GetAt(0);
        return true;
    }

    current = current.Next;
    return current != null;
}

Neben der oben gezeigten neuen Membervariable in der Klasse LinkedListEnumerator verschieben wir auch die Klasse LinkedListElement, welche früher eine geschachtelte Klasse war, in den normalen Namespace und deklarieren sie als internal. Die Methode GetAt(int) deklarieren wir auch als internal, so dass der Enumerator sie verwenden kann.

Dank der Unit-Tests gehen wir bei diesen Umbauarbeiten kein Risiko ein, was wir auch jederzeit überprüfen können.

Und ja, ich habe wieder zu viel implementiert, also noch einen allgemeineren Test, um alle Abläufe durchzugehen.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Faster, Faster!

Den Rest des Enumerators erledigen wir im Schnellzugtempo. Ein paar Tests und nur noch seht wenig zu implementieren.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Und hier die dazugehörigen Implementationen. Für Dispose habe ich keinen Test geschrieben, da Dispose auch nichts macht. Wenn man mit Sicherheit verhindern will, dass noch die NotImplementedException geworfen wird, kann man einen Test schreiben, es würde aber spätestens auffallen, wenn man die Liste ein erstes mal mit foreach verwendet.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public T Current
{
    get
    {
        if (current == null) { throw new InvalidOperationException(); }
        return current.Element;
    }
}

object System.Collections.IEnumerator.Current
{
    get
    {
        return Current;
    }
}

public void Dispose()
{
}

public void Reset()
{
    current = null;
}

Die Tests benötigen noch ein Refactoring und der Konstruktor des Enumerators sollte nicht zugänglich sein, da nur die Liste den passenden Enumerator erstellen soll. Nach den Änderungen sieht dann die Testklasse wie folgt aus.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Den Enumerator haben wir nun, die restlichen Funktionen fehlen aber immer noch. Wir nehmen uns diesen im fünften Teil an.

LinkedList Dojo mit Visual T# – Teil 3

Karate

Image courtesy of „luigi diamanti“ / FreeDigitalPhotos.net


Dies ist der dritte Teil von „LinkedList Dojo mit Visual T#“. In dieser Übungsaufgabe implementieren wir eine verknüpfte Liste nach dem Test-First Ansatz mit Hilfe von Visual T#.

Am Ende von Teil 2 habe ich ja bereits angekündigt, dass als Erstes ein Refactoring ansteht. Dabei werden wir nicht nur die LinkedList überarbeiten sondern auch die dazugehörigen Tests (ohne natürlich die Test-Funktionalitäten zu ändern).

Was riecht hier so streng?

Code Smell: Suchschleifen

Im Indexer haben wir zwei mal dieselbe Suchschleife, einmal im get- und einmal im set-Block. Diese Schleife extrahieren wir in eine eigene Methode, die wir dann aufrufen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public T this[int index]
{
    get
    {
        if (index >= Count) throw new ArgumentOutOfRangeException();

        LinkedListElement temp = GetAt(index);

        return temp.Element;
    }
    set
    {
        if (index >= Count) throw new ArgumentOutOfRangeException();

        LinkedListElement temp = GetAt(index);

        temp.Element = value;
    }
}

private LinkedListElement GetAt(int index)
{
    LinkedListElement temp = first;

    for (int i = 0; i < index; i++)
    {
        temp = temp.Next;
    }
    return temp;
}

Wieder alle Tests ausführen und beruhigt sehen, dass immer noch alle Tests funktionieren.

Code Smell: Initialisierung in Tests

In jedem Test wird die Liste neu erzeugt. Dies kann in eine Methode ausgelagert werden, den sogenannten testcontext. Der Codeabschnitt vor dem runtest entspricht dabei dem TestInitialize- bzw. dem SetUp-Attribut, der Codeabschnitt nach dem runtest dem TestCleanup- bzw. dem TearDown-Attribut beim Microsoft Unit Test Tool bzw. bei NUnit.
Das sieht dann wie folgt aus (nur ein Test als Beispiel).

testclass LinkedListTest for LinkedList
{
    LinkedList sut;
       
    testcontext
    {
        test
        {
            sut = new LinkedList ();
            runtest;
        }
    }
       
    test Count get
    {
        runtest int count = sut.Count;

        assert count == 0;
    }
    (...)
}

Die beiden schlimmsten Code Smells sind behoben, aber die zwei Schleifen in Count und Add riechen auch noch ein wenig. Ich erlaube mir aber, die Nase noch ein bisschen zuzuklemmen und noch etwas Funktionalität hinzuzufügen.

Leeren Sie Ihren Kopf

Als erstes möchte ich die Liste leeren können, so dass ich wieder neue Elemente hinzufügen kann.


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Der assert-Ausdruck vor dem runtest ist eine kleine Zusatzsicherheit, dass die Liste auch Elemente enthält bevor wir sie löschen. Das ist erlaubt und auch vernünftig, da so auch wirklich das getestet wird, was man testen will. Der Test schlägt natürlich fehl, da Clear noch die Standard-Implementation enthält, die eine Exception wirft. Lassen wir den Test grün werden.

1
2
3
4
public void Clear()
{
    first = null;
}

Ganz nach dem Motto nach uns die Sintflut hängen wir einfach das erste Element der verketteten Liste ab und wir sind die Liste los. Der Garbage Collector kann sich dann um die Überreste kümmern.

Haben Sie ..?

Da dies so einfach war fügen wir noch eine Funktionalität mehr hinzu, die Methode Contains(). Wiederum zuerst den Test, der dann fehlschlägt:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Und die einfachste Implementation, die den Test erfüllt:

1
2
3
4
public bool Contains(T item)
{
    return true;
}

Ein zweiter Test soll den Fall abdecken, dass das Element nicht in der Liste vorhanden ist:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Natürlich kann die Überprüfung auch mit


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

erfolgen.

Hier die (nun sinnvolle) Implementierung, die den neuen Test erfüllt, ohne die anderen Tests fehlschlagen zu lassen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public bool Contains(T item)
{
    LinkedListElement temp = first;

    if (EqualityComparer.Default.Equals(temp.Element, item))
        return true;
    while (temp.Next != null)
    {
        temp = temp.Next;
        if (EqualityComparer.Default.Equals(temp.Element,item))
            return true;
    }

    return false;
}

Das Konstrukt in der if-Abfrage ist dem Umstand geschuldet, dass unsere LinkedList sowohl mit Value- als auch mit Reference-Typen funktionieren soll. Weiter Infos sind z.B. bei StackOverflow zu finden.

Bei der Implementation dieser Funktion habe ich gemerkt, dass noch nicht beide Randbedingungen überprüft werden. Also fügen wir noch einen zusätzlichen Test hinzu:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Dieser Test ist von Anfang an grün, wir haben unsere Implementation also richtig gemacht.

Wo finde ich ..?

Verwandt mit Contains() scheint IndexOf() zu sein. Nehmen wir uns diese Implementation auch noch gleich vor. Wie immer zuerst der Test. Wir übernehmen gleich die drei Tests von Contains() und passen sie an:


GeSHi Error: GeSHi could not find the language tsharp (using path /home/rolandba/public_html/blog/wp-content/plugins/codecolorer/lib/geshi/) (code 2)

Natürlich schlagen alle drei Tests fehl. Nehmen wir nun auch den Code von Contains() und passen ihn für IndexOf() an:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public int IndexOf(T item)
{
    int pos = 0;
    LinkedListElement temp = first;

    if (EqualityComparer.Default.Equals(temp.Element, item))
        return pos;
    while (temp.Next != null)
    {
        temp = temp.Next;
        pos++;
        if (EqualityComparer.Default.Equals(temp.Element, item))
            return pos;
    }

    return -1;
}

Mit minimalen Anpassungen sind wieder alle Tests grün. Aber wir haben wieder einen Code Smell, den wir auch gleich beheben:

1
2
3
4
public bool Contains(T item)
{
    return IndexOf(item) > -1;
}

Contains() überprüft nur noch, ob IndexOf() einen gültigen Index zurück gibt. Und dieses Refactoring hat nichts kaputt gemacht, es sind immer noch alle Tests grün.

Immer noch warten einige Methoden auf ihre Implementation. Diesen werden wir uns im vierten Teil annehmen.

Zeitgeist 2012

Wie schon vor einem Jahr veröffentliche ich hier ein paar Statistiken aus meinen Piwik-Analysen des vergangenen Jahres.

Suchbegriffe

Dieses Jahr war ein Suchbegriff sowohl auf der Webseite als auch auf dem Blog klarer Spitzenreiter: „Suchbegriff nicht definiert“. Infos, woher diese Ansammlung herkommt ist z.B. bei Redirect301 zu finden. Dieser Suchbegriff wird aber in der folgenden Tabelle nicht aufgeführt, da er keinerlei Nutzwert bietet.

Rang Webseite Blog
1 nsis tutorial slippery when wet
2 nsis slippery
3 nsis create directory nsis tutorial deutsch

Browser

Gegenüber 2011 ist im 2012 der Marktanteil des Firefox zurückgegangen, besonders beim Blog. Dort sind Chrome und Safari stärker geworden, während bei der Webseite der Internet Explorer einige Anteile zurückerobern konnte.

Browser-Marktanteil Web 2012

Browser-Marktanteil Web 2012

Browser-Marktanteil Blog 2012

Browser-Marktanteil Blog 2012

Browser Webseite (%) Blog (%)
Firefox 56 37
Internet Explorer 21 23
Chrome 15 29
Opera 5 2
Safari 2 7
Iron, Andere 1 1

Suchmaschinen

Bei der Liste der wichtigsten Suchmaschinen hat sich nicht viel geändert, nur beim Blog ist Bing Images auf Kosten von Bing auf den dritten Rang hochgerutscht:

Rang Webseite Blog
1 Google Google
2 Bing Google Images
3 Yahoo! Bing Images

Aber die Google-Übermacht besteht weiterhin, wie in den folgenden beiden Grafiken zu sehen ist:

Suchmaschinen-Marktanteil Web 2012

Suchmaschinen-Marktanteil Web 2012

Suchmaschinen-Marktanteil Blog 2012

Suchmaschinen-Marktanteil Blog 2012

Verweise

Und zum Abschluss wiederum die Liste der wichtigsten Domänen, die auf meine Webseiten verlinken:

Rang Webseite Blog
1 nsis.sourceforge.net www.rolandbaer.ch
2 de.wikipedia.org www.google.com
3 forum.computerbild.de m.facebook.com
4 blog.rolandbaer.ch www.facebook.com
5 www.pcfreunde.de www.google.de

Fazit

Grosse Veränderungen gegenüber dem letzten Jahr gab es nicht, es sind immer noch die selben Themen, Suchmaschinen und verweisende Seiten, welche die Besucher auf meine Webseiten bringen. Nur bei den Browsern gab es einige Verschiebungen und hier sind wohl auch im 2013 am ehesten Veränderungen zu erwarten.