10-152-312 - Object-Oriented Programming 2

4.3 Zoo Assignment

Replace anonymous functions in zoo Find/Get methods with lambda expressions

  1. Replace all anonymous functions with lambda expressions in the following methods
    1. FindAnimal (all overloads)
    2. FindEmployee
    3. FindGuest
    4. GetAnimals

Replace a foreach loop with a simple lambda expression

  1. In the Cage's ToString method, replace the existing foreach loop with a lambda expression in the ForEach extension method
  2. The ToString method should contain this code:

Replace more foreach loops with simple lambda expressions

  1. Replace the foreach loops in the following method with a lambda expression inside of the ForEach extension method:
    1. Zoo.OnDeserialized

Replace an accumulator loop with an extension method

  1. Many foreach loops can be replaced with the ForEach extension method, but some loops should be replaced with other extension methods, depending upon what the loop is intended to do
  2. Look at the foreach loop inside the Zoo's TotalAnimalWeight property
    1. The loop adds each animal's weight to a variable holding a running total - in other words, it is summing the animals' weights
    2. Replace the foreach loop with the Sum extension method and a lambda expression

Refactor the AverageAnimalWeight to use an extension method

  1. In the zoo's AverageAnimalWeight property, instead of dividing the total animal weight by the number of animals, use the Average extension method with a lambda expression

Create a query window to test lambda expressions

  1. Create a new window called QueryWindow
    1. Pass the zoo into the window's constructor and store it in a field
    2. Add a label to the window
    3. Add a button called totalAnimalWeightButton
      1. Add a click event handler to the button
      2. The event handler should call the TotalAnimalWeight property and put the result into the label's content
    4. Add a button called totalAnimalWeightButton
      1. Add a click event handler
      2. The handler should call the AverageAnimalWeight property and put the result into the label's content
    5. Add a button called animalCountButton
      1. Add a click event handler
      2. The handler should call the AnimalCount property and put the result into the label's content
    6. Add a button called guestCountButton
      1. Add a click event handler
      2. The handler should call the GuestCount property and put the result into the label's content
  2. Add a button to the MainWindow that launches the QueryWindow
  3. Run the WPF application
    1. Create three animals
      1. The first has a weight of 10
      2. The second has a weight of 15
      3. The third has a weight of 18.5
    2. Create two guests
    3. Launch the QueryWindow
    4. Click each of the four buttons
      1. The total animal weight should be 43.5
      2. The average animal weight should be 14.5
      3. The animal count should be 3
      4. The guest count should be 2

Create a query command in the console application

  1. Add a new command to the console application called "query"
  2. The command takes two parameters
    1. The first parameter is "animal"
    2. The second parameter is "totalweight"
  3. The command should call the zoo's TotalAnimalWeight property and output the result to the console
  4. Run the console application
    1. Add two animals
      1. The first has a weight of 10
      2. The second has a weight of 12
    2. Run the "query animal totalweight" command
    3. The console should output a total animal weight of 22

Add more queries to the console query command

  1. Add more parameter options to the console's query command
    1. The command's first parameter can be either "animal" or "guest"
    2. For animals, the command's second parameter can be "totalweight", "averageweight", or "count"
      1. "totalweight" returns the total weight of all animals in the zoo
      2. "averageweight" returns the average weight of all animals in the zoo
      3. "count" returns the number of animals in the zoo
    3. For guests, the command's second parameter is "count"
      1. "count" returns the number of guests in the zoo
  2. Add the "query" command's description and instructions to the "help" command
  3. Run the console application
    1. Add two animals
      1. The first has a weight of 15
      2. The second has a weight of 13
    2. Add two guests
    3. Run the "query animal averageweight" command
      1. The console should output the number 14
    4. Run the "query animal count" command
      1. The console should output the number 2
    5. Run the "query guest count" command
      1. The console should output the number 2

Create a list on a collection in order to use the ForEach extension method

  1. The ForEach extension method is only available on collections of type List, which means that other collection types, such as Enumerables, must be turned into Lists before the ForEach method can be used on them
  2. In the ListUtil's Flatten method, change the IEnumerable parameter to be of type IEnumerable<string>
  3. Inside the method, replace the foreach loop with a lambda expression by first applying the ToList method and then the ForEach method
  4. The Flatten method should contain this code:

Use the ToList extension method to replace more foreach loops

  1. Replace foreach loops with the ToList and ForEach extension methods in the following methods:
    1. CageWindow.DrawAllItems
    2. MainWindow.CloseAllCageWindows (turn the dictionary's Values into a list)
    3. ConsoleUtil.WriteHelpDetail

Use the Cast method to define a type in order to use a lambda expression

  1. Untyped lambda expressions can only be used if the type of objects in a collection is known. If the objects' type is unknown, the Cast method can be used to cast all objects in a collection to a known type.
  2. Replace the contents of EnumUtil's Flatten method with the following code:
  3. Replace the existing loop in the Zoo constructor that creates the cages with this following code:
  4. Replace the existing loop in the CageWindow's window_Closed event handler with the ForEach extension method and a lambda expression
    1. First Cast the Children collection as type Viewbox
    2. Then use the ToList method
    3. Then use the ForEach method with a lambda expression

Use the Find extension method to replace control-break loops

  1. Not all foreach loops should be replaced with the ForEach extension method, as we saw with the Sum extension method replacing an accumulator loop. Another example of this is the Find extension method replacing control-break loops, which loop through a collection, testing each item for a certain condition. The loop breaks as soon as the first item matching the condition is found.
  2. Replace the loop in Animal's ConvertTypeToAnimalType method with the Find extension method
    1. Use the ToList method on the dictionary
    2. Find the Value that matches the passed-in type
    3. Return the Key
  3. Using extension methods here can simplify the method significantly. The entire method should now contain the following:

Use the Find extension method to replace another control-break loop

  1. Replace the existing foreach loop in the CageWindow's Remove method with the Find extension method
    1. First Cast the Children collection as type Viewbox
    2. Then use the ToList method
    3. Then use the Find method to return the Viewbox whose Tag is the passed-in cageable
  2. If the found Viewbox is not null, remove it from the grid's Children collection

Pass predicates to zoo's Find methods

  1. Create a new FindAnimal method on the zoo that takes an animal predicate as a parameter
    1. Pass the predicate in to the Find method
    2. Return the resulting animal
  2. Modify the FindGuest method on the zoo that takes a guest predicate as a parameter
    1. Pass the predicate in to the Find method
    2. Return the resulting guest
  3. Modify the FindEmployee method on the zoo that takes a employee predicate as a parameter
    1. Pass the predicate in to the Find method
    2. Return the resulting employee
  4. Call the new/modified methods in each place where the existing methods are called, passing in the appropriate predicate
  5. Remove the overloaded versions of the FindAnimal method that do not take a predicate
  6. Run the console application
    1. Set a breakpoint in the FindAnimal method that takes a predicate
    2. Add a few animals
    3. Call the show animal command
    4. After the breakpoint is hit, step through the code to ensure that the method returns the correct animal

Add a data grid to the query window

  1. Add a data grid control to the query window
  2. Add the following buttons to the query window
    1. Find dingoes
      1. Set the data grid's ItemsSource to a collection of all dingoes in the zoo
    2. Find the first pregnant animal
      1. Set the data grid's ItemsSource to a collection containing the first pregnant animal found in the zoo
    3. Find the first young guest
      1. Set the data grid's ItemsSource to a collection containing the first guest who is 10 years old or younger found in the zoo
  3. Run the WPF application
    1. Add a guest who is 9 years old and a guest who is 25 years old
    2. Add two dingoes, one of them pregnant
    3. Launch the query window
    4. Click the Find dingoes button - both dingoes should appear in the data grid
    5. Click the Find pregnant animals button - only the pregnant dingo should appear in the data grid
    6. Click the Find young guests button - only the guest who is 9 should appear in the data grid

Replace anonymous methods with typed and untyped lambda expressions

  1. In the MainWindow's AttachDelegates method, replace the code that attaches an anonymous method to the zoo's OnRemoveAnimal delegate with the following code:
  2. That code uses a lambda expression with a typed parameter to replace the anonymous method. But lambda expressions can infer parameter types, so the explicit typing can be removed, as in the following code:
  3. Follow the same steps to replace the anonymous method attached to the zoo's OnRemoveGuest delegate with a lambda expression
  4. Run the WPF application
    1. Add at least one guest and one animal
    2. Remove the guest and the animal
    3. Ensure that all fuctionality works as it did before

Replace anonymous methods with lambda expressions with a body

  1. Lambda expressions that contain more than one line of code need brackets surrounding its body
  2. In the MainWindow's AttachDelegates method, replace the code that attaches an anonymous method to the zoo's OnAddGuest delegate with the following code:
  3. Follow the same steps to replace the anonymous method attached to the zoo's OnAddAnimal delegate with a lambda expression
  4. Run the WPF application
    1. Add at least one guest and one animal
    2. Ensure that all functionality works as it did before

Replace anonymous methods with lambda expressions with multiple parameters

  1. Lambda expressions that take two or more parameters require parentheses around the parameters, in contrast to those that take only one parameter
  2. In the MainWindow's AttachDelegates method, replace the anonymous method attached to the zoo's OnBirthingRoomTemperatureChanged delegate with a lambda expression that takes two parameters - a birthing room and a double
  3. The lambda expression should look like the following code:
  4. Run the WPF application
    1. Change the birthing room temperature
    2. Ensure that all functionality works as it did before

Replace anonymous methods with lambda expressions that take no parameters

  1. Lambda expressions that take no parameters also require parentheses, like those that take two or more
  2. In the Zoo's AddGuest method, replace the anonymous method attached to the guest's GetVendingMachine delegate with a lambda expression that takes no parameters
  3. The lambda expression should look like the following code:
  4. Run the WPF application
    1. Add at least one animal and one guest
    2. Have the guest feed the animal
    3. Ensure that all functionality works as it did before

Replace other anonymous methods with lambda expressions

  1. Replace the anonymous methods attached to the following delegates and event handlers with lambda expressions
    1. moveTimer.Elapsed in Animal.CreateTimers
    2. hungerTimer.Elapsed in Animal.CreateTimers
    3. feedTimer.Elapsed in Guest.CreateTimers
    4. moneyPocket.OnBalanceChange in Wallet constructor
    5. Wallet.OnBalanceChange in Guest constructor
    6. CheckingAccount.OnBalanceChange in Guest constructor
    7. adoptedAnimal.OnHunger in Guest.AdoptedAnimal setter
    8. zoo.OnBirthingRoomTemperatureChange in ConsoleHelper.AttachDelegates
    9. cageable.OnImageUpdate in Cage.Add
    10. b168.OnTemperatureChange in Zoo constructor
    11. cage.OnImageUpdate in CageWindow constructor
    12. Dispatcher.Invoke in CageWindow constructor
    13. Dispatcher.Invoke in MainWindow.UpdateAnimalDisplay
    14. Dispatcher.Invoke in MainWindow.UpdateGuestDisplay
  2. Run the WPF application
    1. Add at least one animal and one guest
    2. Have the guest adopt the animal
    3. Change the birthing room temperature
    4. Open a cage window
    5. Ensure that all functionality (animals moving, guest automatically feeding animal, birthing room temperature updating, etc.) works as it did before

Add a birthing timer for pregnant animals

  1. Add an abstract GestationPeriod property to Mammal
    1. Chimpanzee returns 17
    2. Dingo returns 21
    3. Kangaroo returns 20
    4. Squirrel returns 11
    5. Platypus returns its incubation duration
  2. Add a birthTimer on Animal and mark it as nonserialized
    1. In the CreateTimers method, initialize the timer without setting the interval
    2. Attach a lambda expression to the birthTimer's Elapsed event
      1. In the method, set the animal's IsPregnant property to false
    3. In the constructors of Fish, Bird and Mammal, set the timer's interval to incubation duration or gestation period (in whole seconds)
  3. Enhance the IsPregnant property
    1. If the animal is being made pregnant, start the birth timer
    2. If the animal is being made not pregnant, stop the birth timer
    3. All of the code in the setter should only happen if the value is actually being changed
  4. Ensure that the MakePregnant method calls the property and not the field
  5. Run the WPF application
    1. Add a couple of pregnant animals to the zoo
    2. Ensure that the animals' text changes (the stars are removed) after the birth timer goes off
    3. Make the animals pregnant again
    4. Save the zoo and close the application before the animals give birth
    5. Re-run the application
    6. Animals should be deserialized successfully, but pregnant deserialized animals should never automatically give birth

Deserialize the birth timer

  1. Make the animal's CreateTimers method protected and virtual instead of private
  2. Add an overridden CreateTimers method to Fish, Bird and Mammal
    1. In the method, call base
    2. Then, move the code setting the birth timer's interval from the constructor to the CreateTimers method
    3. Last, if the animal is pregnant, start the timer
  3. Run the WPF application
    1. Add a couple animals, save the zoo and close the application
    2. Re-run the application
    3. Pregnant animals should deserialized successfully and be made not pregnant automatically when the timer goes off

Automatically birth an animal using the timer and delegates

  1. Create an IReproducer Action read/write auto-property on Animal called OnReadyToBirth
    1. Call the action in the lambda expression attached to the birth timer's Elapsed event and remove the existing code
  2. Add an IAnimalManager interface in the Animals project
    1. Add the OnAddAnimal Action read/write property
    2. Have the Zoo implement the interface
  3. Create a constructor for BirthingRoom
    1. It should take an IAnimalManager (pass in the zoo when the BirthingRoom is created)
    2. It should also take an Employee that should be set to the birthing room's vet
    3. The vet should be passed in to the zoo's constructor and then passed through to the birthing room
    4. Make Flora the vet
  4. In the birthing room's constructor, attach a lambda expression to the animal manager's OnAddAnimal action
    1. In the lambda, attach another lambda to the passed-in animal's OnReadyToBirth delegate
    2. In the inner lambda, call the vet's DeliverAnimal method, storing the result in a variable
  5. Run the WPF application
    1. Set a breakpoint on the line that calls DeliverAnimal
    2. Restart the zoo
    3. Add a pregnant animal
    4. Ensure that the breakpoint gets hit when the birth timer goes off

Add a birthed animal to the zoo using delegates

  1. The birthing room now needs to notify the zoo when a baby is born, so that the zoo can add it to its list
    1. Create an IReproducer Action read/write auto-property on the birthing room called OnReproducerBorn
    2. Call the action in the lambda attached to the OnReadyToBirth delegate, passing in the baby
    3. Attach a lambda expression to the birthing room's OnAnimalBorn action in the zoo's constructor
      1. In it, first check if the passed-in IReproducer is an Animal
      2. If it is, add the animal to the zoo

    This nested lambda expression can be challenging to write. If you struggle to do it in a single step, attach the existing BirthingRoom's BirthAnimal method to the OnReadyToBirth action before incorporating the method inline into a lambda expression.

  2. Remove the BirthAnimal and BirthAllPregnantAnimals methods from the zoo
  3. Run the WPF application
    1. Add at least one pregnant animal
    2. Wait for the animal to give birth
    3. Ensure that the baby animal appears in the listbox after it is born

Grading and Submission

  1. Build/compile your program
  2. Debug/test your program against the following rubric:
  3. PNG image of 4.3 Zoo rubric
  4. Make your code StyleCop-compliant
  5. Close your Visual Studio solution
  6. Compress your Visual Studio solution to a zip file
  7. Submit the zip file via Blackboard