16.9 USING MIXIN CLASSES


16.9 USING MIXIN CLASSES

As should be clear from the discussion so far, repeated inheritance complicates a C++ program and can make its extension particularly troublesome. Let's say you wish to extend a previously written program that did not have repeated inheritance and that your extension creates an inheritance loop because you bring together a couple of what seemed like disparate classes into a new derived class. For the new program to work correctly, you will have to go back into the old program and re-engineer it by declaring the common base to be virtual, by changing all the downstream constructors and copy constructors, and so on. These problems can often be avoided by using what are known in C++ as mixin classes. Ideally, a mixin class in C++ is very much like an interface in Java-an abstract class that only consists of pure virtual functions.

To illustrate the idea of mixin classes, let's first quickly review the inheritance hierarchy of the previous section. Recall, our program there kept a record of what departments were supervised by which manager. A Manager supervised only one department, while an ExecutiveManager could supervise an arbitrary number of departments. Each department kept a record of its productivity for the previous two years and the managers were promoted only after a certain minimum number of years on the job and only if their productivity was above a certain threshold. The same was true of SalesPersons and SalesManagers, although their productivity was measured taking into account the sales-related numbers.

We will now present an alternative design for achieving the same overall functionality as that described in the previous section. Our new design will use the more extendible hierarchy of Figure 16.14 which uses mixin classes and has no inheritance loops for the implementation code.

click to expand
Figure 16.14

In this new design, the classes ManagerType and SalesType are abstract-no objects will ever be made of these two classes. The responsibility of these classes is to impart some additional behavior to other classes. So when the behavior represented by the ManagerType class is combined with the behavior of the Employee class, we get the Manager class. On the other hand, when we impart the behavior represented by the ManagerType class to the SalesPerson class, we get the SalesManager class. By the same token, when we impart the behavior represented by the SalesType abstract class to the Employee class, we get the SalesPerson class. Classes such as ManagerType and SalesType are the mixin classes.

For our example here, we will assign the following behaviors to the mixin classes:

      class ManagerType {      protected:          virtual double productivityGainYtoY() = 0;          virtual int getYearsInManagement() = 0;          virtual bool getEmployeeSatisfaction() = 0; };      class SalesType {      protected:           virtual int getYearsInSales() = 0;           virtual double salesGainYtoY() = 0;      }; 

Based on the discussion in the last section, these behaviors make sense. As was said earlier, managers will be judged by the year-to-year productivity gains in the departments they head, by the job satisfaction experienced by the employees in those departments, and by the number of years in rank. So calculating these measures of performance represents important behaviors for a program that keeps track of managers. Similarly, for the SalesType mixin class.

The code that follows pulls in the header file MI_Utilities.h we created in the previous section for the enumerations and the Department class needed here. As was the case with the implementation in the previous section, it is also true here that the data members of type Department will carry different meanings in different classes, and sometimes different meanings within the same class.

It is interesting to note that much of the code that was written for the repeated-inheritance based implementation of the previous section remains unchanged in the program shown below. To highlight this point, the implementation of important functions like ProductivityGainYtoY() for the various classes remains exactly the same as before, as can be seen by the functions in lines (A), (B), and (C). The source code follows:

 
//Mixin.cc #include "MI_Utilities.h" ///////////////////////// class Employee ///////////////////////// class Employee { protected: string name; string address; EducationLevel education; int yearsExperience; // years on job Department dept; PeopleSkill pSkill; // needed for sales positions Leadership leadership;// needed for management public: Employee( string nam, string add, EducationLevel edLevel, Department depart ) : name( nam ), address( add ), education( edLevel ), yearsExperience( 0 ), dept( depart ), pSkill( pUnknown ), leadership( lUnknown ) {} // since senior level managers do not belong to // any particular department, the next constructor // is for such employees Employee( string nam, string add, EducationLevel edLevel ) : name( nam ), address( add ), education( edLevel ), yearsExperience( 0 ), pSkill( pUnknown ), leadership( lUnknown ) {} void setYearsExperience( int yy ) { yearsExperience = yy; } void setPeopleSkill( PeopleSkill skill ) { pSkill = skill; } string getName() const { return name; } string getAddress() const { return address; } EducationLevel getEducationLevel() const { return education; } virtual void print(){ cout << name << endl; cout << address << endl; cout << EducationLevels[ education ] << endl; cout << "Years in job: " << yearsExperience << endl; cout << "People skill: " << PeopleSkills[ (int) pSkill ] << endl; cout << "Leadership quality: " << LeaderQualities[ (int) leadership ]; cout << endl; } virtual ~Employee() {} }; ////////////////////// mixin class ManagerType ////////////////////// class ManagerType { protected: virtual double productivityGainYtoY() = 0; virtual int getYearsInManagement() = 0; virtual bool getEmployeeSatisfaction() = 0; }; //////////////////////////// class Manager ////////////////////////// class Manager : public Employee, public ManagerType { Department dept; // note same name as dept in Employee // but different meaning. Here it is // dept supervised and not // department worked in protected: int yearsInManagement; bool employeeSatisfaction; public: Manager( string name, string address, EducationLevel education, Department aDept ) : Employee( name, address, education ), dept( aDept ), yearsInManagement( 0 ), employeeSatisfaction( false ) {} // Since senior-level managers do not belong to any particular // department, the next constructor is actually for them Manager( string name, string address, EducationLevel education ) : Employee( name, address, education ) {} double productivityGainYtoY() { //(A) int lastProd = dept.getProductionLastYear(); int PrevProd = dept.getProductionPreviousYear(); return 100 * ( lastProd-prevProd ) / (double) prevProd; } void setDepartment( Department dept ){ this->dept = dept; } int getYearsInManagement() { return yearsInManagement; } void setYearsInManagement( int y ) { yearsInManagement = y; } bool getEmployeeSatisfaction() { return employeeSatisfaction; } void setEmployeeSatisfaction( bool satis ) { employeeSatisfaction = satis; } virtual bool readyForPromotion(){ return ( ( yearsInManagement >= MinYearsForPromotion ) ? true : false ) && ( productivityGainYtoY() > 10 ) && employeeSatisfaction; } viod print() { Employee::print(); dept.print(); } ~Manager() {} }; /////////////////////// class ExecutiveManager ////////////////////// // An ExecutiveManager supervises more than one department class ExecutiveManager : public Manager { short level; vector<Department> departments; public: // Needed in the second example for type conversion // from Manager to ExecutiveManager ExecutiveManager() : Manager(" ", " ", eUnknown ), level( 0 ) {} ExecutiveManager( string name, string address, EducationLevel education, short level ) : Manager( name, address, education ), level( level ) { departments = vector<Department>(); } void addDepartment( Department dept ) { departments.push_back( dept ); } void setLevel( int lvl ) { level = lvl; } // overrides Manager's productivityGainYtoY(): double productivityGainYtoY(){ //(B) double gain = 0.0; vector<Department>::iterator iter = departments.begin(); while ( iter != departments.end() ) { int lastProd = iter->getProductionLastYear(); int prevProd = iter->getProductionPreviousYear(); gain +;= ( lastProd-prevProd ) / prevProd; } return gain/departments.size(); } void print() { Employee::print(); cout << "Departments supervised: " << endl; vector<Department>::iterator iter = departments. begin(); while ( iter != departments.end() ) iter+;+;->print(); } ~ExecutiveManager(){} }; /////////////////////// mixin class SalesType ////////////// ///////// class SalesType { protected: virtual int getYearsInSales() = 0; virtual double productivityGainYtoY() =0; }; ///////////////////////// class SalesPerson ///////////////////////// class SalesPerson : public Employee, public SalesType { int salesLastYear; int salesPreviousYear; protected: int yearsInSales; public: SalesPerson( string name, string address, EducationLevel education ) : Employee( name, address, education, Department( "Sales" )), salesLastYear( 0 ), salesPreviousYear( 0 ) yearsInSales( 0 ) {} int getSalesLastYear() { return salesLastYear; } void setSalesLastYear( int sales ) { salesLastYear = sales; } int getSalesPreviousYear() { return salesPreviousYear; } void setSalesPreviousYear( int sales ) { salesPreviousYear = sales; } int getYearsInSales() { return yearsInSales; } void setYearsInSales( int y ) { yearsInSales = y; } double productivityGainYtoY() { //(C) return 100 * ( salesLastYear - salesPreviousYear ) / (double) salesPreviousYear; } virtual bool readyForPromotion(){ return ( ( yearsInSales >= MinYearsForPromotion ) ? true : false ) && ( productivityGainYtoY() > 10 ); } ~SalesPerson() {} }; ///////////////////////// class SalesManager ///////////////////////// class SalesManager : public SalesPerson, public ManagerType { int yearInSalesManagement; Employee* sellersSupervised; public: SalesManager( string name, string address, EducationLevel education ) : SalesPerson( name, address, education ) {} double productivityGainYtoY(){ return 0; // left for the reader to complete } }; //////////////////////////////// main /////////////////////////////// int main() { Department d1( "Design" ); d1.setProductionLastYear( 110001 ); // for last year d1.setProductionPreviousYear( 100000 ); // for two years back Manager manager1("Miz Importante", "UptownUSA", College, d1); //(D) manager1.setYearsInManagement( 8 ); manager1.setEmployeeSatisfaction( true ); if ( manager1.readyForPromotion() ) { cout << manager1.getName() << " " << "is ready for promotion. " << endl; } else { cout << manager1.getName() << " " << "is not ready for promotion." << endl; } Department d2( "Manufacturing" ); Department d3( "Development" ); SalesPerson salesman( "Joe Seller", "Downtown, USA", College); //(E) salesman. setYearsInSales( 5 ); salesman. setSalesPreviousYear( 100 ); salesman. setSalesLastYear( 100 ); if ( salesman.readyForPromotion() ) { cout << salesman.getName() << " " << "is ready for promotion." << endl << endl; } else { cout << salesman.getName() << " " << "is not ready for promotion." << endl; } ExecutiveManager bigshot( "Zushock Zinger", "MainstreetUSA", CollegePlus, 4 ); bigshot.addDepartment( d1 ); bigshot.addDepartment( d2 ); bigshot.addDepartment( d3 ); bigshot.print(); return 0; }

The test code shown in main above is again the same as in the implementation of the previous section. We construct a Manager and a SalesPerson objects in lines (D) and (E) and test whether or not they are ready for promotion. We also construct an ExecutiveManager object in line (F). The output produced for all these three objects remains the same as shown at the end of the previous section.




Programming With Objects[c] A Comparative Presentation of Object-Oriented Programming With C++ and Java
Programming with Objects: A Comparative Presentation of Object Oriented Programming with C++ and Java
ISBN: 0471268526
EAN: 2147483647
Year: 2005
Pages: 273
Authors: Avinash Kak

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net