Issue in using Composition for “is – a “ relationship

此生再无相见时 提交于 2019-12-07 01:06:14

问题


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; }
}

@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

回答1:


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.




回答2:


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);
    }
}



回答3:


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




回答4:


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#)



来源:https://stackoverflow.com/questions/11759120/issue-in-using-composition-for-is-a-relationship

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!