From: Jason D. <ja...@in...> - 2001-06-06 08:14:23
|
> I updated the interface proposal below with > Jason's ReadCharacter() variants... I'm sure > I'm totally botching C# by now... so forgive > me. But this is a simpler way to express stuff > than the C interface. Your C# is pretty damn close. We can pretend it's Java if you like. ;-) Perhaps I'm being too simplistic but I think that the following two interfaces are all that's needed to traverse both stream and tree-based data sources: public interface IYamlIterator { YamlNodeType NodeType { get; } string Key { get; } int Anchor { get; } string ReadCharacters(); // ... byte[] ReadBytes(); // ... bool Move(); // MoveToNextNodeInDocument bool MoveNext(); // MoveToNextSiblingNode bool MoveIn(); // MoveToFirstChildNode } public interface IYamlNavigator : IYamlIterator { bool MoveOut(); // MoveToParentNode IYamlNavigator Clone(); } (I'm perfectly open to suggestions for better names...) We can always add more properties/methods to the navigator like ChildCount, MoveLast (to the previous sibling), etc, if we thought we needed it. I tried to reach a compromise between our two approaches. It's supposed to be both a hierarchical and flat iterator. But instead of returning a new iterator like FirstChild(), MoveIn() simply changes it's internal state to point to the first child if there is one. MoveNext() is analogous to your Next() method. Once it reaches the last element of a list or pair of a map it will continue to return false until Move() is called which moves it to the next node after the current node's parent. This is perfect for stream-based data sources. Note that calling MoveNext() instead of MoveIn() on a node with child nodes is like calling your Next() or my Skip(). It skips the children. But if you were on a node with children and called Move(), it would move to the first child. Was I able to accurately capture both our approaches? The derived interface, IYamlNavigator adds MoveOut(). This moves up to the current node's parent like your ParentIterator() but changes the internal state rather than returning a new Iterator or Navigator. The Clone() method can be used to clone the navigator so that you can have multiple navigators over the same tree. This is possible with tree-based data sources but not with stream-based ones. Note that I didn't add anything related to the Visitor pattern. I thought about adding a Visit method to Iterator since it could be possible for the iterator to call MoveIn and MoveNext on itself and invoke the Begin and End methods on the visitor as it did that but this didn't strike me as being code specific to a data source. I envsision that these interfaces are implemented per data source but the visit code (which you implemented in your YamlConnect function) can apply to all types of data sources that expose the iterator interface (you don't have to be a tree to visit, no?) Given that, I though that your YamlConnect function should live outside these interfaces. If we were defining abstract classes instead, I can see that we would implement the method inside iterator and let derivatives inherit it but I prefer to use interfaces and not use up the only inheritance slot in both Java and C#. What I really like about this approach is that it's possible for a data source to implement IYamlNavigator and since that derives from IYamlIterator, processors that only need sequential access through a document will be able to consume both stream and tree-based data sources without having a clue as to what it is they're really communicating with. Anyways, it's time for bed. I suspect that I'm still way off base but it's been fun trying! Jason. |