65.9K
CodeProject 正在变化。 阅读更多。
Home

带自定义链表的 Fluent ChainLinkList

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.60/5 (6投票s)

2010年3月5日

CPOL

2分钟阅读

viewsIcon

21061

downloadIcon

110

创建一个带自定义链表的流畅 ChainLink。

引言

在软件工程中,**流畅接口**(Fluent Interface)最初由 Eric Evans 和 Martin Fowler 提出,是一种以旨在提供更易读代码的方式实现面向对象 API 的方法。换句话说,开发人员会以一种能够通过其自身语法传达代码目的的方式编写代码。 

我决定使用我称之为 ChainLink 的自定义链表来演示这种范例。ChainLink 对象实际上是链表数据结构的一个完整实现,并添加了诸如 BubbleSort Reverse 之类的功能。我本可以使用 Microsoft 提供的默认 LinkList 数据结构,但我觉得自己创建自己的会更有趣。此外,Microsoft 提供的 LinkList 没有 BubbleSort Reverse 函数。

Using the Code

我们开始吧。让我们来看代码!这是通用 ChainLinkList 数据结构的示例代码

 [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
}

这是 ChainListNode<T> 类的代码

 [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
 }

流畅接口编程

请注意,ChainListNode 对其容器(即 ChainLinkList)有一个引用。我们很快就会看到这意味着什么!

现在是本文的“流畅”部分。假设我想创建一个包含一个项目的简单链表。我会使用以下代码

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

如果我想添加更多节点怎么办?我是否总是需要对 chainlink 对象进行引用才能添加更多节点?由于这是一个链条,我不能只是将一个节点添加到链条上的最后一个链接吗?当然可以!请查看以下代码片段

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

当你遍历链条时,你的输出将是

1 2 3 4 5 6 7 3 4 20 

代码的编写方式让我可以直接在代码中看到节点链。这是流畅接口的主要目的之一。

你问我,创建链条是否需要初始 ChainLinkList 对象? 不,当然不需要!别荒谬了!请查看以下代码

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

现在是好玩的部分。你可能想知道他在做什么!你不会失望的。请查看以下代码

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

就这样了。 一个采用“流畅接口”范例的 ChainLinkList 设计。你还可以使用 BubbleSort 反转链条并对链条进行排序。

在示例代码中,有一个 QuickSort 方法。我只是为了好玩才实现它。你不应该使用 QuickSort 方法。QuickSort 最适合数组。我实现它的唯一原因是为了与链条一起玩,除此之外没有其他原因。它的性能比 QuickSort 的通用性能差得多。请注意。

请告诉我你的想法。

历史

  • 2010年3月5日:初始发布
© . All rights reserved.