Search code examples
c#oopdomain-driven-designsolid-principlesooad

Issue in using Composition for “is – a “ relationship


I have system being developed for an HR system. There are Accountant employees and Programmer employees. For the first month of joining the company, the employee is not given any role. One employee can be an Accountant and a programmer at the same time. I have a design shown by the following code.

Now, I need to enhance the system by implementing a new functionality:

Terminate all Accountants. (Terminate means set status of employee as IsActive = false). The issue is I cannot set all accountants directly as inactive without checking. I need to check whether he has got any other role.

How to remodel these classes in order to do the terminate function more natural OO ?

UPDATE

I am looking for an answer that has EF Database First solution model and database schema for @AlexDev answer.

C# Code

List<Accountant> allAccountants =  Get All accountants from database

public class Employee
{
    public int EmpID { get; set; }
    public DateTime JoinedDate { get; set; }
    public int Salary { get; set; }
    public bool IsActive { get; set; }
}


public class Accountant : Employee
{
    public Employee EmployeeData { get; set; }
}

public class Programmer : Employee
{
    public Employee EmployeeData { get; set; }
}

enter image description here

@AlexDev Answer

public class Employee
{
...
IList<Role> Roles;
bool isActive;

public void TerminateRole(Role role)
{
    Roles.Remove(role);
    if(Roles.Count == 0)
    {
        isActive = false;
    }
}
}

public class Role
{
 abstract string Name { get;}
}

public class ProgrammerRole : Role
{
 override string Name { get { return "Programmer"; } }
}

REFERENCE

  1. DDD Approach to Access External Information
  2. Prefer composition over inheritance?
  3. Inheritance vs enum properties in the domain model
  4. Entity Framework: Get Subclass objects in Repository

Solution

  • To use the structure you are using you would need multiple inheritance for someone who is an accountant and a programmer, besides new roles might be added to the system, and that doesn't exist in C#. You should consider a different design. One possibility:

    public class Employee
    {
        ...
        IList<Role> Roles;
        bool isActive;
    
        public void TerminateRole(Role role)
        {
            Roles.Remove(role);
            if(Roles.Count == 0)
            {
                isActive = false;
            }
        }
    }
    
    public class Role
    {
        abstract string Name { get;}
    }
    
    public class ProgrammerRole : Role
    {
        override string Name { get { return "Programmer"; } }
    }
    

    Then you can subclass Role for each type, and you can decide to terminate just one role, or all of them.