How to make use of a fixed hierarchy of classes for various similar entities, like employee-Manager for company 1,2,3

80 Views Asked by At

I have a class say Employee and a subclass (of Employee) say Manager.

There are various methods (and data member) in Employee class related to payroll, employee details, appraisal, etc. Similarly for Manager there are various methods.

class Employee {
    string name, address, empId;
    int salary;
    Dept dept;
public:
    void [Get/Set]Name();
    void GetEmpid();
    void SetSalary();
    virtual void StartOnboarding();
    virtual void startOfboarding();
    virtual void startAppraisal();
};

class Manager : public Employee {
    list teamList;
    int appraisalBudget;    
public:
    int [Get/Set]AppraisaBudget();
    int [Add/Delete]EmployeeInTeam(emp);
    void StartTeamAppraisal();
};

I have many companies says Company1, Company2, etc. and more can be added in future. An employee or manager can be of company1, company2, etc.

Employee and Manager for company1 will have similar relationship as above, same for company2. But they will have different ways to handle methods available. Like startOnboarding() for employee of company1 will be different than employee of company2, similarly StartTeamAppraisal() for manager of company1 will be different than manager of company2.

Now one way to model this scenario is to create different sub-classes of Employee for every company like EmployeeCompany1, EmployeeCompany2, and similarly sub-classes of Manager for every company like ManagerCompany1, ManagerCompany2, etc.

Employee classes for different company:

class EmployeeCompany1 : public Employee {
    int tasksDone;
    int clientsMeetingsDone;
    int appreciationsReceived
public:
        // Company1 specific implementation of following
    virtual void StartOnboarding() { ... }
    virtual void startOfboarding() { ... }
    virtual void startAppraisal()  { ... }
};

class EmployeeCompany2 : public Employee {
    int bugSolved;
    int featureDeveloped;
public:
        // Company2 specific implementation of following
    virtual void StartOnboarding() { ... }
    virtual void startOfboarding() { ... }
    virtual void startAppraisal()  { ... }
};

But in above case EmployeeCompany[1,2,..] will be subclass of Employee and Manager is already a subclass of Employee but they both(EmployeeCompany[1,2..] and Manager) are not on the same level as per the behaviour is considered. So there is some flaw in the design.

If we do similar for ManagerCompany1, than It has to be a subclass of EmployeeCompany1, but it also has to be a subclass of Manager, like below -

class ManagerCompany1 : public EmployeeCompany1, public Manager {
protected:
    int company1specificData;
public:
    virtual void StartTeamAppraisal() {
    // make use of data member of EmployeeCompany1 and other things specific to company1
    }
};


class ManagerCompany2 : public EmployeeCompany2, public Manager {
protected:
    int company2specificData;
public:
    virtual void StartTeamAppraisal() {
    // make use of data member of EmployeeCompany2 and other things specific to company2
    }
};

Questions:

  1. I feel my design has flaws and the scenario I described would be a well defined scenario in object oriented design literature.

  2. I am looking for community's help for a better design approach to model above scenario in c++.

2

There are 2 best solutions below

0
Amit On

I think templates are good solution for this problem. Try to figure out exactly what is different between different companies, and generate a common interface.

class Company {
public:
  struct StartOnboardingParams { ... };
  virtual static StartOnboardingParams getSOBParams(...);
};

class Company1 : public Company { ... };

template <typename CompanyType>
class Employee {
static_assert(std::is_base_of<Company, CompanyType>::value, "CompanyType must be derived from Company");
public:
   virtual void StartOnboarding() { /* here you can use Company::getSOBParams */ }
}

template <typename Company>
class Manager : public Employee<Company> { ... };

In this case, Manager of Company1 inherits only from Employee of Company1 and not from any other company's employees, which solves the flaw in your design.

The exact assumptions and contents of the templated subject may be non-trivial and require some changes in your interface, but in general this approach should be helpful.

0
Peter On

From a design perspective, your thinking seems backward to me.

An employee is a person. A company (or a department) is not part of a person - it is a distinct (logical) entity that manages a collection of employees, assigns/revokes management rights, pays salary, and initiates on-boarding and off-boarding processes. The company also associates, for its own purposes, things like employee ID with each employee - it doesn't (usually) tattoo an ID on every employee's forehead, so that ID is not a property of the employee.

Also, a person may have more than one job - for example, working part time for multiple companies. However, the relationship of that person with Company1 does not (specifically) affect the relationship of that person with Company2. A person may be a Manager of Company1 but (say, if Company1 is bankrupt and not paying salaries) and also be a janitor for Company2.

The only real difference between a Manager and other employees is that they have different roles. The Department has a number of roles, and associates one or more roles with every employee.

With that in mind, I'd do something like

 class Employee
 {
      // attributes, setters, getters for name, address
 };

 class Department
 {
     public:
     void OnBoard(Employee &);    // set up pay, employee ID, etc
     void Offboard(Employee &);
     
     void PayAllEmployees();  //  pay every employee

     RoleDescription CreateRole(RoleAttributes);   // different roles for staff, cleaners, managers

     void AssignRole(EmployeeID, RoleDescription);
     void UnassignRole(EmployeeID, RoleDescription);

 };

and then construct a Company as a collection of Departments.

If different departments or companies have different procedures (say, for induction, onboarding, offboarding, etc) then make relevant functions virtual, and override them in derived classes. By it is not one class per department (or one class per company). It is one class per category of department (or company). For example, an accounting department may have different procedures than a department concerned with factory operations. An auditing company may have a different set of departments than a car manufacturer.

As an alternative to virtual functions, you can also use templates (so, for example, a company may be composed of a collection of departments, each department may have a set of distinct attributes).