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,EBikeandScooterstop 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. EndRidedoesn't care what kind of vehicle it has.vehicle.RideCost(minutes)prices any kind, andvehicle is IChargeable batterytells 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
RideServiceasks the clock it was given. - A
Dictionary<string, DateTime>keyed by the vehicle'sIdcan remember the start times.TryAddandRemovereturn 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.
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.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 mainIf 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 mainPaste 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.