7

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
Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418
  • The concept of termination (isActive) is for an employee; not for a role. Consider an employee with two roles. Though all accountants are terminated he will continue to be an active employee. – LCJ Aug 01 '12 at 12:39
  • 1
    The first priority should be to make sure that you get this right at the SQL level (table design). After that I would try to model what is in the database as an object. However, do not go nuts with the hierarchy tree. There is an impedance mismatch between OOP and SQL. The thing is that SQL is heavily based on sets and math. OOP is largely hype; its fundamental principles conflict and properly designing a Rectangle class takes forever :). Whenever you have a conflict between SQL and OOP, make OOP bend to accommodate SQL, not the other way around. I would use stored procs and static functions. – Hamish Grubijan Jun 11 '13 at 21:54
  • References: http://programmers.stackexchange.com/questions/194560/a-design-decision-in-composition-or-aggregation?rq=1 – LCJ Jun 19 '13 at 08:48

4 Answers4

6

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.

AlexDev
  • 4,049
  • 31
  • 36
  • So what should happen when someone with two roles is terminated? – AlexDev Aug 01 '12 at 12:39
  • The employee should NOT be terminated. He will continue as active. One record in EmployeeRole should be deleted. – LCJ Aug 01 '12 at 12:40
  • 2
    Edited. Another option would be to have isActive as a readonly property like bool isActive { get { return Roles.Count > 0; } } but that won't be mapped to the database. – AlexDev Aug 01 '12 at 12:47
  • Thanks. The updated answer makes sense. Is it possible to achive this model using EF Database First? How it would be? – LCJ Aug 01 '12 at 16:36
  • Probably since it's a simple mapping, but I haven't used EF. Would an example with fluent nhibernate be useful? – AlexDev Aug 01 '12 at 20:28
  • Can you please tell how the model will look like in NHibernate? – LCJ Jun 20 '13 at 06:47
  • 1
    @Lijo I posted it in a new answer. – AlexDev Jun 24 '13 at 12:42
1

I'm writing a new answer since from the schema you added to the question I'm assuming you won't be subclassing Role. Also if you are using NHibernate don't forget to use public virtual properties.

public class Employee
{
    ...
    public virtual IList<Role> Roles { get; set; }
    public virtual bool isActive { get; set; }

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

public class Role
{
    public virtual int RoleID { get; set; }
    public virtual string Name { get; set; } 
}

And mappings:

public class EmployeeMap : ClassMap<Employee>
{
    public EmployeeMap()
    {
        Id(x => x.EmpId);
        Map(x => x.JoinedDate)
        Map(x => x.Salary);
        Map(x => x.IsActive);
        HasManyToMany(x => x.Roles).Cascade.AllDeleteOrphan().Table("EmployeeRole")
    }
}

public class RoleMap : ClassMap<Role>
{
    public RoleMap()
    {
        Id(x => x.RoleID);
        Map(x => x.RoleName);
    }
}
AlexDev
  • 4,049
  • 31
  • 36
0
public abstract class AbstractEmployee
{
    ...
    public abstract bool IsActiveAccountant { get; set; }
    public abstract bool IsActiveProgrammer { get; set; }
    public bool IsActive() { get { return bitwise or of all roles; } }
}

public class NewEmployee : AbstractEmployee
{
    ...
    public override bool IsActiveAccountant { get; set; }
    public override bool IsActiveProgrammer { get; set; }
}

public class Programmer : AbstractEmployee
{
    ...
    public override bool IsActiveAccountant { get; set; }
    public override bool IsActiveProgrammer { get; set; }
}

Cons:

  • with every new system-wide role added you have to modify classes

Pros:

  • you dont need to search for accountants
  • programmers can have empty implementation of IsActiveAccountant, because this role is inactive for them anyway
  • NewEmployee can have many roles at the same time

If overhead from introducing new roles is significant, I would stick with searching

Roman Saveljev
  • 2,544
  • 1
  • 21
  • 20
  • This doesn't simulate a real world scenario. I don't like IsActiveAccountant inside Programmer class. – LCJ Aug 01 '12 at 13:06
  • "The issue is I cannot set all accountants directly as inactive without checking" - you would do checking in real world would not you? :) I was merely trying to solve your issue with having to check (even though personally I dont see it an issue). Having limited list of roles on an object and removing one of them implies check. My apologies if your actual question was not about removing a need to do check – Roman Saveljev Aug 01 '12 at 13:11
0

From my answer in Prefer composition over inheritance?

I will first start with the check - whether there exists an "is-a" relationship. If it exists I usually check the following:

Whether the base class can be instantiated. That is, whether the base class can be non-abstract. If it can be non-abstract I usually prefer composition

E.g 1. Accountant is an Employee. But I will not use inheritance because a Employee object can be instantiated.

E.g 2. Book is a SellingItem. A SellingItem cannot be instantiated - it is abstract concept. Hence I will use inheritacne. The SellingItem is an abstract base class (or interface in C#)

Community
  • 1
  • 1
LCJ
  • 22,196
  • 67
  • 260
  • 418