To implement the problem, my choice was to extend an abstract superclass Employee:

UML class diagram: inheritance of abstract class Employee

UML class diagram: inheritance of abstract class Employee


The reason for not to choose the implementation of an interface is, that the abstract class Employee has a lot of attributes and methods which can be reused in both subclasses. The code is more compact:

  1. abstract class Employee                                // abstract class
  2. {                       
  3.     private static int cntID = 1000;                   // reuseable code
  4.  
  5.     protected int ID;
  6.     protected String name;
  7.     protected String occupation;
  8.     protected String workPlace;
  9.  
  10.     public Employee() { }
  11.  
  12.     public Employee(String name, String occupation, String workplace)
  13.     {
  14.         ID = ++cntID;
  15.         this.name = name;
  16.         this.occupation = occupation;
  17.         this.workPlace = workplace;
  18.     }
  19.     public int getID()
  20.     {
  21.         return ID;
  22.     }
  23.     public String getName()
  24.     {
  25.         return name;
  26.     }
  27.     public String getOccupation()
  28.     {
  29.         return occupation;
  30.     }
  31.  
  32.     public abstract String determineWorkPlace();    // only this has to be implemented by subclasses
  33. }


The concrete subclass FulltimeEmployee takes over every attribute and member, only the abstract method determineWorkPlace() of superclass Employee has to be defined:

  1. class FulltimeEmployee : Employee
  2. {
  3.     public FulltimeEmployee(String name, String occupation, String workplace)
  4.         : base(name, occupation, workplace)
  5.     { }
  6.  
  7.     public override String determineWorkPlace()     // Method has to be implemented by subclass
  8.     {        
  9.         return workPlace;
  10.     }
  11. }


The second concrete subclass Apprentice has to define the method determineWorkPlace() too. The method getOccupation() is overridden for demonstration purposes:

  1. class Apprentice : Employee
  2. {
  3.     private List<DateRange> dateRanges;           // apprentice visits vocational school
  4.  
  5.     public Apprentice(String name, String occupation, String workplace, List<DateRange> dateRanges)
  6.         : base(name, occupation, workplace)
  7.     {
  8.         this.dateRanges = dateRanges;
  9.     }
  10.  
  11.     // method is overridden for demonstration purposes
  12.     public String getOccupation()
  13.     {
  14.         return "apprentice " + occupation;
  15.     }
  16.  
  17.     public override String determineWorkPlace()     // has to be implemented by subclass
  18.     {
  19.         DateTime now = DateTime.Now;   // today
  20.         foreach (DateRange dateRange in dateRanges)
  21.             if (dateRange.Includes(now))
  22.                 return "vocational school";
  23.         return workPlace;
  24.     }
  25. }


For avoiding code dublication the two objects of subclasses of Employee are assigned to an List of type Employee. It can be iterated by a foreach loop:

  1. class Program
  2. {
  3.     static void Main(string[] args)
  4.     {
  5.         Apprentice apprentice = new Apprentice("apprentice", "developer", "R101",
  6.             new List<DateRange> {
  7.                 new DateRange(new DateTime(2019, 8, 12), new DateTime(2019, 8, 30)),
  8.                 new DateRange(new DateTime(2019, 11, 11), new DateTime(2019, 11, 29))
  9.             });      
  10.  
  11.         FulltimeEmployee fulltimeEmployee = new FulltimeEmployee("full-time employee", "developer", "R102");
  12.  
  13.         // no repeating of code by the use of loop
  14.         List<Employee> employees = new List<Employee> { apprentice, fulltimeEmployee };
  15.         foreach (Employee employee in employees)
  16.         {
  17.             Console.WriteLine("ID: " + employee.getID()
  18.                     + "\tName: " + employee.getName()
  19.                     + "\tOccupation: " + employee.getOccupation()
  20.                     + "\tWorkplace: "
  21.                     + employee.determineWorkPlace()
  22.             );
  23.         }
  24.         Console.ReadKey();
  25.     }
  26. }


The output in case of actual date is in range of vocational school block will be:

ID: 1001    Name: apprentice            Occupation: apprentice developer    Workplace: vocational school
ID: 1002    Name: full-time employee    Occupation: developer    Workplace: R102



The additional helperclass is shown below:

  1. class DateRange
  2. {
  3.     public DateRange(DateTime start, DateTime end)
  4.     {
  5.         Start = start;
  6.         End = end;
  7.     }
  8.  
  9.     public DateTime Start { get; private set; }
  10.     public DateTime End { get; private set; }
  11.  
  12.     public bool Includes(DateTime value)
  13.     {
  14.         return (Start <= value) && (value <= End);
  15.     }
  16. }
Last modified: Friday, 18 October 2019, 9:28 AM