Exercise: Abstraction

Finish a city bike share. Make Vehicle an abstract class that every kind of vehicle prices for itself, give the electric ones the IChargeable contract, and write a RideService that gets its time from an IClock.

Journeyman · C# · OOP · Updated

A city bike share rents out bikes, e-bikes and scooters. Each kind has its own prices, and the electric ones have batteries that run down as they're ridden. You'll use all three tools from this chapter: an abstract class, an interface, and a dependency injected through an interface.

Get the exercise code

Open this lesson's GitHub repository, github.com/ask-codey/exercise-abstraction, also linked from the Exercise box beside the lesson. Click Code, then Download ZIP, and unzip it into your bootcamp folder. Or clone it with Git. Then open BikeShare.slnx in Visual Studio.

File What's in it
BikeShare/Vehicle.cs Every kind of vehicle, for you to finish
BikeShare/Bike.cs A finished kind of vehicle, to use as an example
BikeShare/EBike.cs, Scooter.cs The electric vehicles, for you to finish
BikeShare/IChargeable.cs The contract for anything with a battery, finished
BikeShare/IClock.cs, SystemClock.cs The clock's contract and the real clock, finished
BikeShare/RideService.cs Starts and ends rides, for you to finish
BikeShare.Tests Tests for all of it, with a fake clock

The requirements are in the comment at the top of each file you finish.

What to do

Step What it does
1. Vehicle Make it abstract, with abstract UnlockFee and PricePerMinute. RideCost(minutes) is the unlock fee plus the price for every minute.
2. EBike, Scooter Their prices, and the IChargeable contract: a battery that starts at 100%, drains 1% (e-bike) or 2% (scooter) a minute but never below 0%, and charges back to 100%.
3. RideService constructor It's given an IClock, but ignores it. Keep the one it's given.
4. StartRide, EndRide Remember when each ride starts. When it ends, charge for every minute, counting part of a minute as a whole one, and drain the battery if it has one.
5. NeedCharging The vehicles in a fleet that have a battery under 20%.

Run the program with Ctrl+F5 to see the prices, and the tests with Test > Run All Tests.

Hints

  • Start with Vehicle. Once it's abstract, EBike and Scooter stop compiling until they override its abstract members. That's the compiler holding them to the rules. Press Ctrl+. on the red line and choose Implement abstract class to get started.
  • Base class first, then interfaces: class EBike : Vehicle, IChargeable.
  • EndRide doesn't care what kind of vehicle it has. vehicle.RideCost(minutes) prices any kind, and vehicle is IChargeable battery tells it whether there's a battery to drain.
  • The tests move a fake clock forward: they start a ride, add 12 minutes to the clock, and end it. That only works if RideService asks the clock it was given.
  • A Dictionary<string, DateTime> keyed by the vehicle's Id can remember the start times. TryAdd and Remove return whether they did anything, which answers the "already on a ride" and "not on a ride" rules.
  • To refuse, stop the method with throw new InvalidOperationException("B1 is already on a ride.");. The program stops there with that message, unless some code catches it. The next chapter, Files and Exceptions, covers exceptions properly.

When you're done

Every test passes, and the program prints:

B1: a 20-minute ride costs $4.00
E1: a 20-minute ride costs $8.00
E1: 55% left after 45 minutes
S1: a 20-minute ride costs $6.50
S1: 10% left after 45 minutes
Needs charging: S1

Look at EndRide: it works for bikes, e-bikes and scooters without asking which is which, and it would work for a new kind of vehicle tomorrow, battery or not.

Submit your exercise

The exercise is how you finish this lesson. When your work is done, put it on GitHub in a repo of your own, then submit that repo's address here.

  1. On GitHub, click +, then New repository. Name it (for example exercise-abstraction), choose Public, and click Create repository. Leave out the README: your folder already has one.

  2. In a terminal in your exercise folder (Git Bash on Windows), send your work to your new repo. If you downloaded the ZIP:

    git init -b main
    git add .
    git commit -m "My solution"
    git remote add origin https://github.com/your-name/exercise-abstraction.git
    git push -u origin main
    

    If you cloned the starter instead, point your copy at your own repo, then commit and push:

    git remote set-url origin https://github.com/your-name/exercise-abstraction.git
    git add .
    git commit -m "My solution"
    git push -u origin main
    
  3. Paste your repo's address, such as https://github.com/your-name/exercise-abstraction, into Submit your exercise on this page. The site checks that the repo is public and holds your commits, and then the lesson is complete.

Changed something after you submitted? Push it, and submit again: your latest submission is the one that counts.

So what

The abstract class makes every vehicle price itself, the interface marks the ones with batteries, and IClock lets the tests decide what time it is. Three small contracts, and the code that uses them never needs to know more.

Key terms

Related