Composição como alternativa à herança. Muitos programas escritos com herança poderiam ser escritos com composição, e vice-versa. Reescreva a classe FuncionarioBaseMaisComissao da hierarquia FuncionarioComissao–FuncionarioBaseMaisComissao para usar a composição em vez da herança. Depois que você fizer isso, avalie os méritos relativos dos dois métodos para projetar as classes FuncionarioComissao e FuncionarioBaseMaisComissao, bem como para programas orientados a objeto em geral. Qual método é mais natural? Por quê?
Passo 1
Vamos reescrever a classe BasePlusCommissionEmployee para que o relacionamento CommissionEmployee-BasePlusCommissionEmployee seja baseado em composição em vez de herança para demonstrar e comparar as duas técnicas de vinculação de duas classes.
Para que a classe BasePlusCommissionEmployee use a composição para incluir recursos da classe CommissionEmployee, teremos que adicionar uma instância de classe de membro de dados da classe CommissionEmployee e modificar/adicionar funções de/à classe.
class BasePlusCommissionEmployee {
public:
...
private:
double baseSalary;
CommissionEmployee commissionEmployee;
};
Em primeiro lugar, teremos que adicionar as funções getter/setter para operar com a instância da classe CommissionEmployee:
class BasePlusCommissionEmployee {
public:
...
const string &getFirstName() const;
void setFirstName(const string &firstName);
const string &getLastName() const;
void setLastName(const string &lastName);
const string &getSocialSecurityNumber() const;
void setSocialSecurityNumber(
const string &socialSecurityNumber);
double getGrossSales() const;
void setGrossSales(double grossSales);
double getCommissionRate() const;
void setCommissionRate(double commissionRate);
private:
...
};
Passo 2
Para implementá-los, basta chamar as funções getter/setter existentes da classe BasePlusCommissionEmployee da seguinte forma:
const string &BasePlusCommissionEmployee::getFirstName()
const {
return commissionEmployee.getFirstName();
}
void BasePlusCommissionEmployee::setFirstName(
const string &first) {
commissionEmployee.setFirstName(first);
}
const string &BasePlusCommissionEmployee::getLastName()
const {
return commissionEmployee.getLastName();
}
void BasePlusCommissionEmployee::setLastName(
const string &last) {
commissionEmployee.setLastName(last);
}
const string
&BasePlusCommissionEmployee::getSocialSecurityNumber()
const {
return commissionEmployee.getSocialSecurityNumber();
}
void BasePlusCommissionEmployee::setSocialSecurityNumber(
const string &ssn) {
commissionEmployee.setSocialSecurityNumber(ssn);
}
double BasePlusCommissionEmployee::getGrossSales() const {
return commissionEmployee.getGrossSales();
}
void BasePlusCommissionEmployee::setGrossSales(
double sales) {
commissionEmployee.setGrossSales(sales);
}
double BasePlusCommissionEmployee::getCommissionRate()
const {
return commissionEmployee.getCommissionRate();
}
void BasePlusCommissionEmployee::setCommissionRate(
double rate) {
commissionEmployee.setCommissionRate(rate);
}
Agora, vamos modificar o construtor da classe:
BasePlusCommissionEmployee::BasePlusCommissionEmployee(
const string &first, const string &last,
const string &ssn, double sales, double rate,
double salary)
: commissionEmployee(first, last,
ssn, sales, rate)
{
setBaseSalary(salary);
cout
print();
cout
}
Para que isso funcione, teremos que adicionar um construtor padrão à classe CommissionEmployee.
Simplesmente aprimore o construtor existente com os valores padrão para as strings:
class CommissionEmployee {
public:
CommissionEmployee(const string& = "",
const string & = "",
const string & = "",
double = 0.0,
double = 0.0 );
...
private:
...
}
Passo 3
Modifique as funções earn() e print() para chamar as funções correspondentes da instância da classe do membro de dados em vez da própria classe:
double BasePlusCommissionEmployee::earnings() const {
return getBaseSalary()
+ commissionEmployee.earnings();
}
void BasePlusCommissionEmployee::print() const {
cout
commissionEmployee.print();
cout
}
O arquivo principal para fins de demonstração:
#include
#include
#include "BasePlusCommissionEmployee.h"
using namespace std;
int main()
{
cout
{
CommissionEmployee employee1(
"Bob", "Lewis", "333-33-3333", 5000, .04);
}
cout
BasePlusCommissionEmployee
employee2("Lisa", "Jones", "555-55-5555",
2000, .06, 800 );
cout
BasePlusCommissionEmployee
employee3("Mark", "Sands", "888-88-8888",
8000, .15, 2000 );
cout
}
A composição é usada quando uma classe contém um objeto de outra classe. Por exemplo, um computador contém um processador, RAM, dispositivos de entrada/saída e outros.
Por outro lado, a herança é melhor usada quando uma classe é uma derivada baseada em outra classe "pai". Por exemplo, computadores, telefones, teclados são todos derivados do dispositivo de "classe base".
No nosso caso, seria melhor usar herança em vez de composição, já que a classe BasePlusCommissionEmployee é uma derivada da CommissionEmployee.
Em outras palavras, a classe possui todas as funcionalidades e propriedades da classe base CommissionEmployee, com a adição de seus próprios comportamentos e propriedades.
Uma das razões também pode ser que a construção de funções getter e setter adicionais para operar com a instância da classe CommissionEmployee é redundante.
Resposta
Ver desenvolvimento.