2011-08-26 11 views
6

Approccio: 1che è l'approccio migliore controllare tipo di oggetto

class Employee 
{ 
public: 
    virtual int calculateSalary() = 0; 

}; 

class PermanentEmployee : public Employee { 
    const int salaryPerMonth; 
public: 
    PermanentEmployee(int sal) : salaryPerMonth(sal){} 

    int calculateSalary() { 
     return salaryPerMonth; 
    } 
}; 

class ContractEmployee : public Employee { 
    const int wagesPerHr; 
    int totalHour; 
public: 
    ContractEmployee(int sal) : wagesPerHr(sal), totalHour(0){} 

    void setWorkingDuration(int time) { 
     totalHour = totalHour + time; 
    } 
    int calculateSalary() { 
     return wagesPerHr * totalHour; 
    } 
}; 

class Manager { 
    list<Employee *> ls; 
public: 
    void assignWorkingHour() { 
     list<Employee *>::iterator it; 

     for(it = ls.begin(); it != ls.end(); it++) { 
      Employee *emp = *it; 
      ContractEmployee* contractEmp = dynamic_cast<ContractEmployee* >(emp); 
      if(contractEmp) { 
       contractEmp->setWorkingDuration(5); 
      } 
     } 
    } 
}; 

In problema, ci sono 2 tipo di organigramma PermanentEmployee e ContractEmployee. Esiste una classe chiamata Manager che contiene un elenco di tutti i dipendenti che lavorano sotto di lui. Per ContractEmployee, deve richiamare la funzione setWorkingDuration, che viene richiamata nel metodo assignWorkingHour della classe Manager.

Il problema è: Ecco tipo di dipendenti è in corso determind dall'operatore dynamic_cast e manager deve sapere su tutti i tipi di classe di derive di Employee

approach2: aggiungere un altro membro nella classe Employee

 enum TypeOfEmployee {CONTRACT, PERMANENT}; 

e selezionare TypeOfEmployee per determinare il tipo di impiegato

Indicare qual è il migliore oppure Esiste un approccio alternativo?

+0

Come la domanda. Curioso del costo di dynamic_cast. – Lou

+0

http://www.nerdblog.com/2006/12/how-slow-is-dynamiccast.html potrebbe essere una lettura rilevante per te. –

+0

Questa è una domanda sui compiti? Sono solo curioso. –

risposta

7

L'approccio migliore consiste nel scrivere codice che non richiede la conoscenza del tipo di oggetto esatto. Mi sembra il modo più elegante per affrontare questo è quello di spostare il setWorkingDuration() alla classe dei dipendenti. Probabilmente in questo modo:

class Employee 
{ 
public: 
    // Calculates the salary for this employee. 
    // Returns the calculated salary. 
    virtual int calculateSalary() = 0; 
    // Sets the working duration. Does nothing if the employee is permanent. 
    // Returns true if Employee is on a contract, false if permanent. 
    virtual bool setWorkingDuration(int time) 
    { 
     return false; 
    } 
}; 

class PermanentEmployee : public Employee 
{ 
    const int salaryPerMonth; 
public: 
    PermanentEmployee(int sal) : salaryPerMonth(sal) {} 

    int calculateSalary() 
    { 
     return salaryPerMonth; 
    } 
}; 

class ContractEmployee : public Employee 
{ 
    const int wagesPerHr; 
    int totalHour; 
public: 
    ContractEmployee(int sal) : wagesPerHr(sal), totalHour(0) {} 

    int calculateSalary() 
    { 
     return wagesPerHr * totalHour; 
    } 

    bool setWorkingDuration(int time) 
    { 
     totalHour = totalHour + time; 
     return true; 
    } 
}; 

class Manager 
{ 
    list<Employee *> ls; 
public: 
    void assignWorkingHours() 
    { 
     list<Employee *>::iterator it; 
     for(it = ls.begin(); it != ls.end(); it++) 
     { 
      Employee* emp = *it; 
      emp->setWorkingDuration(5); 
     } 
    } 
}; 

In questo modo, la classe Manager non deve sapere se il Employee è in realtà un PermanentEmployee o un ContractEmployee. Questo è ciò che il polimorfismo ti dà. In generale, se devi usare dynamic_cast<>, puoi dare un'altra occhiata al design e vedere se puoi ometterlo.

+0

Mi picchia! Ben detto. –

+0

Per quanto riguarda l'utilizzo del campione fornito, non è necessario restituire un valore. Le classi derivate semplicemente aumentano le loro ore lavorate o non fanno nulla. In tal caso, setWorkingDuration() non deve essere puro virtuale e può essere implementato solo in classi che si occupano delle ore lavorate (lasciando la classe base con un'implementazione vuota). –

+0

@Jack Smith: corretto. Ecco perché ho detto "Probabilmente così." :-) Se le informazioni sul fatto che la funzione 'setWorkingDuration()' faccia qualcosa non è effettivamente interessante o necessaria, allora la funzione può semplicemente restituire 'void'. –

2

Bene, l'intero punto del polimorfismo del sottotipo è di consentire alle sottoclassi concrete di definire il proprio comportamento. Quello che stai facendo è la corrispondenza con il tipo di oggetto che hai, e quindi specificando il comportamento dipendente da quello. In sostanza, hai duplicato l'intero punto dei sottotipi, e quindi l'hai perso. :)

Il mio consiglio? Delegare questo comportamento all'oggetto stesso (non al suo gestore) come metodo virtual su Employee.

0

Ovviamente è possibile l'operatore dynamic_cast e enum TypeOfEmployee, ma l'ultimo non ha nulla in comune con il polimorfismo.

Si dovrebbe pensare perché Manager imposta l'orario di lavoro a PermanentEmployee. Va bene? Un altro approccio è quello di espandere l'interfaccia di classe base Dipendente con il metodo virtuale setWorkingDuration. Quindi in PermanentEmployee non farà nulla.

0

Gli altri hanno già fornito la risposta (vale la pena accettare) per i casi tipici.

Ecco alcune note per i casi meno tipici:

è possibile ridurre la dimensione del binario (numero di simboli esportati), tempi di esecuzione, conte di allocazioni dinamiche, l'utilizzo della memoria totale, e possibilità di errori di runtime se potete evitare l'uso di tipi dinamici in fase di runtime.

rimozione di virtuals e il polimorfismo di runtime può ridurre le dimensioni di oltre il 75%.

analogamente, è possibile utilizzare le variabili per identificare i tipi o le implementazioni in casi semplici (ad esempio il proprio approccio 2).

ho fatto un semplice test. ha utilizzato più tipi dinamici (360, in particolare), rtti, ecc. è stato generato un dylib della dimensione 588K.

sostituire in modo efficace i metodi virtuali e il polimorfismo di runtime con i puntatori di funzione lo riduce a 130 K. sono ancora 360 classi.

il consolidamento dell'implementazione in una singola classe con le variabili ha portato solo la dimensione del dylib a 10.000.

di nuovo, questo approccio interessa molto di più delle dimensioni binarie.

il mio punto è che questa è una buona sostituzione nelle giuste circostanze.

0

Approccio tre: tenere traccia degli appaltatori separatamente.

class Manager { 
    typedef list<Employee*> Employees; // Contains all employees, including contractors. 
    typedef list<ContractEmployee*> Contractors; // Contractors are in this list as well. 
    Employees employees; 
    Contractors contractors; 
public: 
    void assignWorkingHour() { 
     for(Contractors::iterator it = contractors.begin(); it != contractors.end(); ++it) { 
      (*it)->setWorkingDuration(5); 
     } 
    } 

    int calculateEmployeeCost() { 
     int totalSalary = 0; 
     for (Employees::const_iterator it = employees.begin(); it != employees.end(); ++it) { 
      totalSalary += (*it)->calculateSalary(); 
     } 
     return totalSalary; 
    } 
}; 
Problemi correlati