6
\$\begingroup\$

Employee hierarchy in an Organization is classic example of modeling Composite design pattern. Following are the details that are to be modeled:

  1. There are two type of employees; Manager, and Developer.
  2. A Manager manages a team that can comprise managers, and developer.
  3. A Developer has fixed salary at given point of time and get work from the manager.
  4. The salary of a manager is the sum of salary of all the team members + 10K.
  5. We need to model work distribution, and salary of employees of the organization.

 

#include <string>
#include <vector>
#include <iostream>

using namespace std;

class Employee {
   private:
      string _name;
   public:
      Employee(string name_) {
         _name = name_;
      }
      string name() {
         return _name;
      }
      void virtual work() = 0; 
      float virtual salary() = 0;
};
// Common Interface to interact with Manager, and Developer
class Developer : public Employee {
   private:
      float _salary;
   public:
      Developer(string name_,float salary_) : Employee(name_),_salary(salary_) {}
      void work() {
         cout << "Developer " << name() << " Worked\n";
         // A developer works own.
      }
      float salary() {
         return _salary;
      }
};

class Manager : public Employee {
   private:
      vector<Employee*> _team; 
   public:
      Manager(string name_) : Employee(name_) {}
      void add(Employee * emp_) {
          _team.push_back(emp_);
      } 
      void work() {
         cout << "Manager " << name() << " Distributed Work\n"; 
         for(vector<Employee*>::iterator it = _team.begin(); it!=_team.end(); ++it) {
            (*it)->work();
         }
         // A manager distributes work in team members.
      }
      float salary() {
         float salary_ = 0.0;
         for(vector<Employee*>::iterator it = _team.begin(); it!=_team.end(); ++it) {
            salary_ += (*it)->salary()+15000;
         }
         return salary_;
         // Salary of a manager is derived from team members.
      }
};
int main() {
   Developer d1("developer1",10000);
   Developer d2("developer2",15000);
   Manager m1("manager1");
   m1.add(&d1);
   m1.add(&d2);
   Manager m2("manager2");
   m2.add(&m1);
   m2.work();
   return 0;
}
\$\endgroup\$
3
\$\begingroup\$

1. Don't use using namespace std

That's generally considered bad practice.

Rather use

using std::cout;
using std::cin;
using std::string;

if you want to avoid typing these out.

2. Use interfaces properly and don't mix them up with abstract class implementations

  1. Instead of simply declaring an abstract class, sort out specific interfaces in 1st place

    struct IEmployee {
          virtual const& string name() const = 0; // see 3. regarding const please
          virtual const float& salary() const = 0;
          virtual bool managesOtherEmployees() const = 0; // equivalent to isLeaf() in the
                                                          // common composite pattern
                                                          // examples
          virtual void work() = 0; 
    };
    
  2. Add functionality as needed:

    struct IManager {
        void add(IEmployee&) = 0;
        void delete(IEmployee&) = 0; // if you have functions to extend the
                                     // composition, you probably want to have
                                     // the reverse operation if reasonable.
                                     // Here it seems to be so, since managers 
                                     // sometimes need to fire team members.
    };
    
  3. Provide the abstract class for the common implementation

    class Employee : public IEmployee {
    private:
        string name_; // Don't use prefix '_' for symbols, these are reserved 
                        // for the compiler and standard library implementations (see 4.)
        float salary_;
    public:
        Employee(string name, float salary) : name_(name), salary_(salary) {}
        string name() const override {
            return name_;
        }
        float salary() const override {
            return salary_;
        }
        bool managesOtherEmployees() const override { return false; }
    };
    

2. leads to an implementation of Developer and Manager classes as follows:

class Developer : public Employee {
public:
    Developer(string name,float salary) : Employee(name,salary) {}
    void work() {
         cout << "Developer " << name() << " Worked\n";
         // A developer works own.
    }
};

class Manager : public Employee, public IManager {
private:
    vector<std::reference_wrapper<IEmployee>> team_; 
public:
    Manager(string name, float salary) : Employee(name,salary) {}
    bool managesOtherEmployees() const override { return true; }
    void add(IEmployee& emp) override {
        team_.push_back(std::reference_wrapper<IEmployee>(emp));
    } 
    void delete(IEmployee& emp) override {
        std::remove(team_.begin(),team_.end(),std::reference_wrapper<IEmployee>(emp));
    }
    void work() {
        cout << "Manager " << name() << " Distributed Work\n"; 
        for(auto& emp : team_) { // Use range based loops and auto whenever possible (see 5.)
            emp.work();
        }
        // A manager distributes work in team members.
     }
     float salary() const override {
        float accumulatedSalary = 0.0;
        for(const auto& emp : team_) { // Note const and reference again
            accumulatedSalary += emp.salary() +15000;
        }
        return accumulatedSalary;
        // Salary of a manager is derived from team members.
     }
};

3. Use const qualifiers as much as possible

If you're declaring a class operation (class member function), decide if this should change the class state in first place.
Append a const qualifier, if you decide it shouldn't.

Same for parameters and return values. Usually you don't have to worry about which method by value or by const reference will be more efficient.
With any sophisticated, modern c++ compiler, you can assume the most efficient emitted assembly code will be chosen.

Passing/returning by value, still might be prone to generate copies if RVO isn't applicable. Thus I simply prefer to be explicit about that.

4. Don't use symbol names prefixed with _

Symbol names prefixed with a single or double underscore (_) are preserved by the c++ standard for compiler internals and standard library implementations.

It's easy to come over that just using underscores as a postfix for symbols as indication of class member variables:

template<typename TValue>
class MyClass {
private:
     TValue myValue_;
public:
     MyClass(const TValue& myValue) : myValue_(myValue) {}
     const TValue& myValue() { return myValue_; } // standard getter signature
     void myValue(const & TValue value) { myValue_ = value; } // standard setter signature
};

The above pattern will serve 98% of cases unambiguously quite well.

5. Use range based loop syntax beyond c++98

Since c++11 standard was established, the language supports range based for() loops of the form

for(<type spec> el : <container_ref>) { /* ... */ }

You should use the auto keyword for <type spec>, it can be garnished with const and & as usual, and automatically evaluates a dereferenced Iterator when enumerating the Containers values.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.