1 有一个基类容器包含所有职员。 2 共同性操作,比如获得该发的薪水、结薪作为基类的虚函数。 3 特殊操作,比如某类职员的报到、具体薪水计算情况作为子类的一般函数。 比如: ------------------------------------ class Employee{ public: RMB getSalary() const = 0; }; // 钟点工 class HourWorker: public Employee{ public: checkIn( Time ); // 开工和手工登记 checkOut( Time ); Time getHourCount() const; // 总工作时间 }; // 月工(一般员工) class MonthWorker: public Employee{ public: overWork( Time ); // 加班登记 leave( Time ); // 休假登记 RMB getDefSalary(); // 返回底薪 RMB getRealSalary();// 返回实际应发工资 }; ------------------------------------ 如果只需要知道一般性的信息,可以搜索职员表即可得到该发薪水。 =========================================== 假如要获得详细信息,或者特定操作,那么有两种选择: 1 RTTI 遍历职员表,用 dynamic_cast 转换,如: ------------------------------------ for( 每个Employee ){ if( HourWorker* p = dynamic_cast< HourWorker* > pEmployee ) HourWorkerShow( p ); else if( MonthWorker* p = dynamic_cast< MonthWorker* > pEmployee ) MonthWorkerShow( p ); else ErrorLog( "具体的错误信息" ); } ------------------------------------ 很多人不喜欢 cast, 认为这不是纯正的OOP。 可以看到,这个方法有个缺点,当你添加了新的子类的时候,你必须修改原有代码。所以在所有转换失败的时候,我推荐写ErrorLog,以便避免遗漏。 另外,很容易看到,若是给 Employee 一个虚函数 show,就可以避免上面的RTTI: ------------------------------------ Employee{ public: ... show(); } /// for( 每个Employee ){ Employee.show(); } ------------------------------------ 这种方法更加oop一些; 不过一来这会使得类的设计复杂化( 我们应该实现完备的最小接口 );二来,很多情况下,你不一定有权利修改类体系。 =========================================== 2 为每个子类提供一个容器 这个做法比起前者更加规整一些 —— 而且很多情况下很适用。 假如我们需要添加某个职员的加班信息,我们往往已经知道他是“月工”。 比如: ------------------------------------ // id 号月工职员加班,添加加班记录。 addOverWork( IdType id, Time t ){ // moonWorkerSet 为月工集合 moonWorkerSet. find( id ). overWork( t ); } // id 号钟点工闪人,checkout workerCheckout( IdType id, Time t ){ // hourWorkerSet 为钟点工集合 hourWorkerSet. find( id ).checkOut( t ); } ------------------------------------ =========================================== 无论是用一个总集合RTTI,还是总集合+分集合 都有自己的好处。 若是连接数据库,你可以根据数据库本身的结构决定如何取舍。
最后我还是决定采用cast
期间,我发现使用visitor模式可以避免cast,不过它使设计复杂化了
visitor 实现:
EmployeeVisitor
{
public:
virtual void getHourWorkerInfo(HourWorker * pEmplyee);
virtual void getMonthWorkerInfo(MonthWorker * pEmplyee);
}
EmployeeInfoGetter :public EmployeeVisitor
{
void getHourWorkerInfo(MonthWorker * pEmplyee)
{
//此处调用派生类的特殊函数,取得月薪员工的具体信息
}
void getHourWorkerInfo(HourWorker * pEmplyee)
{
//此处调用派生类的特殊函数,取得钟点员工的具体信息
}
}
class Employee{
public:
virturl RMB getSalary() const = 0;
vitrual void Accept(EmployeeVisitor *pVisitor);
};
// 钟点工
class HourWorker: public Employee{
public:
checkIn( Time ); // 开工和手工登记
checkOut( Time );
Time getHourCount() const; // 总工作时间
void Accept(EmployeeVisitor *pVisitor)
{
pVisitor->getHourWorkerInfo(this);
}
};
// 月工(一般员工)
class MonthWorker: public Employee{
public:
overWork( Time ); // 加班登记
leave( Time ); // 休假登记
RMB getDefSalary(); // 返回底薪
RMB getRealSalary();// 返回实际应发工资
void Accept(EmployeeVisitor *pVisitor)
{
pVisitor->getMonthWorkerInfo(this);
}
};
//使用----------------------------------------
for( 每个Employee *pEmployee)
{
EmployeeVisitor* getterVisitor = new EmployeeInfoGetter;
pEmployee->Accept(getterVisitor);
}