Click here to Skip to main content
15,881,803 members
Articles / Programming Languages / C#

Fluent ChainLinkList with a Custom Linked List

Rate me:
Please Sign up or sign in to vote.
4.60/5 (6 votes)
5 Mar 2010CPOL2 min read 20.3K   94   10   5
Creating a fluent ChainLink with a custom linked list

Introduction

In software engineering, a Fluent Interface, first coined by Eric Evans and Martin Fowler, is a way of implementing an object oriented API in a way that aims to provide for more readable code. In other words, a developer would write the code in such a way that it conveys the purpose of the code in its syntax itself.  

I've decided to demonstrate this paradigm using a custom linked list which I call a ChainLink. The ChainLink object is actually a full implementation of a linked list data structure with added functionality like BubbleSort and Reverse. I could have used the default LinkList data structure provided by Microsoft, however I thought it would be more fun to create my own.  Plus the LinkList provided by Microsoft does not have BubbleSort or Reverse functions.

Using the Code

Here we go. Let’s get to the code! Here is the code for the generic ChainLinkList data structure:

C#
 [Serializable]
 public class ChainLinkList<T> : ICloneable, IEnumerable<T> where T : IComparable<T>
 {
    #region Public Properties
  
    public int Count
    {
        get;
        private set;
    }
  
    public ChainListNode<T> Head
    {
        get;
        private set;
    }
  
    public ChainListNode<T> Tail
    {
        get;
        private set;
    }
  
    #endregion
  
    #region Enumerators
  
    public IEnumerator<T> GetEnumerator()
    {
        ChainListNode<T> current = Head;
  
        while (current != null)
        {
            yield return current.Value;
            current = current.Next;
        }
    }
  
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
  
    #endregion
  
    public ChainListNode<T> this[int index]
    {
        get
        {
            ChainListNode<T> current = Head;
  
            if (index > Count - 1)
            {
                throw new IndexOutOfRangeException();
            }
  
            for (int i = 0; i < index; i++)
            {
                current = current.Next;
            }
  
            return current;
        }
    }
  
    public ChainListNode<T> AddItem(ChainListNode<T> node)
    {
        if (node == null)
        {
            throw new ArgumentNullException();
        }
  
        node.Container = this;
  
        if (Head == null)
        {
            Head = node;
            Tail = node;
        }
        else
        {
            Tail.Next = node;
            Tail = node;
        }
  
        Count++;
  
        return node;
    }
  
    public ChainListNode<T> AddItem(T value)
    {
        ChainListNode<T> node = new ChainListNode<T>(value);
  
        node.Container = this;
  
        if (Head == null)
        {
            Head = node;
            Tail = node;
        }
        else
        {
            Tail.Next = node;
            Tail = node;
        }
  
        Count++;
  
        return node;
    }
  
    public ChainListNode<T> Find(T value)
    {
        var current = Head;
  
        while (current != null && current.Value.CompareTo(value) != 0)
        {
            current = current.Next;
        }
  
        return current;
    }
  
    public void AddItemAtHead(ChainListNode<T> node)
    {
        if (node == null)
        {
            throw new ArgumentNullException();
        }
  
        node.Container = this;
  
        if (Head == null)
        {
            Head = node;
            Tail = node;
        }
        else
        {
            node.Next = Head;
            Head = node;
        }
  
        Count++;
    }
  
    public void HookOnBack(ChainLinkList<T> toAdd)
    {
        if (toAdd == null)
        {
            throw new ArgumentNullException();
        }
  
        Count = toAdd.Count;
  
        if (Head == null)
        {
            Head = toAdd.Head;
            Tail = toAdd.Tail;
        }
        else
        {
            Tail.Next = toAdd.Head;
            Tail = toAdd.Tail;
        }
    }
  
    public void HookInFront(ChainLinkList<T> toAdd)
    {
        if (toAdd == null)
        {
            throw new ArgumentNullException();
        }
  
        if (Head == null)
        {
            HookOnBack(toAdd);
        }
        else
        {
            ChainListNode<T> temp = Head;
  
            Head = toAdd.Head;
            toAdd.Tail.Next = temp;
  
            Count = toAdd.Count;
        }
    }
  
    // <summary>
    // In-place Bubble Sort: Performance O(n2).  Not bad for a small list.
    // </summary>
    public void BubbleSort()
    {
        ChainListNode<T> first = Head;
  
        while (first != null)
        {
            ChainListNode<T> second = first.Next;
  
            while (second != null)
            {
                if (first.Value.CompareTo(second.Value) > 0)
                {
                    T temp = first.Value;
  
                    first.Value = second.Value;
                    second.Value = temp;
                }
  
                second = second.Next;
            }
  
            first = first.Next;
        }
    }
  
    public void Reverse()
    {
        ChainListNode<T> r = null;
        ChainListNode<T> y = Head;
  
        Head = Tail;
  
        while (y != null)
        {
            ChainListNode<T> temp = y.Next;
            y.Next = r;
            r = y;
            y = temp;
        }
  
        Tail = y;
    }
  
    public ChainListNode<T> AddAfter(ChainListNode<T> node, ChainListNode<T> toAdd)
    {
        ChainListNode<T> current = Head;
  
        while (current != null)
        {
            if (current == node)
            {
                toAdd.Next = current.Next;
                current.Next = toAdd;
                toAdd.Container = this;
  
                if (toAdd.Next == null)
                {
                    Tail = toAdd;
                }
  
                Count++;
                return toAdd;
            }
  
            current = current.Next;
        }
  
        return null;
    }
   
   public bool Contains(T value)
    {
        ChainListNode<T> current = Head;
  
        while (current != null)
        {
            if (current.Value.Equals(value))
            {
                return true;
            }
  
            current = current.Next;
        }

        return false;
    }

    public void Clear()
    {
        Head = null;
        Tail = null;

        Count = 0;
    }

    #region ICloneable

    public object Clone()
    {
        ChainLinkList<T> copy = new ChainLinkList<T>();
        copy.HookOnBack(this);

        return copy;
    }

    #endregion
}

And here is the code for the ChainListNode<T> class:

C#
[Serializable]
 public class ChainListNode<T> : IEnumerable<T> where T : IComparable<T>
 {
   public ChainListNode()
   {
       Container = new ChainLinkList<T>();
       Container.AddItem(this);
   }

   public ChainLinkList<T> Container
   {
       get;
       internal set;
   }

   public ChainListNode(T value)
   {
       this.Value = value;

       Container = new ChainLinkList<T>();
       Container.AddItem(this);
   }

   public ChainListNode<T> Next
   {
       get;
       set;
   }

   public T Value
   {
       get;
       set;
   }

   public ChainListNode<T> AddAfter(ChainListNode<T> toAdd)
   {
       return Container.AddAfter(this, toAdd);
   }

   public ChainListNode<T> AddAfter(T toAdd)
   {
       return Container.AddAfter(this, new ChainListNode<T>(toAdd));
   }

   public override string ToString()
   {
       return Value.ToString();
   }

   #region Enumerators

   public IEnumerator<T> GetEnumerator()
   {
       return Container.GetEnumerator();
   }

   IEnumerator IEnumerable.GetEnumerator()
   {
       return this.GetEnumerator();
   }

   #endregion
}

Fluent Interface Programming

Notice that the ChainListNode has a reference to its container (which is a ChainLinkList). We'll see what this means soon!

Now for the ‘fluent’ part of this article. Suppose I want to create a simple linked list with one item. I would use the following code:

C#
ChainLinkList<int> chainLink = new ChainLinkList<int>();
ChainListNode<int> one = new ChainListNode<int>(1);
chainLink.AddItem(one);

What if I wanted to add more nodes? Should I always need a reference to the chainlink object in order to add more nodes? Since this is a chain, can't I just add a node to the last link on the chain? Sure I can! Examine the following code snippet:

C#
//Chain Linking
one.AddAfter(2).AddAfter(3).AddAfter(4).AddAfter(5).AddAfter(6);
chainLink.AddItem(7);
chainLink.AddItem(3);
chainLink.AddItem(4).AddAfter(20);

When you iterate over the chain, your output will be:

1 2 3 4 5 6 7 3 4 20 

And the code is written in such a way that I can see the chain of nodes directly in the code. This is one of the main points of fluent interfaces.

Do I even need an initial ChainLinkList object to create a chain you ask? No, of course not! Don't be ridiculous! Please examine the following code:

C#
ChainLinkList<int> chainLink1 =
   new ChainListNode<int>(4).AddAfter(5).AddAfter(6).Container;
ChainLinkList<int> chainLink2 =
   new ChainListNode<int>(1).AddAfter(2).AddAfter(3).Container;

Now for the fun part. You're probably wondering what is he up to now! And you will not be disappointed. Please examine the following code:

C#
foreach (int value in new ChainListNode<int>(1).AddAfter(2).AddAfter(3))
{
    Console.Write(value + " ");
}

And there you have it. A ChainLinkList design with the ‘fluent interface’ paradigm. You can also reverse the chain and sort the chain using BubbleSort.

In the sample code, there is a QuickSort method. I implemented it just for fun. You should not use the QuickSort method. QuickSort works best with arrays. The only reason I implemented it was to have fun with the chain, nothing else. Its performance is much worse than the general performance for QuickSort. You are warned.

Please tell me what you think.

History

  • 5th March, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Finance Industry
United States United States
Currently pursuing 'Programming Nirvana' (The ineffable ultimate in which one has attained disinterested wisdom and compassion as it relates to programming)

Respected Technologies
1. Confusor (https://confuser.codeplex.com/)
2. Power Threading (http://www.wintellect.com/Resources/visit-the-power-threading-library)
3. EDI Parsers (http://www.rdpcrystal.com)


Acknowledgements:

Microsoft Certified Technologist for WPF and .Net 3.5 (MCTS)
Microsoft Certified Technologist for WCF and .Net 3.5 (MCTS)
Microsoft Certified Application Developer for .Net (MCAD)
Microsoft Certified Systems Engineer (MCSE)
Microsoft Certified Professional (MCP)

Sun Certified Developer for Java 2 Platform (SCD)
Sun Certified Programmer for Java 2 Platform (SCP)
Sun Certified Web Component Developer (SCWCD)

CompTIA A+ Certified Professional

Registered Business School Teacher for Computer Programming and Computer Applications (2004)
(University of the State of New York Education Department)

Graduated from University At Stony Brook

Comments and Discussions

 
GeneralMy vote of 5 Pin
Kanasz Robert28-Sep-12 7:15
professionalKanasz Robert28-Sep-12 7:15 
GeneralRe: My vote of 5 Pin
FatCatProgrammer30-Sep-12 5:45
FatCatProgrammer30-Sep-12 5:45 
GeneralI like it Pin
Stephen Swensen5-Mar-10 16:50
Stephen Swensen5-Mar-10 16:50 
GeneralRe: I like it Pin
FatCatProgrammer6-Mar-10 7:12
FatCatProgrammer6-Mar-10 7:12 
GeneralVery nice!! [modified] Pin
AnthonyPaulO5-Mar-10 8:18
AnthonyPaulO5-Mar-10 8:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.