Motivation

Imagine you want to represent a variety of devices, and comes up with the following UML diagram:

A UML diagram for the ComputingDevice ⇽ Abacus class (text version)

Note that it is possible to gather that e.g., the Instructions() method in the Abacus class is overriding the Instructions() method in the ComputingDevice class because it has the same signature: this can be the case only because it is overriding the inherited abstract method.

Your abstract classes are “completely abstract”, in the sense that all of their properties and methods are abstract, but it serves your purpose just well:

  • you do not want “ComputingDevices” to exist in isolation, but you want any class representing a computing device such as the Abacus, the Pascaline or the Turing Tumble to have an Instruction per seconds property, and a method to display instructions.
  • similarly, you want any “ElectronicalDevice” to have properties pertaining to their voltage and frequency, as well as a method to display a safety notice.

A class that is “completely abstract” actually forces you to enforce a series of constraints and is a good way of making sure that you are consistent e.g., with the naming of your methods, the accessibility of your properties, or the return type of your methods.

You implement it as follows:

abstract class ComputingDevice
{
  public abstract double IPS { get; set; }
  public abstract void Instructions();
}
using System;
 
class Abacus : ComputingDevice
{
  private double ips;
  public override double IPS
  {
    get { return ips; }
    set
    {
      if (value < 0 || value > 1000)
        throw new ArgumentException(
          "This is not plausible"
        );
      else
        ips = value;
    }
  }
  public string Material { get; set; }
 
  public Abacus(double ipsP, string materialP)
  {
    IPS = ipsP;
    Material = materialP;
  }
 
  public override void Instructions()
  {
    Console.WriteLine(
      "Refer to https://www.wikihow.com/Use-an-Abacus"
    );
  }
}
abstract class ElectricalDevice
{
  public abstract int Voltage { get; set; }
  public abstract int Frequency { get; set; }
  public abstract void SafetyNotice();
}
using System;
 
class USWashingMachine : ElectricalDevice
{
  private int voltage;
  public override int Voltage
  {
    get { return voltage; }
    set
    {
      if (value < 110 || value > 220)
      {
        throw new ArgumentOutOfRangeException();
      }
      else
      {
        voltage = value;
      }
    }
  }
  private int frequency;
  public override int Frequency
  {
    get { return frequency; }
    set
    {
      if (value != 50 && value != 60)
      {
        throw new ArgumentOutOfRangeException();
      }
      else
        frequency = value;
    }
  }
 
  public USWashingMachine(int vP, int fP)
  {
    Voltage = vP;
    Frequency = fP;
  }
 
  public override void SafetyNotice()
  {
    Console.WriteLine(
      "Refer to https://www.energy.gov/sites/"
        + "prod/files/2016/06/f32/"
        + "NFPA_DryerWasherSafetyTips.pdf"
    );
  }
}
using System;
 
class Program
{
  static void Main()
  {
    Abacus test0 = new Abacus(1.5, "Wood");
    test0.Instructions();
    USWashingMachine test1 = new USWashingMachine(120, 50);
    test1.SafetyNotice();
  }
}

(Download this code){.download-button}

Then, you would like to add a “Computer” class, but face an issue: classes can inherit only from one class directly, but of course a computer is both an electrical device and a computing device. A solution is to switch to interfaces.

Explanations

Interfaces are completely abstract classes: they do not implement anything, they simply force classes inheriting from them (we actually say that realizes them) to implement certain features.

In Diagram

Interfaces are prefixed by the «Interface» mention, and have all their properties and methods marked as abstract (so, in italics). A class can “inherits” from multiple interface (we say that it realizes multiple interfaces), and this is marked with an arrow with an open triangle end and a dashed line1.

A UML diagram for the ComputingDevice ◁┈ Computer class (text version)

An Implementation

Implementing such interfaces and their realization could be done as follows:

interface ComputingDevice
{
  double IPS { get; set; }
  void Instructions();
}
interface ElectricalDevice
{
  int Voltage { get; set; }
  int Frequency { get; set; }
  void SafetyNotice();
}
using System;
class Computer: ElectricalDevice, ComputingDevice
{
    private double ips;
    public double IPS
    {
        get { return ips; }
        set
        {
            if (value < 0)
                throw new ArgumentException(
                  "This is not possible."
                );
            else
                ips = value;
        }
 
    }
    private int voltage;
    public int Voltage
    {
        get { return voltage; }
        set
        {
            if (value < 110 || value > 220)
            {
                throw new ArgumentOutOfRangeException();
            }
            else
            {
                voltage = value;
            }
        }
    }
    private int frequency;
    public int Frequency
    {
        get { return frequency; }
        set
        {
            if (value != 50 && value != 60)
            {
                throw new ArgumentOutOfRangeException();
            }
            else
                frequency = value;
        }
    }
    public Computer(double ipsP, int voltageP, int frequencyP)
    {
        IPS = ipsP;
        Voltage = voltageP;
        Frequency = frequencyP;
    }
    public void Instructions()
    {
        Console.WriteLine(
          "Refer to your operating system manual."
        );
    }
    public void SafetyNotice()
    {
        Console.WriteLine("Refer to your manufacturer website.");
    }
}
using System;
 
class Program
{
    static void Main()
    {
        Computer test0 = new Computer(100000, 120, 50);
        test0.SafetyNotice();
        test0.Instructions();
    }
}

(Download this code){.download-button}

Note that

  • in the ComputingDevice and ElectricalDevice,
    • abstract class has been replaced by interface,
    • there is no need for the abstract keyword (all is abstract already!),
    • there is no need for the public keyword (everything has to be public),
  • in the Computer realization,
    • the class realizes two interfaces, simply separated by a comma:

      Computer: ElectricalDevice, ComputingDevice
    • there is no need for the override keyword,

A More Complicated Example

A UML diagram for the IAnimation ◁┈ Shape class (text version)

in this archive

Footnotes

  1. Note that, this time, since our code below does not override the methods and properties, there really is no need to repeat them the derived classes.