Original question: http://codereview.stackexchange.com/q/64943/54489

Here is my first attempt at refactoring.  Instead of using a `Tuple`, I used a `STRUCT` and this made it more readable.  I also extracted printing paths and finding paths into interfaces so someone could print their own messages or implement there own path finding algorithm.  The graph class has a corresponding interface as well.

I am still not sure if I need all the path finding methods. Some of them are just one-liners, so I wonder if they are necessary.  I also want to pass a file name as an `args` parameter into `main`, but I am not sure how to get that to my `CreateGraph` method.  Also, `IGraph graph = new Graph().CreateGraph();` seems a bit weird.  Is this normal?  I am also curious if there is a way to better pass in messages to my `PrintXXXPath...` methods, so I don't have to have so many of them.

### **Node** class

    public class Node
    {
        public string Name { get; private set; }
        public List<Edge> Edges { get; private set; }

        public Node(string name)
        {
            Name = name;
            Edges = new List<Edge>();
        }

        public void AddEdge(Node targetNode, double weight)
        {
            Edges.Add(new Edge(targetNode,weight));
        }
    }

### **Edge** class

    public class Edge
    {
        public Node TargetNode { get; private set; }
        public double Weight { get; private set; }

        public Edge(Node targetNode, double weight)
        {
            TargetNode = targetNode;
            Weight = weight;
        }
    }

### **QueueItem** class

    public class QueueItem
    {
        public Node Node { get; private set; }
        public List<Edge> VisitedEdges { get; private set; }

        public QueueItem(Node node, List<Edge> visitedEdges)
        {
            Node = node;
            VisitedEdges = visitedEdges;
        }
    }

### **Path** struct

    public struct Path
    {
        public readonly Node StartNode;
        public readonly Node EndNode;
        public readonly string PathRepresentation;
        public readonly int VisitedCount;
        public readonly double TotalWeight;

        public Path(Node startNode, Node endNode, string pathRepresentation, int visitedCount, double totalWeight)
        {
            StartNode = startNode;
            EndNode = endNode;
            PathRepresentation = pathRepresentation;
            VisitedCount = visitedCount;
            TotalWeight = totalWeight;
         
        }
    }

### **IGraph** interface

    public interface IGraph
    {
        void AddNode(string name);
        Node GetNode(string name);
        IGraph CreateGraph();
    }

### **Graph** class

    public class Graph : IGraph
    {
        public Dictionary<string, Node> Nodes { get; private set; }

        public Graph()
        {
            Nodes = new Dictionary<string, Node>();
        }

        public void AddNode(string name)
        {
            Nodes.Add(name, new Node(name));
        }

        public Node GetNode(string name)
        {
            if (Nodes.ContainsKey(name))
                return Nodes[name];

            return null;
        }

        public IGraph CreateGraph()
        {
            const int START_NODE_INDEX = 0;
            const int END_NODE_INDEX = 1;
            const int EDGE_WEIGHT_INDEX = 2;
            
            var nodes = File.ReadAllText("graph.csv").Split(',');

            IGraph graph = new Graph();

            foreach (var n in nodes)
            {
                var node = n.Trim();
                
                if (graph.GetNode(node[START_NODE_INDEX].ToString()) == null)
                {
                    graph.AddNode(node[START_NODE_INDEX].ToString());
                }

                if (graph.GetNode(node[END_NODE_INDEX].ToString()) == null)
                {
                    graph.AddNode(node[END_NODE_INDEX].ToString());
                }

                graph.GetNode(node[START_NODE_INDEX].ToString())
                     .AddEdge(graph.GetNode(node[END_NODE_INDEX].ToString()), Convert.ToInt32(node[EDGE_WEIGHT_INDEX].ToString()));

            }

            return graph;
        }
    }

### **IPathFinder** interface

    public interface IPathFinder
    {
        Path GetShortestPath(Node startNode, Node endNode);

        List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive);

        List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive);

        List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight);

        List<Path> GetAllPaths(Node startNode, Node endNode);

        List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive);

        List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive);

        List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops);

        Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges);

        string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges);
    }

### **PathFinder** class

    public class PathFinder : IPathFinder
    {
        public string GetPathRepresentation(Node startNode, Node endNode, IEnumerable<Edge> visiteEdges)
        {
            var pathRepresentation = new StringBuilder();

            pathRepresentation.AppendFormat("{0}->", startNode.Name);

            foreach (var visitedEdge in visiteEdges)
            {
                if (visitedEdge.TargetNode == endNode)
                {
                    pathRepresentation.Append(endNode.Name);
                }
                else
                {
                    pathRepresentation.AppendFormat("{0}->", visitedEdge.TargetNode.Name);
                }
            }

            return pathRepresentation.ToString();
        }
        
        public Path GetPath(Node startNode, Node endNode, IEnumerable<Edge> visitedEdges)
        {
            var visitedPaths = visitedEdges as IList<Edge> ?? visitedEdges.ToList();

            string pathRepresentation = GetPathRepresentation(startNode, endNode, visitedPaths);

            double totalWeight = visitedPaths.Aggregate<Edge, double>(0,
                                (current, visitedEdge) => current + visitedEdge.Weight);

            return new Path(startNode, endNode, pathRepresentation, visitedPaths.Count(), totalWeight);
        }

        public List<Path> GetAllPaths(Node startNode, Node endNode)
        {
            var paths = new List<Path>();
            var queue = new Queue<QueueItem>();

            queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

            while (queue.Count > 0)
            {
                var currentItem = queue.Dequeue();
                foreach (var edge in currentItem.Node.Edges)
                {
                    if (!currentItem.VisitedEdges.Contains(edge))
                    {
                        var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                        if (edge.TargetNode == endNode)
                        {
                            var path = GetPath(startNode, endNode, visitedEdges);

                            paths.Add(path);
                        }
                        else
                        {
                            queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                        }
                    }
                }
            }
            return paths;
        }
        
        public Path GetShortestPath(Node startNode, Node endNode)
        {
            List<Path> paths = GetAllPaths(startNode, endNode);
            var shortestPath = new Path();
            double shortestPathWeight = double.PositiveInfinity;

            if (paths.Count > 0)
            {
                foreach (var path in paths)
                {
                    if (path.TotalWeight < shortestPathWeight)
                    {
                        if (path.TotalWeight < shortestPathWeight)
                        {
                            shortestPathWeight = path.TotalWeight;

                            shortestPath = new Path(startNode, endNode, path.PathRepresentation, path.VisitedCount,
                                path.TotalWeight);
                        }
                    }
                }
            }

            return shortestPath;
        }

        public List<Path> GetPathsWithMinWeight(Node startNode, Node endNode, double minWeight, bool inclusive)
        {
            if (inclusive)
                return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight >= minWeight).ToList();

            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight > minWeight).ToList();
        }

        public List<Path> GetPathsWithMaxWeight(Node startNode, Node endNode, double maxWeight, bool inclusive)
        {
            if(inclusive)
                return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight <= maxWeight).ToList();

            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight < maxWeight).ToList();
        }

        public List<Path> GetPathsWithExactWeight(Node startNode, Node endNode, double weight)
        {
            return GetAllPaths(startNode, endNode).Where(path => path.TotalWeight.Equals(weight)).ToList();
        }

        public List<Path> GetPathsWithMinStops(Node startNode, Node endNode, int minStops, bool inclusive)
        {
            if(inclusive)
                return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount >= minStops).ToList();
                
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount > minStops).ToList();
        }

        public List<Path> GetPathsWithMaxStops(Node startNode, Node endNode, int maxStops, bool inclusive)
        {
            if(inclusive)
                return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount <= maxStops).ToList();

            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount < maxStops).ToList();
        }

        public List<Path> GetPathsWithExactStops(Node startNode, Node endNode, int stops)
        {
            return GetAllPaths(startNode, endNode).Where(path => path.VisitedCount == stops).ToList();
        }

        public List<Path> GetAllPaths2(Node startNode, Node endNode)
        {
            var paths = new List<Path>();
            var queue = new Queue<QueueItem>();

            queue.Enqueue(new QueueItem(startNode, new List<Edge>()));

            while (queue.Count > 0)
            {
                var currentItem = queue.Dequeue();
                foreach (var edge in currentItem.Node.Edges)
                {
                    if (!currentItem.VisitedEdges.Contains(edge))
                    {
                        var visitedEdges = new List<Edge>(currentItem.VisitedEdges) { edge };
                        if (edge.TargetNode == endNode)
                        {
                            var path = GetPath(startNode, endNode, visitedEdges);

                            paths.Add(path);
                        }
                        else
                        {
                            queue.Enqueue(new QueueItem(edge.TargetNode, visitedEdges));
                        }
                    }
                }
            }
            return paths;
        }
    }

### **IPathPrinter** interface

    public interface IPathPrinter
    {
        void PrintShortestPath(Path path);
        void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive);
        void PrintPathDistance(List<Path> paths);
        void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive);
        void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops);
    }

### **PathPrinter** class

    public class PathPrinter : IPathPrinter
    {
        public void PrintShortestPath(Path path)
        {
            Console.WriteLine("The shortest path from '{0}' to '{1} is '{2}' with a distance of {3}",
                path.StartNode.Name, path.EndNode.Name, path.PathRepresentation, path.TotalWeight);
        }

        public void PrintPathsWithMaxWeight(List<Path> paths, Node startNode, Node endNode, double maxWeight, bool inclusive)
        {
            if (inclusive)
            {
                Console.WriteLine(
                    "The number of trips from '{0}' to '{1}' with a distance of less than or equal to {2} is {3}:",
                    startNode.Name, endNode.Name, maxWeight, paths.Count);
            }
            else
            {
                Console.WriteLine(
                    "The number of trips from '{0}' to '{1}' with a distance of less than {2} is {3}:",
                    startNode.Name, endNode.Name, maxWeight, paths.Count);
            }

            foreach (var path in paths)
            {
                Console.WriteLine("{0} with a distance of {1}", path.PathRepresentation, path.TotalWeight);
            }
        }

        public void PrintPathsWithMaxStops(List<Path> paths, Node startNode, Node endNode, int maxStops, bool inclusive)
        {
            if (inclusive)
            {
                Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
            }
            else
            {
                Console.WriteLine("The number of trips from '{0}' to '{1}' with a maximum of less than {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);
            }
            

            PrintPaths(paths);
        }

        public void PrintPathsWithExactStops(List<Path> paths, Node startNode, Node endNode, int maxStops)
        {

            Console.WriteLine("The number of trips from '{0}' to '{1}' with exactly {2} stops is {3}:", startNode.Name, endNode.Name, maxStops, paths.Count);

            PrintPaths(paths);
        }

        public void PrintPathDistance(List<Path> paths)
        {
            foreach (var path in paths)
            {
                Console.WriteLine("The distance of the route '{0}' is {1}", path.PathRepresentation, path.TotalWeight);
            }
        }

        private static void PrintPaths(IEnumerable<Path> paths)
        {
            foreach (var path in paths)
            {
                Console.WriteLine(path.PathRepresentation);
            }
        }
    }

### **Program** class

    public class Program
    {
        public static void Main(string[] args)
        {
            IPathFinder pathFinder = new PathFinder();
            IPathPrinter pathPrinter = new PathPrinter();

            IGraph graph = new Graph().CreateGraph();
            
            var pathsAtoC = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("C"));
            pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->B->C")).ToList());

            var pathsAtoD = pathFinder.GetAllPaths(graph.GetNode("A"), graph.GetNode("D"));
            
            pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->D")).ToList());

            pathPrinter.PrintPathDistance(pathsAtoC.Where(x => x.PathRepresentation.Equals("A->D->C")).ToList());

            pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->B->C->D")).ToList());

            var validPath = pathsAtoD.Any(x => x.PathRepresentation.Equals("A->E->D"));

            if(validPath)
                pathPrinter.PrintPathDistance(pathsAtoD.Where(x => x.PathRepresentation.Equals("A->E->D")).ToList());

            Console.WriteLine(Environment.NewLine);

            var path = pathFinder.GetShortestPath(graph.GetNode("A"), graph.GetNode("C"));
            pathPrinter.PrintShortestPath(path);

            path = pathFinder.GetShortestPath(graph.GetNode("C"), graph.GetNode("C"));
            pathPrinter.PrintShortestPath(path);

            Console.WriteLine(Environment.NewLine);

            var paths = pathFinder.GetPathsWithMaxStops(graph.GetNode("C"), graph.GetNode("C"),3,true);
            pathPrinter.PrintPathsWithMaxStops(paths,graph.GetNode("C"),graph.GetNode("C"),3,true);

            Console.WriteLine(Environment.NewLine);

            paths = pathFinder.GetPathsWithExactStops(graph.GetNode("A"), graph.GetNode("C"), 4);
            pathPrinter.PrintPathsWithExactStops(paths, graph.GetNode("A"), graph.GetNode("C"), 4);

            Console.WriteLine(Environment.NewLine);

            paths = pathFinder.GetPathsWithMaxWeight(graph.GetNode("C"), graph.GetNode("C"), 30, false);
            pathPrinter.PrintPathsWithMaxWeight(paths, graph.GetNode("C"), graph.GetNode("C"), 30,false);

            Console.ReadKey();
        } 
    }


  [1]: http://codereview.stackexchange.com/q/64943/54489