abstract

How To Design An Abstraction

 

How do we design a useful abstraction? Our notion of a good abstraction evolves as we encounter more specific types.

OK, let’s investigate with an example:

Say we have a system modelling cars. Initially, the program only caters to petrol cars—we create a PetrolCar class. PetrolCar must keep track of how much petrol remains in the tank. To that end, we could create a property called LitresOfPetrolRemaining on PetrolCar.

  PetrolCar petrolCar = new PetrolCar();
  petrolCar.FillUp();
  petrolCar.LitresOfPetrolRemaining.Should().Be(50);
  petrolCar.Drive(100);
  petrolCar.LitresOfPetrolRemaining.Should().Be(40);

OK, that works well.

We receive a change request: Our application should also be able to model Diesel cars, and these must be usable wherever we currently accept PetrolCars.

It’s time for us to do some designing. We could have a new type, DieselCar. Since we want to be able to use PetrolCar and DieselCar interchangeably, we need a common abstraction, an interface or abstract class, that we shall call Car.

Car will contain the declarations for common methods and properties:

  • void FillUp()
  • void Drive(int kilometres)
  • int LitresOfPetrolRemaining

One of these looks a bit off. LitresOfPetrolRemaining is fine for PetrolCar but doesn’t work well for DieselCar:

  var dieselRemaining = dieselCar.LitresOfPetrolRemaining; // ?!?

OK, we need a common abstraction that will work for PetrolCar and DieselCar. How about LitresOfFuelRemaining?

  Car petrolCar = new PetrolCar();
  petrolCar.FillUp();
  var petrol = petrolCar.LitresOfFuelRemaining;

  Car dieselCar = new DieselCar();
  dieselCar.FillUp();
  var diesel = dieselCar.LitresOfFuelRemaining;

That works.

Electric cars are becoming popular, and requirements have changed again. We have been asked to modify the system to include electric cars. We will include a new type, ElectricCar, that extends abstraction Car.

Electric cars do not use liquid fuel the way petrol and Diesel cars do. For propulsion, they use electric charge stored in batteries. The existing abstraction in Car does not work for ElectricCar:

  var electricCharge = electricCar.LitresOfFuelRemaining; // ?!?

Yuch—that will not do. We will need to come up with a better abstraction that works for all types of Cars. 

How about PercentOfFuelRemaining?

Yes, that may work. Fuel is still fuel even when we are holding it in electric charge. Using percentages (0% – empty, 100% – full) instead of litres allows us to use all fuel types. Nice! 

Let’s see how it works out with the different kinds of Car models:

  Car petrolCar = new PetrolCar();
  petrolCar.FillUp();
  petrolCar.PercentOfFuelRemaining.Should().Be(100);
  petrolCar.Drive(100);
  petrolCar.PercentOfFuelRemaining.Should().Be(80);

  Car dieselCar = new DieselCar();
  dieselCar.FillUp();
  dieselCar.PercentOfFuelRemaining.Should().Be(100);
  dieselCar.Drive(300);
  dieselCar.PercentOfFuelRemaining.Should().Be(60);

  Car electricCar = new electricCar();
  electricCar.FillUp();
  electricCar.PercentOfFuelRemaining.Should().Be(100);
  electricCar.Drive(200);
  electricCar.PercentOfFuelRemaining.Should().Be(50);

It can be tricky to design an abstraction that works well with all specific types. However, we can usually achieve it by reflecting on the common aspects of the various particular implementations.

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply