SlideShare a Scribd company logo
1 of 13
Test Driving developing software
Laat ik me even voorstellen. Mijn naam is Wilbert van Dolleweerd, application developer bij Professional
Services. Ik ben werkzaam bij het Command en Control Support Center in Ede. Hier wordt software
geschreven voor Command en Control applicaties voor de Nederlandse landmacht. Zie
http://www.c2sc.org voor meer informatie.

Vanuit mijn CSC global role en de werkzaamheden bij de klant zullen jullie vanaf nu regelmatig artikelen
van mijn hand terugvinden in de PS Monthly over het onderwerp software development in de breedste zin
des woords.

Deze keer wil ik jullie aan de hand van een klein stuk code laten zien hoe ik gebruik maak van Test Driven
Design (TDD). Hierbij hoop ik te laten zien hoe je door het schrijven van tests voordat je aan de
functionaliteit begint, in kleine iteraties tot een goed resultaat komt. De bedoeling van het schrijven van
de tests vooraf is dat je gedwongen wordt om na te denken vanuit het oogpunt van de gebruiker van de
code.

Voor een bepaalde functionaliteit heb ik behoefte aan een circulair gelinkte lijst.




                                        Afbeelding 1 - Circulaire gelinkte lijst

Ik heb de volgende functionele eisen:

    •   Ik wil een object kunnen toevoegen. Zodra ik een object toevoeg moet het achter het laatst
        geplaatste object komen te staan.

    •   Ik wil van voor naar achter door deze lijst kunnen lopen. Tijdens het doorlopen van de lijst moet ik
        het huidige object kunnen opvragen. Als ik aan het einde van de lijst kom moet er verwezen
        worden naar het begin van de lijst.

    •   Ik wil weten hoeveel objecten er in een lijst zitten.
Het .NET framework heeft een redelijk aantal collection classes, maar geen circulair gelinkte lijst. Omdat ik
een collection wil hebben die volledig is toegespitst op mijn wensen, besluit ik zelf een eigen collection
class te schrijven.

Overigens besef ik me dat het .NET framework eigen interfaces bied voor het implementeren van
collection classes. Ik kiest er bewust voor om deze niet te gebruiken om de aandacht niet af te leiden van
datgene waar het artikel om draait: Test Driven Design.

Ik wil het concept van een element in de lijst vertegenwoordigen door een Node class. Dit Node object
bevat het object wat ik in de lijst wil stoppen en de link naar het volgende element.

Als eerste schrijf ik een unittest waarmee ik test of ik een object in een Node object kwijt kan.

[Test]
public void TestNodeValue()
{
        Node<String> node = new Node<String>(quot;Fooquot;);
        Assert.AreEqual(node.Value, quot;Fooquot;);
}

De Node class wordt via de constructor gevuld met het object wat ik erin wil stoppen. Vervolgens
controleer ik of de Value property van de Node gelijk is aan het object wat via de constructor is binnen
gekomen. Deze test werkt dus nog niet! Sterker nog, dit zal niet eens compileren omdat ik nog geen Node
class heb.

Vervolgens ga ik de Node class uitwerken.

public class Node<T>
{
         private readonly T _item;

        public Node(T item)
        {
                _item = item;
        }

        public T Value
        {
                get { return _item; }
        }
}

Hierna werkt mijn unittest. Mijn class is echter nog niet compleet. Ik heb namelijk nog geen verwijzing
naar een volgende Node. Het idee is om met een Next property te verwijzen naar de volgende Node.
[Test]
public void TestNextNode()
{
        Node<String> nodeLeft = new Node<string>(quot;Fooquot;);
         Node<String> nodeRight = new Node<string>(quot;Barquot;);

         nodeLeft.Next = nodeRight;
         Assert.AreEqual(nodeLeft.Next, nodeRight);
}

In de nieuwe unittest maak ik twee Node objecten aan, waarbij ik de een laat verwijzen naar de ander.
Vervolgens ga ik controleren of deze objecten overeenkomen. Nu ik de test heb wijzig ik de Node class als
volgt:

public class Node<T>
{
         private Node<T> _next;
         private readonly T _item;

         public Node(T item)
         {
                 _item = item;
         }

         public Node<T> Next
         {
                 get { return _next; }
                 set { _next = value; }
         }

         public T Value
         {
                 get { return _item; }
         }
}

So far, so good. Na deze wijzigingen werkt mijn unittest. Mijn Node class lijkt ergens op. Nu moet ik nog
iets hebben waar ik de nodes in kwijt kan. Als ik kijk naar mijn eisen lijkt het me handig om iets te bouwen
waarmee ik nodes kan toevoegen en waarmee ik de huidige node kan opvragen.

Ik schrijf een unittest voor het toevoegen van een enkele node.

[Test]
public void AddSingleNode()
{
        CircularLinkedList<String> linkedList = new CircularLinkedList<String>();

        linkedList.Add(quot;Fooquot;);
        Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;);
}

Je ziet dat ik dus de aanname maak dat ik een class met de naam CircularLinkedList ga schrijven. Deze class
heeft een Add methode om objecten (Nodes) toe te voegen. Vervolgens kan ik de huidige Node uitlezen
via een Current property.

De eerste opzet:

public class CircularLinkedList<T>
{
         private Node<T> _current;

        public void Add(T item)
        {
                Node<T> insert = new Node<T>(item);
                _current = insert;
        }

        public Node<T> Current
        {
                get { return _current; }
        }
}


Ik heb een Add method geschreven die alleen werkt als er nog geen nodes aanwezig zijn. Dit is prima, het
doel is om iets te schrijven wat ervoor zorgt dat de unittest slaagt.

Een volgende eis is dat er bijgehouden wordt hoeveel items er in een collection zitten. Ik breid mijn
unittest als volgt uit:

[Test]
public void AddSingleNode()
{
        CircularLinkedList<String> linkedList = new
                CircularLinkedList<String>();
linkedList.Add(quot;Fooquot;);

        Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;);
        Assert.AreEqual(linkedList.Count, 1);
}

Om aan de unittest te voldoen voeg ik een Count property toe.

public class CircularLinkedList<T>
{
        private Node<T> _current;
        private int _count;

        public void Add(T item)
        {
                Node<T> insert = new Node<T>(item);

                 _current = insert;
                 _count++;
        }

        public int Count
        {
                 get { return _count; }
        }

        public Node<T> Current
        {
                get { return _current; }
        }
}


De volgende eis is dat het een circulaire lijst wordt. Als ik door de lijst loop wil ik aan het einde van de lijst
terugspringen naar het begin van de lijst. Hiervoor heb ik een method nodig waarmee ik naar de volgende
node kan springen.

Ik breid mijn unittest uit met een MoveNext method. Na de MoveNext zou de Current property nog steeds
naar dezelfde node moeten verwijzen omdat we op dit moment slechts met 1 node werken.

[Test]
public void AddSingleNode()
{
CircularLinkedList<String> linkedList = new
                CircularLinkedList<String>();

        linkedList.Add(quot;Fooquot;);

        Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;);
        Assert.AreEqual(linkedList.Count, 1);
        linkedList.MoveNext();
        Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;);
}

Ik wijzig CircularLinkedList om de MoveNext methode uit te werken. Ook zorg ik ervoor dat de Next
property van een Node gevuld wordt in de Add methode.

public class CircularLinkedList<T>
{
        private Node<T> _current;
        private int _count;

        public void Add(T item)
        {
                Node<T> insert = new Node<T>(item);

                _current = insert;
                _current.Next = _current;
                _count++;
        }

        public int Count
        {
                 get { return _count; }
        }

        public Node<T> Current
        {
                get { return _current; }
        }

        public void MoveNext()
        {
                _current = _current.Next;
}
}


Ik heb nu een CircularLinkedList die werkt voor mijn eisen. Het enige nadeel is dat het slechts voor een
enkele node werkt. Ik besluit mijn testen uit te breiden met het toevoegen van twee nodes.

[Test]
public void AddTwoNodes()
{
        CircularLinkedList<String> linkedList = new
                CircularLinkedList<String>();

        linkedList.Add(quot;Fooquot;);
        linkedList.Add(quot;Barquot;);

        Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;);
        Assert.AreEqual(linkedList.Count, 2);

        linkedList.MoveNext();
        Assert.AreEqual(linkedList.Current.Value, quot;Barquot;);

        linkedList.MoveNext();
        Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;);
}

Deze test faalt omdat de CircularLinkedList nog niet overweg kan met twee nodes.

public class CircularLinkedList<T>
{
        private Node<T> _current;
        private Node<T> _first;
        private int _count;

        public void Add(T item)
        {
                Node<T> insert = new Node<T>(item);

                if (_count == 0)
                {
_first = insert;
                        _first.Next = _first;

                        _current = _first;
                        _count++;

                        return;
                }
                if (_count == 1)
                {
                        _first.Next = insert;
                        insert.Next = _first;

                        _count++;
                        return;
                }
        }

        public int Count
        {
                 get { return _count; }
        }

        public Node<T> Current
        {
                get { return _current; }
        }

        public void MoveNext()
        {
                _current = _current.Next;
        }
}

Ik maak bij de Add methode nu onderscheid tussen 1 of 2 nodes. Omdat ik bij wil kunnen houden wat de
eerste node is introduceer ik een _first Node<T> variabele. Mijn unittest werkt nu voor 1 en voor 2 nodes.

Leuk en aardig, maar ik wil natuurlijk dat het gaat werken voor alle aantallen nodes. Om dit te kunnen
maken voeg ik nog een unittest toe. Ik ga ervan uit dat als ik het werkend heb voor 3 nodes,dat ik het dan
werkend heb voor een willekeurig aantal nodes.

[Test]
public void AddThreeNodes()
{
       CircularLinkedList<String> linkedList = new
               CircularLinkedList<String>();

       linkedList.Add(quot;Fooquot;);
       linkedList.Add(quot;Barquot;);
       linkedList.Add(quot;Fuzquot;);

       Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;);
       Assert.AreEqual(linkedList.Count, 3);

       linkedList.MoveNext();
       Assert.AreEqual(linkedList.Current.Value, quot;Barquot;);

       linkedList.MoveNext();
       Assert.AreEqual(linkedList.Current.Value, quot;Fuzquot;);

       linkedList.MoveNext();
       Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;);
}

Ik pas de Add method aan om met meerdere nodes om te kunnen gaan.

public class CircularLinkedList<T>
{
         private Node<T> _current;
       private Node<T> _first;
       private int _count;

       public void Add(T item)
       {
               Node<T> insert = new Node<T>(item);

               if (_count == 0)
               {
                       _first = insert;
                       _first.Next = _first;
_current = _first;
                    _count++;

                    return;
            }

            if (_count == 1)
            {
                    _first.Next = insert;
                    insert.Next = _first;

                    _count++;
                    return;
            }

            // > 2 nodes present
            Node<T> iterate = _first;
            for (int i=1; i < _count; i++)
            {
                    iterate = iterate.Next;
            }
            iterate.Next = insert;
            insert.Next = _first;
            _count++;
    }

    public int Count
    {
             get { return _count; }
    }

    public Node<T> Current
    {
            get { return _current; }
    }

    public void MoveNext()
    {
            _current = _current.Next;
    }
}
Je ziet dat ik bij meer dan 2 nodes aan de hand van _count door de lijst loop. Zodra ik op het einde van de
lijst ben voeg ik de nieuwe node toe en verbindt deze weer met de eerste node.

Alhoewel mijn unittest nu werkt ben ik nog niet tevreden. Voor het toevoegen van een node moet ik nu de
hele lijst doorlopen. De Add method is nu een O(n) operatie geworden. (De snelheid van de method is nu
afhankelijk van het aantal elementen in de lijst).

Ik besluit mijn code te refactoren. Daarbij kan ik mijn bestaande unittests gebruiken om te kijken of de
functionaliteit blijft werken.

public class CircularLinkedList<T>
{
        private Node<T> _current;
        private Node<T> _first;
        private Node<T> _last;
        private int _count;

        public void Add(T item)
        {
                Node<T> insert = new Node<T>(item);

                if (_count == 0)
                {
                         _first = insert;
                         _first.Next = _first;

                         _current = _first;
                         _last = _first;
                         _count++;

                         return;
                }

                if (_count == 1)
                {
                         _last.Next = insert;
                         insert.Next = _first;

                         _last = insert;
_count++;
                         return;
                }

                _last.Next = insert;
                insert.Next = _first;

                _last = insert;

                _count++;
        }

        public int Count
        {
                 get { return _count; }
        }

        public Node<T> Current
        {
                get { return _current; }
        }

        public void MoveNext()
        {
                _current = _current.Next;
        }
}


Ik heb een _last Node<T> geintroduceerd. Hiermee hou ik nu bij wat de laatste node is die wordt
toegevoegd. Hierdoor kan ik op een vrij eenvoudige manier de nieuwe node toevoegen, namelijk achter
de laatste. Add is nu van een O(n) operatie veranderd in een O(1) operatie. (De snelheid van de Add
methode is nu constant).

Mijn unittesten blijven werken wat voor mij het bewijs is dat mijn refactoring de functionaliteit niet laat
omvallen.

Het valt me wel op dat de code voor het toevoegen van de tweede node en het toevoegen van alle nodes
daarna in de Add methode wel erg veel op elkaar lijkt. Volgens mij kan ik die code eenvoudiger maken.

public void Add(T item)
{
        Node<T> insert = new Node<T>(item);
// No nodes present
        if (_count == 0)
        {
                _first = insert;
                _first.Next = _first;

                _current = _first;
                _last = _first;
                _count++;

                return;
        }

        // One or more nodes already present.
        _last.Next = insert;
        insert.Next = _first;

        _last = insert;
        _count++;
}


Je ziet dat ik de conditie voor 1 node en 2 nodes of meer heb kunnen combineren. Mijn unittests blijven
werken wat voor mij weer een bevestiging is dat de implementatie wel is aangepast, maar niet de
functionaliteit.

De CircularLinkedList werkt nu voor de in het begin gestelde eisen. Ik hoop dat ik op deze manier heel in
het kort heb kunnen laten zien hoe een typische TDD-sessie kan verlopen.

More Related Content

Featured

Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
 
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellGood Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Saba Software
 

Featured (20)

Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
Unlocking the Power of ChatGPT and AI in Testing - A Real-World Look, present...
 
12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work12 Ways to Increase Your Influence at Work
12 Ways to Increase Your Influence at Work
 
ChatGPT webinar slides
ChatGPT webinar slidesChatGPT webinar slides
ChatGPT webinar slides
 
More than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike RoutesMore than Just Lines on a Map: Best Practices for U.S Bike Routes
More than Just Lines on a Map: Best Practices for U.S Bike Routes
 
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
Ride the Storm: Navigating Through Unstable Periods / Katerina Rudko (Belka G...
 
Barbie - Brand Strategy Presentation
Barbie - Brand Strategy PresentationBarbie - Brand Strategy Presentation
Barbie - Brand Strategy Presentation
 
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them wellGood Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
Good Stuff Happens in 1:1 Meetings: Why you need them and how to do them well
 

Test Driven Development

  • 1. Test Driving developing software Laat ik me even voorstellen. Mijn naam is Wilbert van Dolleweerd, application developer bij Professional Services. Ik ben werkzaam bij het Command en Control Support Center in Ede. Hier wordt software geschreven voor Command en Control applicaties voor de Nederlandse landmacht. Zie http://www.c2sc.org voor meer informatie. Vanuit mijn CSC global role en de werkzaamheden bij de klant zullen jullie vanaf nu regelmatig artikelen van mijn hand terugvinden in de PS Monthly over het onderwerp software development in de breedste zin des woords. Deze keer wil ik jullie aan de hand van een klein stuk code laten zien hoe ik gebruik maak van Test Driven Design (TDD). Hierbij hoop ik te laten zien hoe je door het schrijven van tests voordat je aan de functionaliteit begint, in kleine iteraties tot een goed resultaat komt. De bedoeling van het schrijven van de tests vooraf is dat je gedwongen wordt om na te denken vanuit het oogpunt van de gebruiker van de code. Voor een bepaalde functionaliteit heb ik behoefte aan een circulair gelinkte lijst. Afbeelding 1 - Circulaire gelinkte lijst Ik heb de volgende functionele eisen: • Ik wil een object kunnen toevoegen. Zodra ik een object toevoeg moet het achter het laatst geplaatste object komen te staan. • Ik wil van voor naar achter door deze lijst kunnen lopen. Tijdens het doorlopen van de lijst moet ik het huidige object kunnen opvragen. Als ik aan het einde van de lijst kom moet er verwezen worden naar het begin van de lijst. • Ik wil weten hoeveel objecten er in een lijst zitten.
  • 2. Het .NET framework heeft een redelijk aantal collection classes, maar geen circulair gelinkte lijst. Omdat ik een collection wil hebben die volledig is toegespitst op mijn wensen, besluit ik zelf een eigen collection class te schrijven. Overigens besef ik me dat het .NET framework eigen interfaces bied voor het implementeren van collection classes. Ik kiest er bewust voor om deze niet te gebruiken om de aandacht niet af te leiden van datgene waar het artikel om draait: Test Driven Design. Ik wil het concept van een element in de lijst vertegenwoordigen door een Node class. Dit Node object bevat het object wat ik in de lijst wil stoppen en de link naar het volgende element. Als eerste schrijf ik een unittest waarmee ik test of ik een object in een Node object kwijt kan. [Test] public void TestNodeValue() { Node<String> node = new Node<String>(quot;Fooquot;); Assert.AreEqual(node.Value, quot;Fooquot;); } De Node class wordt via de constructor gevuld met het object wat ik erin wil stoppen. Vervolgens controleer ik of de Value property van de Node gelijk is aan het object wat via de constructor is binnen gekomen. Deze test werkt dus nog niet! Sterker nog, dit zal niet eens compileren omdat ik nog geen Node class heb. Vervolgens ga ik de Node class uitwerken. public class Node<T> { private readonly T _item; public Node(T item) { _item = item; } public T Value { get { return _item; } } } Hierna werkt mijn unittest. Mijn class is echter nog niet compleet. Ik heb namelijk nog geen verwijzing naar een volgende Node. Het idee is om met een Next property te verwijzen naar de volgende Node.
  • 3. [Test] public void TestNextNode() { Node<String> nodeLeft = new Node<string>(quot;Fooquot;); Node<String> nodeRight = new Node<string>(quot;Barquot;); nodeLeft.Next = nodeRight; Assert.AreEqual(nodeLeft.Next, nodeRight); } In de nieuwe unittest maak ik twee Node objecten aan, waarbij ik de een laat verwijzen naar de ander. Vervolgens ga ik controleren of deze objecten overeenkomen. Nu ik de test heb wijzig ik de Node class als volgt: public class Node<T> { private Node<T> _next; private readonly T _item; public Node(T item) { _item = item; } public Node<T> Next { get { return _next; } set { _next = value; } } public T Value { get { return _item; } } } So far, so good. Na deze wijzigingen werkt mijn unittest. Mijn Node class lijkt ergens op. Nu moet ik nog iets hebben waar ik de nodes in kwijt kan. Als ik kijk naar mijn eisen lijkt het me handig om iets te bouwen waarmee ik nodes kan toevoegen en waarmee ik de huidige node kan opvragen. Ik schrijf een unittest voor het toevoegen van een enkele node. [Test]
  • 4. public void AddSingleNode() { CircularLinkedList<String> linkedList = new CircularLinkedList<String>(); linkedList.Add(quot;Fooquot;); Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;); } Je ziet dat ik dus de aanname maak dat ik een class met de naam CircularLinkedList ga schrijven. Deze class heeft een Add methode om objecten (Nodes) toe te voegen. Vervolgens kan ik de huidige Node uitlezen via een Current property. De eerste opzet: public class CircularLinkedList<T> { private Node<T> _current; public void Add(T item) { Node<T> insert = new Node<T>(item); _current = insert; } public Node<T> Current { get { return _current; } } } Ik heb een Add method geschreven die alleen werkt als er nog geen nodes aanwezig zijn. Dit is prima, het doel is om iets te schrijven wat ervoor zorgt dat de unittest slaagt. Een volgende eis is dat er bijgehouden wordt hoeveel items er in een collection zitten. Ik breid mijn unittest als volgt uit: [Test] public void AddSingleNode() { CircularLinkedList<String> linkedList = new CircularLinkedList<String>();
  • 5. linkedList.Add(quot;Fooquot;); Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;); Assert.AreEqual(linkedList.Count, 1); } Om aan de unittest te voldoen voeg ik een Count property toe. public class CircularLinkedList<T> { private Node<T> _current; private int _count; public void Add(T item) { Node<T> insert = new Node<T>(item); _current = insert; _count++; } public int Count { get { return _count; } } public Node<T> Current { get { return _current; } } } De volgende eis is dat het een circulaire lijst wordt. Als ik door de lijst loop wil ik aan het einde van de lijst terugspringen naar het begin van de lijst. Hiervoor heb ik een method nodig waarmee ik naar de volgende node kan springen. Ik breid mijn unittest uit met een MoveNext method. Na de MoveNext zou de Current property nog steeds naar dezelfde node moeten verwijzen omdat we op dit moment slechts met 1 node werken. [Test] public void AddSingleNode() {
  • 6. CircularLinkedList<String> linkedList = new CircularLinkedList<String>(); linkedList.Add(quot;Fooquot;); Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;); Assert.AreEqual(linkedList.Count, 1); linkedList.MoveNext(); Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;); } Ik wijzig CircularLinkedList om de MoveNext methode uit te werken. Ook zorg ik ervoor dat de Next property van een Node gevuld wordt in de Add methode. public class CircularLinkedList<T> { private Node<T> _current; private int _count; public void Add(T item) { Node<T> insert = new Node<T>(item); _current = insert; _current.Next = _current; _count++; } public int Count { get { return _count; } } public Node<T> Current { get { return _current; } } public void MoveNext() { _current = _current.Next;
  • 7. } } Ik heb nu een CircularLinkedList die werkt voor mijn eisen. Het enige nadeel is dat het slechts voor een enkele node werkt. Ik besluit mijn testen uit te breiden met het toevoegen van twee nodes. [Test] public void AddTwoNodes() { CircularLinkedList<String> linkedList = new CircularLinkedList<String>(); linkedList.Add(quot;Fooquot;); linkedList.Add(quot;Barquot;); Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;); Assert.AreEqual(linkedList.Count, 2); linkedList.MoveNext(); Assert.AreEqual(linkedList.Current.Value, quot;Barquot;); linkedList.MoveNext(); Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;); } Deze test faalt omdat de CircularLinkedList nog niet overweg kan met twee nodes. public class CircularLinkedList<T> { private Node<T> _current; private Node<T> _first; private int _count; public void Add(T item) { Node<T> insert = new Node<T>(item); if (_count == 0) {
  • 8. _first = insert; _first.Next = _first; _current = _first; _count++; return; } if (_count == 1) { _first.Next = insert; insert.Next = _first; _count++; return; } } public int Count { get { return _count; } } public Node<T> Current { get { return _current; } } public void MoveNext() { _current = _current.Next; } } Ik maak bij de Add methode nu onderscheid tussen 1 of 2 nodes. Omdat ik bij wil kunnen houden wat de eerste node is introduceer ik een _first Node<T> variabele. Mijn unittest werkt nu voor 1 en voor 2 nodes. Leuk en aardig, maar ik wil natuurlijk dat het gaat werken voor alle aantallen nodes. Om dit te kunnen maken voeg ik nog een unittest toe. Ik ga ervan uit dat als ik het werkend heb voor 3 nodes,dat ik het dan werkend heb voor een willekeurig aantal nodes. [Test] public void AddThreeNodes()
  • 9. { CircularLinkedList<String> linkedList = new CircularLinkedList<String>(); linkedList.Add(quot;Fooquot;); linkedList.Add(quot;Barquot;); linkedList.Add(quot;Fuzquot;); Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;); Assert.AreEqual(linkedList.Count, 3); linkedList.MoveNext(); Assert.AreEqual(linkedList.Current.Value, quot;Barquot;); linkedList.MoveNext(); Assert.AreEqual(linkedList.Current.Value, quot;Fuzquot;); linkedList.MoveNext(); Assert.AreEqual(linkedList.Current.Value, quot;Fooquot;); } Ik pas de Add method aan om met meerdere nodes om te kunnen gaan. public class CircularLinkedList<T> { private Node<T> _current; private Node<T> _first; private int _count; public void Add(T item) { Node<T> insert = new Node<T>(item); if (_count == 0) { _first = insert; _first.Next = _first;
  • 10. _current = _first; _count++; return; } if (_count == 1) { _first.Next = insert; insert.Next = _first; _count++; return; } // > 2 nodes present Node<T> iterate = _first; for (int i=1; i < _count; i++) { iterate = iterate.Next; } iterate.Next = insert; insert.Next = _first; _count++; } public int Count { get { return _count; } } public Node<T> Current { get { return _current; } } public void MoveNext() { _current = _current.Next; } }
  • 11. Je ziet dat ik bij meer dan 2 nodes aan de hand van _count door de lijst loop. Zodra ik op het einde van de lijst ben voeg ik de nieuwe node toe en verbindt deze weer met de eerste node. Alhoewel mijn unittest nu werkt ben ik nog niet tevreden. Voor het toevoegen van een node moet ik nu de hele lijst doorlopen. De Add method is nu een O(n) operatie geworden. (De snelheid van de method is nu afhankelijk van het aantal elementen in de lijst). Ik besluit mijn code te refactoren. Daarbij kan ik mijn bestaande unittests gebruiken om te kijken of de functionaliteit blijft werken. public class CircularLinkedList<T> { private Node<T> _current; private Node<T> _first; private Node<T> _last; private int _count; public void Add(T item) { Node<T> insert = new Node<T>(item); if (_count == 0) { _first = insert; _first.Next = _first; _current = _first; _last = _first; _count++; return; } if (_count == 1) { _last.Next = insert; insert.Next = _first; _last = insert;
  • 12. _count++; return; } _last.Next = insert; insert.Next = _first; _last = insert; _count++; } public int Count { get { return _count; } } public Node<T> Current { get { return _current; } } public void MoveNext() { _current = _current.Next; } } Ik heb een _last Node<T> geintroduceerd. Hiermee hou ik nu bij wat de laatste node is die wordt toegevoegd. Hierdoor kan ik op een vrij eenvoudige manier de nieuwe node toevoegen, namelijk achter de laatste. Add is nu van een O(n) operatie veranderd in een O(1) operatie. (De snelheid van de Add methode is nu constant). Mijn unittesten blijven werken wat voor mij het bewijs is dat mijn refactoring de functionaliteit niet laat omvallen. Het valt me wel op dat de code voor het toevoegen van de tweede node en het toevoegen van alle nodes daarna in de Add methode wel erg veel op elkaar lijkt. Volgens mij kan ik die code eenvoudiger maken. public void Add(T item) { Node<T> insert = new Node<T>(item);
  • 13. // No nodes present if (_count == 0) { _first = insert; _first.Next = _first; _current = _first; _last = _first; _count++; return; } // One or more nodes already present. _last.Next = insert; insert.Next = _first; _last = insert; _count++; } Je ziet dat ik de conditie voor 1 node en 2 nodes of meer heb kunnen combineren. Mijn unittests blijven werken wat voor mij weer een bevestiging is dat de implementatie wel is aangepast, maar niet de functionaliteit. De CircularLinkedList werkt nu voor de in het begin gestelde eisen. Ik hoop dat ik op deze manier heel in het kort heb kunnen laten zien hoe een typische TDD-sessie kan verlopen.