嗔秤戻幣�哉膵�云利匈嬉蝕湊蛸賜�塋床四衲���萩晦編報炎嘔囚^泡仟 ̄云利匈��
源平慎弌傍利 卦指云慕朕村 紗秘慕禰 厘議慕尺 厘議慕禰 TXT畠云和墮 〆辺茄欺厘議箝誓匂〇

VB2008貫秘壇欺娼宥(PDF鯉塀哂猟井)-及21何蛍

酔楯荷恬: 梓囚徒貧圭�鮗� ○ 賜 ★ 辛酔堀貧和鍬匈 梓囚徒貧議 Enter 囚辛指欺云慕朕村匈 梓囚徒貧圭�鮗� ● 辛指欺云匈競何! 泌惚云慕短嗤堋響頼���誅卒亮茂�俊彭堋響��辛聞喘貧圭 "辺茄欺厘議箝誓匂" 孔嬬 才 "紗秘慕禰" 孔嬬��



method�察�implying that DepthFirstSearch must be instantiated before we can call  FindRoute�┌�。  

We should modify the test again as follows�此�



Public Sub TestSearch�┌�  

  Dim cls As DepthFirstSearch = New DepthFirstSearch�┌� 

  cls。FindRoute�─�Montreal;�察 �Seattle;�� 

End Sub 



     To execute the method FindRoute�┌��察�we need to create a DepthFirstSearch object�察�allowing  

multiple users to perform searches without getting state mixed up。 At this point�察�we could pat  

ourselves on the back and think that we have written a good test that requires a class  

implementation。 



The Problem of Magic Data 



Our test is not yet plete�察�because we don¨t have access to the route found by the algorithm�察 �

but that will be explained in a moment。  

     In the implementation of DepthFirstSearch�察�a reference to the data structure is necessary。  

The search algorithm needs to know which tree to navigate。 One way to implement a reference  

to the tree is to directly reference the shared data Node。RootNodes。 An implementation of  

DepthFirstSearch would be as follows�此�


´´´´´´´´´´´´´´´´´´´´´´Page 122´´´´´´´´´´´´´´´´´´´´´´´

100       CH AP T E R   4   *    L E A R N IN G   AB OU T   D AT A  S TR U CT U R E S�察  �DE CI SI ON S�察  �A N D   L O OP S 



           Public Class DepthFirstSearch  

              Public Sub FindRoute��ByVal start As String�察�ByVal finish As String��  

                  Dim startNodes As Node�┌� = Node。RootNodes 

              End Sub 

           End Class 



                This example declares a variable called startNodes�察�which represents the starting point  

          and root of the tree as shown in Figure 4´2。 The root of the tree is based on the data member  

           Node。RootNodes�察�and this assignment is called a magic type assignment。 A magic type is formed  

          when you call a method�察�and magically�察�it happens to know how to reference data�察�even though  

          you never instructed the type。 In the case of DepthFirstSearch�察�the magic is the ability of  

           FindRoute�┌� to know to reference the correct data member RootNodes。 

                The assumption is bad because it couples the data member  RootNodes to the method  

           FindRoute�┌�。 Imagine if the developer of the Node class later decides to add functionality to load  

          the tree from a file on the hard disk。 So that  FindRoute�┌� is not broken�察�the developer would  

          need to explicitly copy the hard´disk´loaded tree to the data member RootNodes。 

                Or what if two different users wanted to create two different flight trees�拭�Node。RootNodes is  

          a shared resource�察�and thus can process only a single flight tree。 The developer of Node might  

          alter RootNodes�察�and thus FindRoute�┌� would behave erratically。 

                When you have a case of magic data�察�whatever data is magic needs to be passed to the type  

          via a constructor or other method。 So the test for the flight route would change to the following�此�



           Public Sub TestSearch�┌�  

            Dim cls As DepthFirstSearch= _ 

              New DepthFirstSearch��Node。RootNodes�� 

            cls。FindRoute�─�Montreal;�察 �Seattle;�� 

           End Sub 



                As the root tree node is required�察�we change the constructor to require that a caller pass in the  

          root tree node。 The test code still uses the shared data member RootNodes�察�but DepthFirstSearch  

          does not need to know where to find the tree。 If the Node developer were to alter the behavior of  

          the data member RootNodes�察�then only the constructor code to DepthFirstSearch would need  

          altering�察�not the FindRoute�┌� method。 Thus�察�Node and DepthFirstSearch are properly decoupled  

          from each other。 



           Getting the Found Route 



          Once you have called the FindRoute�┌� method�察�you expect an answer。 Because the route could  

          involve multiple cities�察�the found route is stored in an array of Node elements。 In programmatic  

          terms�察�there are two ways of retrieving the array of Nodes。 The first is a return value�察�like this�此�



           Public Sub TestSearch�┌�  

            Dim cls As DepthFirstSearch = New DepthFirstSearch��Node。RootNodes�� 

            Dim foundRoute As Node�┌� = cls。FindRoute�─�Montreal;�察 �Seattle;�� 

           End Sub 



                The bold code shows the assignment of the return value to the variable foundRoute。  


´´´´´´´´´´´´´´´´´´´´´´Page 123´´´´´´´´´´´´´´´´´´´´´´´

                      CH AP T E R   4   *    L E A R N I N G   A B OU T   D AT A  S TR U CT U R E S�察  �DE CI SI ON S�察  �A N D   L O OP S 101 



     The second approach is for the FindRoute�┌� method to store the result in an internal data  

member。 The following example assumes that the FindRoute�┌� method stores the result in a  

data member named FoundRoute�此�



Public Sub TestSearch�┌�  

  Dim cls As DepthfirstSearch = New DepthFirstSearch��Node。RootNodes�� 

  cls。FindRoute�─�Montreal;�察 �Seattle;�� 

  Dim foundRoute As Node�┌� = cls。FoundRoute 

End Sub 



     Each approach seems acceptable�察�and you are not sure which to use。 When you have a  

choice like this�察�you need to make a decision。 The safest way to make a decision is to write tests  

and see if there are any problems with either approach。 

     In the example of calculating a single route�察�either approach is fine。 But let¨s look at the  

code when multiple routes are being searched。 First�察�consider the code where the found path  

is a return parameter value�此�



Public Sub TestSearch�┌�  

  Dim cls As DepthFirstSearch = New DepthFirstSearch��Node。RootNodes�� 

  Dim foundRoute1 As Node�┌� = cls。FindRoute�─�Montreal;�察 �Seattle;�� 

  Dim foundRoute2 As Node�┌� = cls。FindRoute�─�New York;�察 �Seattle;�� 

End Sub 



     Now take a look at the code that uses the data member�此�



Public Sub TestSearch�┌�  

  Dim cls As DepthFirstSearch = New DepthFirstSearch��Node。RootNodes�� 

  cls。FindRoute�─�Montreal;�察 �Seattle;�� 

  Dim foundRoute1 As Node�┌� = cls。FoundRoute 

  cls。FindRoute�─�New York;�察 �Seattle;�� 

  Dim foundRoute2 As Node�┌� = cls。FoundRoute 

End Sub 



     Again�察�it would seem that both choices are adequate。 However�察�there is a difference�察�the  

difference is subtle�察�but distinct enough to matter。 In the test implementation where the found  

route is a return value�察�the variables foundRoute1 and foundRoute2 represent routes that relate  

directly to the route being searched。 There is no chance that the variables foundRoute1 can  

represent the route New York�CSeattle。 With the data member code�察�it could happen that  

foundRoute1 points to the route New York�CSeattle�察�as shown in the following code。 



Public Sub TestSearch�┌�  

  Dim cls As DepthFirstSearch = New DepthFirstSearch��Node。RootNodes�� 

  cls。FindRoute�─�Montreal;�察 �Seattle;�� 

  cls。FindRoute�─�New York;�察 �Seattle;�� 

  Dim foundRoute1 As Node�┌� = cls。FoundRoute 

  Dim foundRoute2 As Node�┌� = cls。FoundRoute 

End Sub 


´´´´´´´´´´´´´´´´´´´´´´Page 124´´´´´´´´´´´´´´´´´´´´´´´

102       CH AP T E R   4   *    L E A R N IN G   AB OU T   D AT A  S TR U CT U R E S�察  �DE CI SI ON S�察  �A N D   L O OP S 



                By switching the order of the FindRoute�┌� method calls and references to the data member  

           FoundRoute�察�the variables foundRoute1 and foundRoute2 will reference the same found route�察 �

           specifically the route New York�CSeattle。 This is not a good idea。 The example shows how data  

           members have no direct relation to methods and can vary independently。 

                So the choice of returning the found route from a method is the better and more robust  

           approach。 



           *Note  Data members are useful when you want to store or retrieve data that spans multiple method calls  

           or is not dependent on the order of how methods are called。 When you have data that is dependent on the  

           order of called methods�察�you should use the Return keyword or ByRef parameters。 



                The following is the plete test case that includes the verification code that searches for  

           a flight from Montreal to Seattle。 



           Public Sub TestSearch�┌�  

             Dim cls As DepthFirstSearch = New DepthFirstSearch��Node。RootNodes�� 

             Dim foundRoute As Node�┌� = cls。FindRoute�─�Montreal;�察 �Seattle;�� 

             If foundRoute。Length  2 Then 

               Console。WriteLine�─�Incorrect route as route has two legs;�� 

             End If 

             If foundRoute��0��。CityName。pareTo�─�Los Angeles;��  0 Then 

               Console。WriteLine�─�Incorrect as first leg is Los Angeles;�� 

             End If 

           End Sub 



           *Note  We¨ve already used the If construct in earlier chapters。 It tests a condition and executes its contained  

           code if that condition is true。 The  means does not equal。 We¨ll examine If in more detail later in this  

           chapter�察�in the ^Using the If Statement ̄ section。 



           Implementing the Depth´First Search Algorithm 



           The implementation of the depth´first search algorithm involves creating an algorithm that  

           iterates the tree。 Here�察�we¨ll implement the algorithm in Visual Basic。 In so doing�察�we¨ll use  

           decision statements and For loops to iterate the array data。 These are incredibly mon in  

          Visual Basic programs�察�and life would be very difficult without them。 

                We implemented the test code in the previous section�察�so the next step is to implement a  

           version of DepthFirstSearch that represents a shell�察�so that all of the code piles and runs。  

           The shell is structural and is used to hold up the entire application。 It is defined as shown in  

           Figure 4´15。 


´´´´´´´´´´´´´´´´´´´´´´Page 125´´´´´´´´´´´´´´´´´´´´´´´

                         CH AP T E R   4   *    L E A R N I N G   A B OU T   D AT A  S TR U CT U R E S�察  �DE CI SI ON S�察  �A N D   L O OP S 103 



                                      Tree to be searched is passed as a 

                                         parameter to the constructor 

 Public Class DepthFirstSearch 

     Private root As Node�┌� 



                                                           Constructor parameter is assigned to a private data 

     Public Sub New��ByVal root As Node�┌��� 

         Me。root = root                                member。 Making a data member private means only those 

     End Sub                                             methods declared in DepthFirstSearch can reference it。 



     Public Function FindRoute��ByVal start As String�察�ByVal finish As String�� _ 

 As Node�┌� 

         Throw New Exception�─�Not implemented;�� 

     End Function 

 End Class 



                                        Empty implementation throws an 

                                     exception ��an error�� indicating that the 

                                            code is not implemented 



Figure 4´15。  The initial shell of the depth´first algorithm 



      With a shell implemented�察�you could run the application and see if everything works。 If  

you do run the test code�察�you will get an error�察�because calling  FindRoute�┌� generates an excep

tion that indicates  FindRoute�┌� has not been fully implemented。 ��Exceptions are discussed in  

detail in the next chapter。�� However�察�the shell is plete�察�and we are ready to implement the  

guts of the algorithm。 

      Implementing the guts of an algorithm is arguably one of the most difficult steps�察�as you  

must go through the logic of what you want to do。 Whenever I am confronted with an algorithm  

that needs implementation and I am not quite sure how to proceed�察�I just write code�察�based on  

an entry point ��the method call�� and an exit point ��the end of a method¨s execution��。 



The Keyhole Problem 



In the example�察�the entry and exit point into the algorithm is FindRoute�┌�。 In turn�察�the entry of  

FindRoute�┌� is two parameters�此�start�察�indicating the beginning city�察�and finish�察�indicating the  

destination city。 The exit of FindRoute�┌� is an array of Node objects。 

      The array of Node objects needs to be preallocated with space so that all of the found cities  

can be added。 We can make an assumption at this point that we preallocate the number of  

nodes to the length of the data member DepthFirstSearch。root plus one。 The assumption is  

that the longest trip cannot exceed the number of cities available。 We know that the root node  

is an array of all starting point cities�察�thus the allocation can never be exceeded。 

      Focusing on the FindRoute�┌� method�察�the updated code looks like this�此�



Public Function FindRoute��ByVal start As String�察�ByVal finish As String�� As Node�┌� 

  Dim returnArray��Me。root。Length �� 1�� As Node 

  Return returnArray 

End Function 



      The code with the array allocation is a classic keyhole problem ��an idea first introduced by  

Scott Meyers�察�see http��//aristeia。/TKP/��。 The problem of a keyhole is that you imple

ment an algorithm based on assumptions that cause you to write code that works for that specific  

context�察�but would fail when executed in another context。 


´´´´´´´´´´´´´´´´´´´´´´Page 126´´´´´´´´´´´´´´´´´´´´´´´

104       CH AP T E R   4   *    L E A R N IN G   AB OU T   D AT A  S TR U CT U R E S�察  �DE CI SI ON S�察  �A N D   L O OP S 



                The code allocates an array to the length of the root tree structure�察�and that is making a  

          grand assumption。 Imagine if the Node developers decided to introduce connections that could  

          be reached only via another city that is not included in the root nodes。 At that point�察�you could  

          potentially exceed the available space in the array。 Another solution would be to allocate an  

           array of arbitrary length X 。 But then�察�if there were X��1 unique cities�察�another array could be  

          violated。 

                The simplest solution would be to not allocate an array�察�but instead figure out how many  

           elements you needed after having found a path。 However�察�this would not work�察�because then  

          you would have no idea which city you had already visited。 Another solution ��which will be  

           discussed in Chapter 9�� would be to use a collection class。 

                In this case�察�we are going to wash our hands of the problem and force the Node developers  

          to modify their class。 The Node developers are going to add a shared method that tells the search  

           algorithm how big the array needs to be。 The following is the modified FindRoute�┌� code。 



           Public Function FindRoute��ByVal start As String�察�ByVal finish As String�� As Node�┌� 

            Dim returnA
卦指朕村 貧匯匈 和匯匈 指欺競何 壘��13�� 家��12��
酔楯荷恬: 梓囚徒貧圭�鮗� ○ 賜 ★ 辛酔堀貧和鍬匈 梓囚徒貧議 Enter 囚辛指欺云慕朕村匈 梓囚徒貧圭�鮗� ● 辛指欺云匈競何!
梁椣戻幣�� 梁心弌傍議揖扮窟燕得胎��傍竃徭失議心隈才凪万弌誌育断蛍�輌臆惨軼僑〃�燕慕得珊辛參資誼持蛍才将刮襲潜��範寔亟圻幹慕得 瓜寡追葎娼得辛參資誼寄楚署衛、持蛍才将刮襲潜填��